Синтаксис SQL запросов: основные команды и правила записи

Синтаксис SQL запросов задает порядок ключевых слов, имен, значений и условий. Освоив его, вы сможете написать основные запросы sql: выбрать и сгруппировать строки, соединить таблицы, изменить данные и создать структуру базы. Примеры используют таблицы employees и departments.

Для полного набора упражнений выберите PostgreSQL, MySQL или SQL Server. Выполняйте вариант кода для выбранного диалекта. В SQLite можно потренировать базовые SELECT, фильтрацию и группировку; отдельные диалектные примеры потребуют другой СУБД.

Из чего состоит SQL-запрос

SQL-команда складывается из ключевых слов, идентификаторов, литералов, операторов и разделителей. Ключевые слова описывают действие, идентификаторы называют объекты, а литералы задают конкретные значения.

SELECT employee_name, salary
FROM employees
WHERE salary >= 90000;

Здесь SELECT, FROM и WHERE – ключевые слова. Имена employees, employee_name и salary – идентификаторы, число 90000 – числовой литерал, а >= – оператор сравнения.

Точка с запятой завершает самостоятельную команду.

Регистр ключевых слов не меняет смысл: SELECT и select работают одинаково. Пробелы и переносы между элементами можно менять, но пробел внутри строкового значения сохраняется. Пишите ключевые слова заглавными и начинайте основные части запроса с новой строки.

Учебные таблицы

Сначала создайте таблицы в отдельной учебной базе и заполните их этим блоком.

CREATE TABLE departments (
    department_id INTEGER PRIMARY KEY,
    department_name VARCHAR(100) NOT NULL,
    salary_floor DECIMAL(12, 2) NOT NULL
);

CREATE TABLE employees (
    employee_id INTEGER PRIMARY KEY,
    employee_name VARCHAR(100) NOT NULL,
    department_id INTEGER,
    manager_id INTEGER,
    salary DECIMAL(12, 2) NOT NULL,
    FOREIGN KEY (department_id)
        REFERENCES departments (department_id)
);

INSERT INTO departments (department_id, department_name, salary_floor)
VALUES
    (10, 'Analytics', 100000),
    (20, 'Development', 90000),
    (30, 'Support', 70000);

INSERT INTO employees
    (employee_id, employee_name, department_id, manager_id, salary)
VALUES
    (1, 'Anna', 10, NULL, 120000),
    (2, 'Boris', 10, 1, 90000),
    (3, 'Vera', 20, 1, 80000),
    (4, 'Dan', NULL, NULL, 70000);

После вставки в таблице сотрудников будет 4 строки. У Dan нет отдела, а отдел Support пока не связан с сотрудником.

Теперь выполните SELECT с условием salary >= 90000 из начала раздела: он вернет Anna и Boris.

В упражнениях запускайте код из блоков pre, выбирая только вариант своей СУБД. Если упражнение меняет данные, выполните также завершающие команды восстановления. Они вернут исходный набор сотрудников и зарплат перед следующим упражнением.

Примеры в ячейках таблиц служат справкой и не входят в последовательность упражнений.

Основные команды SQL

Основные команды sql делятся по задаче: одни меняют структуру, другие работают со строками, третьи управляют правами или транзакциями.

ГруппаКомандыЧто делаютПример
DDLCREATE, ALTER, DROPСоздают, изменяют и удаляют объектыALTER TABLE employees ADD email VARCHAR(200);
DMLSELECT, INSERT, UPDATE, DELETEЧитают и изменяют строкиSELECT employee_name FROM employees;
DCLGRANT, REVOKEВыдают и отзывают праваGRANT SELECT ON employees TO analyst;
TCLCOMMIT, ROLLBACK, SAVEPOINTУправляют транзакциейROLLBACK;

SELECT иногда выделяют в отдельную группу DQL. Это учебная классификация: она не меняет синтаксис команды.

Пример GRANT предполагает, что пользователь analyst уже существует.

Синтаксис SELECT

SELECT перечисляет столбцы результата, FROM задает источник строк. Записывайте части в таком порядке: SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, ограничение строк.

-- PostgreSQL и MySQL
SELECT department_id,
       COUNT(*) AS employee_count,
       AVG(salary) AS average_salary
FROM employees
WHERE salary >= 80000
GROUP BY department_id
HAVING COUNT(*) >= 1
ORDER BY average_salary DESC, department_id
LIMIT 2;

-- SQL Server
SELECT TOP (2) department_id,
       COUNT(*) AS employee_count,
       AVG(salary) AS average_salary
FROM employees
WHERE salary >= 80000
GROUP BY department_id
HAVING COUNT(*) >= 1
ORDER BY average_salary DESC, department_id;

Оба варианта выберут отделы 10 и 20.

Логический порядок отличается от записи: сначала FROM и JOIN формируют строки, затем WHERE фильтрует их, GROUP BY собирает группы, HAVING фильтрует группы. После этого SELECT вычисляет результат, ORDER BY сортирует его, а ограничение оставляет часть строк.

Псевдоним из SELECT нельзя использовать в WHERE, потому что WHERE обрабатывается раньше вычисления результата. Повторите выражение или вынесите его в подзапрос.

SELECT employee_name, salary * 12 AS annual_salary
FROM employees
WHERE salary * 12 > 1000000
ORDER BY annual_salary DESC;

Псевдоним annual_salary доступен в ORDER BY. Запрос вернет Anna и Boris с годовыми доходами 1440000 и 1080000.

Ограничение числа строк

Выберите форму ограничения под свою СУБД. Всегда добавляйте ORDER BY, иначе состав первых строк не определен.

СУБДФорма
PostgreSQLLIMIT 2 или FETCH FIRST 2 ROWS ONLY
MySQLLIMIT 2
SQL ServerTOP (2) или OFFSET 0 ROWS FETCH NEXT 2 ROWS ONLY
OracleFETCH FIRST 2 ROWS ONLY
-- PostgreSQL и MySQL
SELECT employee_name, salary
FROM employees
ORDER BY salary DESC, employee_id
LIMIT 2;

-- SQL Server
SELECT TOP (2) employee_name, salary
FROM employees
ORDER BY salary DESC, employee_id;

-- PostgreSQL и Oracle
SELECT employee_name, salary
FROM employees
ORDER BY salary DESC, employee_id
FETCH FIRST 2 ROWS ONLY;

Каждый вариант вернет Anna и Boris. Уникальный employee_id разрешает одинаковые зарплаты при сортировке.

Условия в WHERE

WHERE оставляет строки, для которых условие дает TRUE. Сравнение используют с операторами =, <>, <, >, <= и >=.

ОператорНазначениеПример
LIKEШаблон строкиemployee_name LIKE 'A%'
INПроверка спискаdepartment_id IN (10, 20)
BETWEENДиапазон, включая обе границыsalary BETWEEN 80000 AND 90000
IS NULLОтсутствующее значениеdepartment_id IS NULL
SELECT employee_name
FROM employees
WHERE employee_name LIKE 'A%';

SELECT employee_name
FROM employees
WHERE department_id IN (10, 20)
ORDER BY employee_id;

SELECT employee_name
FROM employees
WHERE salary BETWEEN 80000 AND 90000
ORDER BY employee_id;

SELECT employee_name
FROM employees
WHERE department_id IS NULL;

Результаты будут такими: Anna; Anna, Boris и Vera; Boris и Vera; Dan. Символ процента заменяет последовательность символов, а подчеркивание – один символ.

NULL обозначает неизвестное или отсутствующее значение. Выражение department_id = NULL дает UNKNOWN, поэтому WHERE не оставляет строку. Используйте IS NULL или IS NOT NULL.

Помните и о NOT IN: NULL внутри набора может дать UNKNOWN.

Соединение таблиц: JOIN

JOIN сопоставляет строки двух таблиц через условие ON. В учебном примере связь проходит по department_id, а псевдонимы сокращают имена таблиц.

INNER JOIN

SELECT e.employee_name, d.department_name
FROM employees AS e
INNER JOIN departments AS d
    ON e.department_id = d.department_id
ORDER BY e.employee_id;

INNER JOIN оставит Anna, Boris и Vera. Dan исчезнет, потому что его отдел не задан.

LEFT JOIN

SELECT e.employee_name, d.department_name
FROM employees AS e
LEFT JOIN departments AS d
    ON e.department_id = d.department_id
ORDER BY e.employee_id;

LEFT JOIN сохранит всех 4 сотрудников, а в строке Dan название отдела будет NULL.

RIGHT JOIN

SELECT e.employee_name, d.department_name
FROM employees AS e
RIGHT JOIN departments AS d
    ON e.department_id = d.department_id
ORDER BY d.department_id, e.employee_id;

RIGHT JOIN сохранит все отделы, включая Support. Для Support имя сотрудника будет NULL. Тот же результат можно получить через LEFT JOIN, поменяв таблицы местами.

FULL JOIN

PostgreSQL и SQL Server поддерживают FULL OUTER JOIN. MySQL не поддерживает его напрямую, поэтому объедините два LEFT JOIN через UNION ALL.

-- PostgreSQL и SQL Server
SELECT e.employee_name, d.department_name
FROM employees AS e
FULL OUTER JOIN departments AS d
    ON e.department_id = d.department_id;

-- MySQL
SELECT e.employee_name, d.department_name
FROM employees AS e
LEFT JOIN departments AS d
    ON e.department_id = d.department_id
UNION ALL
SELECT e.employee_name, d.department_name
FROM departments AS d
LEFT JOIN employees AS e
    ON e.department_id = d.department_id
WHERE e.employee_id IS NULL;

Оба варианта возвращают 5 строк: три совпадения, Dan и Support.

Синтаксис INSERT, UPDATE, DELETE

INSERT добавляет строки, UPDATE меняет значения, DELETE удаляет строки. Перед UPDATE и DELETE выполните SELECT с тем же условием. Каждый пример заканчивается восстановлением исходных данных для следующего упражнения.

INSERT

INSERT INTO employees
    (employee_id, employee_name, department_id, manager_id, salary)
VALUES
    (5, 'Elena', 20, 1, 95000);

SELECT employee_id, employee_name, salary
FROM employees
WHERE employee_id = 5;

-- Восстановление учебных данных
DELETE FROM employees
WHERE employee_id = 5;

SELECT покажет Elena с зарплатой 95000. Последний DELETE удалит учебную запись и вернет таблицу к 4 сотрудникам.

Список столбцов в INSERT задает порядок значений.

UPDATE: синтаксис изменения строк

Шаблон UPDATE ... SET ... WHERE ... раскрывает update sql синтаксис изменения выбранных строк. SET перечисляет новые значения через запятую.

SELECT employee_id, salary, department_id
FROM employees
WHERE employee_id = 2;

UPDATE employees
SET salary = 95000,
    department_id = 20
WHERE employee_id = 2;

SELECT employee_id, salary, department_id
FROM employees
WHERE employee_id = 2;

-- Восстановление учебных данных
UPDATE employees
SET salary = 90000,
    department_id = 10
WHERE employee_id = 2;

Второй SELECT покажет зарплату Boris 95000 и отдел 20. Последняя команда вернет зарплату 90000 и отдел 10.

UPDATE без WHERE изменит каждую строку, поэтому для точечной правки условие обязательно.

UPDATE по данным другой таблицы

Синтаксис соединения при UPDATE отличается по СУБД. Выполните предварительный SELECT, затем один подходящий вариант UPDATE и блок восстановления.

SELECT e.employee_name, e.salary, d.salary_floor
FROM employees AS e
INNER JOIN departments AS d
    ON e.department_id = d.department_id
WHERE e.salary < d.salary_floor
ORDER BY e.employee_id;

SELECT найдет Boris и Vera с зарплатами ниже порога отдела.

-- PostgreSQL
UPDATE employees AS e
SET salary = d.salary_floor
FROM departments AS d
WHERE e.department_id = d.department_id
  AND e.salary < d.salary_floor;

-- SQL Server
UPDATE e
SET salary = d.salary_floor
FROM employees AS e
INNER JOIN departments AS d
    ON e.department_id = d.department_id
WHERE e.salary < d.salary_floor;

-- MySQL
UPDATE employees AS e
INNER JOIN departments AS d
    ON e.department_id = d.department_id
SET e.salary = d.salary_floor
WHERE e.salary < d.salary_floor;

На исходных данных запрос поднимет зарплаты Boris до 100000 и Vera до 90000. Связь по первичному ключу отдела дает каждому сотруднику единственное новое значение.

SELECT employee_name, salary
FROM employees
WHERE employee_id IN (2, 3)
ORDER BY employee_id;

-- Восстановление учебных данных
UPDATE employees
SET salary = 90000
WHERE employee_id = 2;

UPDATE employees
SET salary = 80000
WHERE employee_id = 3;

SELECT покажет повышенные зарплаты. Две команды UPDATE вернут Boris 90000, а Vera – 80000.

DELETE

SELECT employee_id, employee_name
FROM employees
WHERE employee_id = 4;

DELETE FROM employees
WHERE employee_id = 4;

SELECT employee_id, employee_name
FROM employees
WHERE employee_id = 4;

-- Восстановление учебных данных
INSERT INTO employees
    (employee_id, employee_name, department_id, manager_id, salary)
VALUES
    (4, 'Dan', NULL, NULL, 70000);

Первый SELECT найдет Dan, а второй не вернет строк. INSERT восстановит Dan для следующих упражнений.

DELETE без WHERE удалит все записи таблицы, поэтому проверяйте условие заранее.

Откат учебного изменения

Транзакция позволяет отменить изменение через ROLLBACK. Выполняйте весь пример в одном соединении; для MySQL используйте таблицы InnoDB.

-- PostgreSQL и MySQL
START TRANSACTION;

UPDATE employees
SET salary = 1
WHERE employee_id = 2;

SELECT employee_id, salary
FROM employees
WHERE employee_id = 2;

ROLLBACK;

SELECT employee_id, salary
FROM employees
WHERE employee_id = 2;

Первый SELECT покажет зарплату 1, второй – исходные 90000. Для SQL Server выполните отдельный вариант.

-- SQL Server
BEGIN TRANSACTION;

UPDATE employees
SET salary = 1
WHERE employee_id = 2;

SELECT employee_id, salary
FROM employees
WHERE employee_id = 2;

ROLLBACK;

SELECT employee_id, salary
FROM employees
WHERE employee_id = 2;

ROLLBACK отменит изменение Boris и сохранит исходные данные для CTE.

WITH AS: общие табличные выражения

Конструкция WITH имя AS (...), которую ищут как with as sql, задает именованный результат для одного оператора. CTE помогает разделить сложный запрос на понятные этапы.

Один и несколько CTE

Несколько выражений разделяйте запятыми после одного WITH. Второй CTE может использовать результат первого.

WITH department_stats AS (
    SELECT department_id, AVG(salary) AS average_salary
    FROM employees
    WHERE department_id IS NOT NULL
    GROUP BY department_id
), selected_departments AS (
    SELECT department_id
    FROM department_stats
    WHERE average_salary >= 100000
)
SELECT e.employee_name
FROM employees AS e
JOIN selected_departments AS s
    ON e.department_id = s.department_id
ORDER BY e.employee_id;

Запрос вернет Anna и Boris, потому что средняя зарплата отдела 10 достигает 100000.

В SQL Server завершайте предыдущую команду точкой с запятой перед WITH.

WITH RECURSIVE для иерархии

Рекурсивный CTE начинает с корневых строк, затем повторяет шаг для подчиненных. MySQL поддерживает WITH RECURSIVE начиная с 8.0; SQL Server использует WITH без слова RECURSIVE.

-- PostgreSQL и MySQL
WITH RECURSIVE employee_tree AS (
    SELECT employee_id, employee_name, manager_id, 0 AS depth
    FROM employees
    WHERE manager_id IS NULL
    UNION ALL
    SELECT e.employee_id, e.employee_name, e.manager_id, t.depth + 1
    FROM employees AS e
    JOIN employee_tree AS t
        ON e.manager_id = t.employee_id
)
SELECT employee_name, depth
FROM employee_tree
ORDER BY depth, employee_id;

Anna и Dan получат глубину 0, Boris и Vera – глубину 1. Для SQL Server используйте следующий вариант.

-- SQL Server
;WITH employee_tree AS (
    SELECT employee_id, employee_name, manager_id, 0 AS depth
    FROM employees
    WHERE manager_id IS NULL
    UNION ALL
    SELECT e.employee_id, e.employee_name, e.manager_id, t.depth + 1
    FROM employees AS e
    JOIN employee_tree AS t
        ON e.manager_id = t.employee_id
)
SELECT employee_name, depth
FROM employee_tree
ORDER BY depth, employee_id;

Результат содержит тех же 4 сотрудников с теми же уровнями.

Следите за циклами в иерархии: повторное посещение строк может привести к ошибке ограничения рекурсии или долгому выполнению.

Создание и изменение таблиц

CREATE TABLE задает столбцы, типы и ограничения. ALTER TABLE меняет структуру, а DROP TABLE удаляет таблицу вместе с данными.

CREATE TABLE employee_notes (
    note_id INTEGER PRIMARY KEY,
    employee_id INTEGER NOT NULL,
    note_text VARCHAR(200),
    FOREIGN KEY (employee_id)
        REFERENCES employees (employee_id)
);

ALTER TABLE employee_notes
ADD author_name VARCHAR(100);

DROP TABLE employee_notes;

INTEGER подходит для идентификаторов, VARCHAR – для строк, а DECIMAL – для точных денежных значений. PRIMARY KEY запрещает дубли и NULL, NOT NULL требует значение. FOREIGN KEY проверяет ссылку на существующую строку, если значение ссылки не NULL.

Форма ALTER TABLE ... ADD подходит для PostgreSQL, MySQL и SQL Server.

После CREATE появится таблица заметок, после ALTER – столбец автора. Завершающий DROP удалит учебную таблицу; сотрудники и отделы останутся.

Идентификаторы и правила именования

Идентификаторы называют таблицы, столбцы, схемы и псевдонимы. Используйте осмысленные имена в стиле snake_case: employee_name, department_id, salary_floor. Не сокращайте имя до emp, если оно скрывает смысл.

СУБДМаксимальная длина имени таблицы или столбцаЭкранированиеРегистр
PostgreSQL63 байта при стандартной сборке"employee_name"Без кавычек имя приводится к нижнему регистру
MySQL64 символа`employee_name`Правила таблиц зависят от платформы и настройки lower_case_table_names
SQL Server128 символов[employee_name]Зависит от параметров сортировки
Oracle128 байт при COMPATIBLE от 12.2"employee_name"Без кавычек имя приводится к верхнему регистру

Стандарт SQL использует двойные кавычки для идентификаторов. Одинарные кавычки оставьте для строк.

В MySQL двойные кавычки работают как кавычки идентификатора при включенном ANSI_QUOTES.

-- PostgreSQL
SELECT employee_name AS "order"
FROM employees;

-- MySQL
SELECT employee_name AS `order`
FROM employees;

-- SQL Server
SELECT employee_name AS [order]
FROM employees;

Каждый запрос выведет имена сотрудников в столбце с псевдонимом order. Лучше выбрать имя без конфликта с ключевым словом.

Полные списки зарезервированных слов приведены в официальной документации PostgreSQL и MySQL. Правила лексики описаны в документации PostgreSQL и MySQL.

Литералы и NULL

Литерал записывает значение прямо в SQL. Числа пишут без кавычек, строки – в одинарных кавычках. Внутреннюю одинарную кавычку удваивают.

SELECT 'O''Brien' AS employee_name,
       95000.50 AS salary,
       NULL AS department_id;

Результат содержит O'Brien, число 95000.50 и NULL. В PostgreSQL, MySQL и SQL Server NULL отличается от пустой строки и нуля.

Стандартная запись даты использует DATE, а даты и времени – TIMESTAMP. Следующий пример выполняется в PostgreSQL и MySQL.

-- PostgreSQL и MySQL
SELECT DATE '2026-01-28' AS hire_date,
       TIMESTAMP '2026-01-28 09:30:00' AS hired_at;

Запрос вернет дату 28 января и время 09:30:00. SQL Server не поддерживает такую запись литералов; используйте CAST.

-- SQL Server
SELECT CAST('20260128' AS date) AS hire_date,
       CAST('2026-01-28T09:30:00' AS datetime2) AS hired_at;

Запрос вернет ту же дату и время в типах date и datetime2.

Тип timestamp в SQL Server означает версию строки и не хранит дату.

Для логических значений и текущей даты также выбирайте вариант диалекта.

-- PostgreSQL и MySQL
SELECT TRUE AS is_active,
       FALSE AS is_archived,
       CURRENT_DATE AS report_date;

PostgreSQL вернет логические значения, MySQL – числа 1 и 0. Последний столбец содержит текущую дату сеанса.

-- SQL Server
SELECT CAST(1 AS bit) AS is_active,
       CAST(0 AS bit) AS is_archived,
       CAST(CURRENT_TIMESTAMP AS date) AS report_date;

SQL Server вернет значения 1 и 0 типа bit. Столбец report_date содержит текущую дату сервера без времени.

Для данных пользователя применяйте параметры драйвера, а не собирайте SQL строковой конкатенацией.

Операторы SQL запросов и их приоритет

Операторы sql запросов вычисляют числа, сравнивают значения и связывают условия. Скобки делают намерение явным, даже когда вы помните правила конкретной СУБД.

Порядок в упрощенной схеме PostgreSQLОператоры и конструкции
1Выражения в скобках
2Умножение, деление, остаток: *, /, %
3Сложение и вычитание: +, -
4BETWEEN, IN, LIKE
5Сравнения: =, <>, <, >, <=, >=
6IS NULL и другие проверки IS
7NOT
8AND
9OR

Таблица охватывает конструкции из статьи и не перечисляет все операторы. Порядок BETWEEN, IN, LIKE и некоторых сравнений различается по диалектам.

Полные таблицы приоритета приведены в документации PostgreSQL, MySQL и SQL Server.

Арифметические скобки

SELECT 12 * 2 + 24 AS without_parentheses,
       12 * (2 + 24) AS with_parentheses;

Первое выражение вернет 48, второе – 312. Умножение выполняется раньше сложения, если скобки не меняют порядок.

AND и OR

В условии a = 1 OR b = 2 AND c = 3 AND имеет более высокий приоритет, чем OR. Поэтому выражение читается как (a = 1) OR ((b = 2) AND (c = 3)).

Приоритет определяет группировку условия, но не гарантирует порядок вычисления его частей.

SELECT employee_name
FROM employees
WHERE department_id = 10
   OR department_id = 20 AND salary >= 100000
ORDER BY employee_id;

SELECT employee_name
FROM employees
WHERE (department_id = 10 OR department_id = 20)
  AND salary >= 100000
ORDER BY employee_id;

Первый запрос вернет Anna и Boris, второй – только Anna. Скобки распространяют порог зарплаты на оба отдела.

Комментарии в SQL

Комментарии в sql поясняют код и временно отключают его часть. Однострочный комментарий начинается с --; в MySQL после двух дефисов нужен пробел или управляющий символ.

-- Сотрудники с зарплатой от 90000
SELECT employee_name, salary
FROM employees
WHERE salary >= 90000
ORDER BY employee_id;

/* Блочный комментарий.
   Он занимает несколько строк. */
SELECT COUNT(*) AS employee_count
FROM employees;

Первый запрос покажет Anna и Boris, второй – число 4. Комментарии не меняют результаты этих запросов.

MySQL также поддерживает комментарий с решеткой.

# Только MySQL
SELECT COUNT(*) FROM departments;

Запрос вернет число 3.

Форма /*+ ... */ в Oracle может передавать подсказку оптимизатору. Специальный комментарий /*! ... */ в MySQL может выполнить свое содержимое, поэтому не отключайте им код без понимания диалекта.

Ошибки в синтаксисе SQL-запросов

Ошибка SQL бывает синтаксической или логической. Запрос может выполниться успешно и все же изменить не те строки, поэтому сверяйте условие SELECT перед UPDATE и DELETE. Опасные и ошибочные команды в примерах закомментированы.

UPDATE и DELETE без WHERE

-- Опасно: изменит все строки
-- UPDATE employees SET salary = 95000;

SELECT employee_id, employee_name, salary
FROM employees
WHERE employee_id = 2;

UPDATE employees
SET salary = 95000
WHERE employee_id = 2;

SELECT employee_id, employee_name, salary
FROM employees
WHERE employee_id = 2;

-- Восстановление учебных данных
UPDATE employees
SET salary = 90000
WHERE employee_id = 2;

-- Опасно: удалит все строки
-- DELETE FROM employees;

SELECT employee_id, employee_name
FROM employees
WHERE employee_id = 4;

DELETE FROM employees
WHERE employee_id = 4;

SELECT employee_id, employee_name
FROM employees
WHERE employee_id = 4;

-- Восстановление учебных данных
INSERT INTO employees
    (employee_id, employee_name, department_id, manager_id, salary)
VALUES
    (4, 'Dan', NULL, NULL, 70000);

UPDATE изменит только Boris, а DELETE удалит только Dan. Контрольные SELECT покажут новую зарплату 95000 и отсутствие Dan.

Команды восстановления вернут исходную зарплату и удаленную строку.

AND и OR без скобок

-- Порог применяется только к отделу 20
SELECT employee_name
FROM employees
WHERE department_id = 10
   OR department_id = 20 AND salary >= 100000
ORDER BY employee_id;

-- Порог применяется к обоим отделам
SELECT employee_name
FROM employees
WHERE (department_id = 10 OR department_id = 20)
  AND salary >= 100000
ORDER BY employee_id;

Первый вариант возвращает Anna и Boris, второй – только Anna. Скобки показывают смысл условия читателю и защищают запрос от ошибки при дальнейшей правке.

Сравнение NULL через =

-- Не найдет сотрудника без отдела
SELECT employee_name
FROM employees
WHERE department_id = NULL;

SELECT employee_name
FROM employees
WHERE department_id IS NULL;

Исправленная команда находит Dan. Для непустого значения используйте IS NOT NULL.

Двойные кавычки вокруг строки

-- Непереносимый вариант
-- SELECT employee_id FROM employees WHERE employee_name = "Anna";

SELECT employee_id
FROM employees
WHERE employee_name = 'Anna';

Исправленный запрос вернет идентификатор 1.

Строковые значения заключайте в одинарные кавычки. Двойные кавычки предназначены для идентификаторов, а поведение в MySQL зависит от режима.

Зарезервированное слово как имя

-- Может вызвать ошибку
-- SELECT salary AS order FROM employees;

SELECT salary AS salary_amount
FROM employees
ORDER BY employee_id;

Понятный псевдоним salary_amount не конфликтует с ключевым словом ORDER. Запрос выведет 4 зарплаты под этим именем столбца.

Агрегат в WHERE

-- WHERE выполняется до группировки
-- SELECT department_id, COUNT(*)
-- FROM employees
-- WHERE COUNT(*) > 1
-- GROUP BY department_id;

SELECT department_id, COUNT(*) AS employee_count
FROM employees
GROUP BY department_id
HAVING COUNT(*) > 1;

WHERE фильтрует отдельные строки, а HAVING фильтрует готовые группы. Запрос вернет отдел 10 с двумя сотрудниками.

Столбец вне GROUP BY

-- Имя сотрудника неоднозначно для группы
-- SELECT department_id, employee_name, COUNT(*)
-- FROM employees
-- GROUP BY department_id;

SELECT department_id, COUNT(*) AS employee_count
FROM employees
GROUP BY department_id
ORDER BY department_id;

Исправленный запрос вернет 3 группы, включая группу с NULL вместо отдела. Исправление убирает неоднозначный столбец. Если добавить employee_name в GROUP BY, запрос начнет группировать по сотрудникам и изменит смысл результата.

Совет от нашей редакции: сначала опишите словами ожидаемые строки, затем проверьте их SELECT и только после этого запускайте UPDATE или DELETE.

Комментарии

Оставьте свой комментарий
Пока никто не оставил комментариев