Перейти к содержимому

Изменение данных: INSERT, UPDATE и DELETE

Курс MS SQL с нуля · Урок 7 из 12 · Часть 3. Изменение и проектирование

До этого урока курс только читал базу. Теперь мы пишем: три оператора изменения, предложение OUTPUT и первое правило выживания — любой массовый UPDATE или DELETE сначала прогоняется как SELECT с тем же WHERE.

Внимание: урок меняет данные TechStore. После занятия база обязательно восстанавливается повторным прогоном скриптов урока 2 — иначе съедут числа всех следующих уроков.

Содержание

1. Что вы освоите

К концу урока вы:

  • добавляете строки с явным списком столбцов и читаете IDENTITY корректно;
  • меняете множества строк с прогнозом и постпроверкой числа затронутых;
  • читаете старые и новые значения через OUTPUT;
  • удаляете только после «корзинного» SELECT;
  • отличаете TRUNCATE от DELETE и знаете цену каждого.
↑ К оглавлению

2. INSERT и номер новой строки

Чистый код

INSERT dbo.Customers (FullName, Email, City, RegisteredAt)
VALUES (N'Giovanni Papadopoulos', Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.', N'Рим', SYSDATETIME());

SELECT SCOPE_IDENTITY() AS new_customer_id;
SQL
  • Построчный разбор

    Ниже приведён тот же фрагмент с пояснением после каждой смысловой строки. Строки, которые начинаются с двух дефисов и стрелки вправо, содержат комментарий преподавателя. Эти строки не являются обязательной частью программы.

    INSERT dbo.Customers (FullName, Email, City, RegisteredAt)
    -- → перечень столбцов обязателен: значения лягут точно по нему
    VALUES (N'Giovanni Papadopoulos', Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.', N'Рим', SYSDATETIME());
    -- → сегмент не указан: сработает его дефолт Retail
    
    SELECT SCOPE_IDENTITY() AS new_customer_id;
    -- → номер нового ключа, присвоенный именно этому оператору, не соседнему
    SQL
  • список столбцов обязателен всегда — даже когда совпадает с порядком таблицы: добавление нового столбца тогда ничего не ломает;
  • IDENTITY-столбец в списке не упоминается: значение назначает сервер;
  • для массовых вставок INSERT берёт строки из SELECT — базовый инструмент копий и снимков.
@@IDENTITY — чужая функция: глобальный @@IDENTITY возвращает последний идентификатор сеанса вообще, и при триггере он может принадлежать другой таблице. Правильная функция — SCOPE_IDENTITY: свой оператор, свой номер.
↑ К оглавлению

3. UPDATE: безопасная анатомия

UPDATE меняет множество строк за один оператор; предикат WHERE — его тормоз. Анатомия безопасного изменения — тройка операторов:

  1. SELECT тем же WHERE: число строк глазами сверяется с ожиданием;
  2. UPDATE с тем же WHERE и OUTPUT для осмотра старых и новых значений;
  3. постпроверка итоговым SELECT: три числа обязаны совпасть.

Чистый код

SELECT COUNT(*) AS moscow_cnt FROM dbo.Customers AS c WHERE c.City = N'Москва';
GO

UPDATE c SET City = N'Москва (МО)'
OUTPUT deleted.CustomerID, deleted.City AS old_city, inserted.City AS new_city
FROM dbo.Customers AS c
WHERE c.City = N'Москва';
GO

SELECT COUNT(*) AS renamed_cnt FROM dbo.Customers AS c WHERE c.City = N'Москва (МО)';
GO
SQL
  • Построчный разбор

    Ниже приведён тот же фрагмент с пояснением после каждой смысловой строки. Строки, которые начинаются с двух дефисов и стрелки вправо, содержат комментарий преподавателя. Эти строки не являются обязательной частью программы.

    SELECT COUNT(*) AS moscow_cnt FROM dbo.Customers AS c WHERE c.City = N'Москва';
    -- → прогноз: сколько строк сейчас под предикатом; число запоминается
    GO
    
    UPDATE c SET City = N'Москва (МО)'
    -- → изменение под тем же предикатом
    OUTPUT deleted.CustomerID, deleted.City AS old_city, inserted.City AS new_city
    -- → осмотр: старое и новое значение каждой затронутой строки
    FROM dbo.Customers AS c
    WHERE c.City = N'Москва';
    GO
    
    SELECT COUNT(*) AS renamed_cnt FROM dbo.Customers AS c WHERE c.City = N'Москва (МО)';
    -- → постпроверка: счётчик обязан равняться прогнозу
    GO
    SQL

OUTPUT: чтение изменённого в том же операторе

Виртуальные таблицы inserted и deleted доступны прямо в UPDATE, DELETE и INSERT: deleted хранит старые значения, inserted — новые. Это опора аудита в продвинутом курсе; здесь важно увидеть вывод руками и понять, что «повторным запросом» старое значение не восстановить по памяти уже нельзя.

↑ К оглавлению

4. DELETE, корзина и TRUNCATE

DELETE удаляет строки по фильтру и журналирует каждую построчно; TRUNCATE опустошает таблицу целиком, быстро и без деталей, но запрещён на таблицах с внешними ключами. В курсе используется DELETE с предварительным SELECT «на корзину»:

Чистый код

SELECT COUNT(*) AS target_cnt FROM dbo.Customers AS c
WHERE c.Email = Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.';

DELETE c FROM dbo.Customers AS c
WHERE c.Email = Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.';

SELECT COUNT(*) AS target_after FROM dbo.Customers AS c
WHERE c.Email = Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.';
SQL
  • Построчный разбор

    Ниже приведён тот же фрагмент с пояснением после каждой смысловой строки. Строки, которые начинаются с двух дефисов и стрелки вправо, содержат комментарий преподавателя. Эти строки не являются обязательной частью программы.

    SELECT COUNT(*) AS target_cnt FROM dbo.Customers AS c
    WHERE c.Email = Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.';
    -- → прицел подтверждён: ожидается ровно одна строка
    
    DELETE c FROM dbo.Customers AS c
    WHERE c.Email = Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.';
    -- → удаление тем же предикатом
    
    SELECT COUNT(*) AS target_after FROM dbo.Customers AS c
    WHERE c.Email = Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.';
    -- → постпроверка: ожидается ноль
    SQL
Msg 547 — не наказание: если строка тянет за собой заказы, внешний ключ запретит удаление с Msg 547: связь защищает целостность. Правильный путь — осознанная цепочка удалений, а не снятие ограничения.
↑ К оглавлению

5. Задачи и эталонные решения

Шесть задач на контексте TechStore. Регламент: урок меняет базу; после занятия она восстанавливается скриптами урока 2.

  1. новый клиент: вставка и номер через SCOPE_IDENTITY, проверка по номеру;
  2. наценка десять процентов на «Зарядные устройства»: номер категории находится по имени LIKE, SELECT-прогноз до UPDATE, OUTPUT первых строк;
  3. обнуление остатков и снятие активности у «Чехлов» одним UPDATE двух столбцов;
  4. удаление тестового клиента по корзинному паттерну, EXISTS-постпроверка;
  5. снимок цен активных товаров: создать dbo.PriceSnapshot и наполнить INSERT SELECT, сверить число строк с числом активных;
  6. переименование города Москвы по тройке «прогноз — изменение — постпроверка», шестьдесят процентов клиентов — серьёзная проверка тормозов.

Эталон безопасного UPDATE

тройка операторов последовательно

Чистый код

SELECT COUNT(*) AS rows_before
FROM dbo.Products AS p WHERE p.CategoryID = 10;

UPDATE p SET Price = Price * 1.10
OUTPUT TOP (5) deleted.ProductID, deleted.Price AS old_price,
               inserted.Price AS new_price
FROM dbo.Products AS p
WHERE p.CategoryID = 10;

SELECT COUNT(*) AS rows_after
FROM dbo.Products AS p WHERE p.CategoryID = 10;
задача 7.2: изменение с прогнозом и осмотром
  • Построчный разбор

    Ниже приведён тот же фрагмент с пояснением после каждой смысловой строки. Строки, которые начинаются с двух дефисов и стрелки вправо, содержат комментарий преподавателя. Эти строки не являются обязательной частью программы.

    SELECT COUNT(*) AS rows_before
    FROM dbo.Products AS p WHERE p.CategoryID = 10;
    -- → категория 10, «Зарядные устройства»: прогноз числа строк
    
    UPDATE p SET Price = Price * 1.10
    -- → наценка десяти процентов
    OUTPUT TOP (5) deleted.ProductID, deleted.Price AS old_price,
                   inserted.Price AS new_price
    -- → осмотр первых пяти пар «было-стало»
    FROM dbo.Products AS p
    WHERE p.CategoryID = 10;
    
    SELECT COUNT(*) AS rows_after
    FROM dbo.Products AS p WHERE p.CategoryID = 10;
    -- → постпроверка: число совпадает с прогнозом
    задача 7.2: изменение с прогнозом и осмотром
↑ К оглавлению

6. Типичные ошибки урока

  1. UPDATE без WHERE — изменена вся таблица; профилактика: курсор на WHERE до Ctrl+Enter и глаза на предикат.
  2. INSERT без списка столбцов — работает до первого добавленного столбца; список обязателен всегда.
  3. Номер прочитан @@IDENTITY — при триггере вернётся чужой номер; канон — SCOPE_IDENTITY.
  4. UPDATE без алиаса в соединении — эталоны курса всегда с алиасом и JOIN в FROM, иначе теряется контроль, какая таблица меняется.
  5. Msg 547 при DELETE — защита внешнего ключа; лечится цепочкой осознанных удалений, не снятием связи.
  6. TRUNCATE доверия ради — запрещён на связанных таблицах; выбор между ним и DELETE — осознанный, не «быстрее же».
↑ К оглавлению

7. Чек-лист практики и источники

Чек-лист урока 7

отметки делаются по факту

Источники для углубления:

  1. INSERT: синтаксис и варианты;
  2. OUTPUT: inserted и deleted;
  3. DELETE и ограничение внешнего ключа (Msg 547).
Итог: данные меняются контролируемо и обратимо. Урок 8 встаёт на сторону проектировщика: типы данных, NULL, ключи и ограничения таблицы.

Проверено: 23.10.2026

↑ К оглавлению
Конспект:
Четверг, 22 октября 2026