Раздел 33 · PostgreSQL с нуля

INSERT, UPDATE, DELETE и RETURNING

junior~25 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Четыре урока подряд мы только читали данные магазина: выбирали, соединяли, считали итоги. Но магазин живой: приходят новые книги, меняются цены, заказы отменяют. В этом уроке учимся менять данные тремя командами и узнаём, как сразу увидеть результат изменения, не делая второй запрос. Примеры работают на учебной базе магазина.

Тетрадь учёта

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

В SQL у этих действий свои команды: INSERT дописывает, UPDATE исправляет, DELETE вычёркивает. Вместе с SELECT их называют DML. А «посмотреть ещё раз» в PostgreSQL умеет сама команда: для этого есть RETURNING.

INSERT: дописать строку

В магазин пришёл новый автор и две его книги:

INSERT INTO authors (id, name, country)
VALUES (7, 'Пелевин', 'Россия');

INSERT INTO books (id, title, author_id, genre, price, pages, year) VALUES
  (10, 'Generation П', 7, 'роман', 590.00, 320, 1999),
  (11, 'Чапаев и Пустота', 7, 'роман', 640.00, 400, 1996);
INSERT 0 1
INSERT 0 2

Последнее число в ответе psql это количество вставленных строк. Ноль перед ним остался от старых версий PostgreSQL, на него не смотри.

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

Колонку можно пропустить. Тогда в неё попадёт значение по умолчанию, а если его нет, то NULL. Книга «Омон Ра» вставлена без цены:

INSERT INTO books (id, title, author_id, genre, pages, year)
VALUES (12, 'Омон Ра', 7, 'повесть', 160, 1992);

SELECT id, title, price FROM books WHERE author_id = 7 ORDER BY id;
 id |      title       | price
----+------------------+--------
 10 | Generation П     | 590.00
 11 | Чапаев и Пустота | 640.00
 12 | Омон Ра          |
(3 rows)

Если строка с таким ключом уже есть, ограничение не пустит вставку:

ERROR:  duplicate key value violates unique constraint "books_pkey"
DETAIL:  Key (id)=(1) already exists.

Что делать, когда дубликат ожидаем и надо «вставить или обновить», разберём в следующем уроке.

INSERT … SELECT

Значения для вставки не обязательно писать руками. Их может дать запрос. Магазин устраивает распродажу учебников со скидкой 20%, и цены для неё берутся прямо из books:

CREATE TABLE sale_books (
  book_id integer PRIMARY KEY REFERENCES books (id),
  price   numeric(8, 2) NOT NULL
);

INSERT INTO sale_books (book_id, price)
SELECT id, round(price * 0.8, 2)
FROM books
WHERE genre = 'учебник';

SELECT * FROM sale_books ORDER BY book_id;
 book_id |  price
---------+---------
       7 | 1032.00
       8 | 1992.00
(2 rows)

Вместо VALUES стоит обычный SELECT, и сколько строк он вернёт, столько и вставится.

RETURNING: посмотреть, что получилось

Для отзывов заведём таблицу с автонумерацией из урока про identity:

CREATE TABLE reviews (
  id      integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  book_id integer NOT NULL REFERENCES books (id),
  rating  integer NOT NULL,
  body    text
);

Покупатель оставил отзыв, и приложению нужен его номер, чтобы показать ссылку «ваш отзыв». Номер выдаёт база. Можно сделать второй запрос и поискать последний отзыв, но если в ту же секунду отзыв оставил кто-то ещё, ты получишь чужой номер. RETURNING отдаёт результат прямо из той же команды:

INSERT INTO reviews (book_id, rating, body)
VALUES (3, 5, 'Перечитываю каждый год')
RETURNING id;
 id
----
  1
(1 row)

Для нескольких строк RETURNING вернёт несколько строк, по одной на каждую вставленную:

INSERT INTO reviews (book_id, rating, body) VALUES
  (1, 5, 'Классика'),
  (8, 4, 'Толстая, но полезная')
RETURNING id, book_id;
 id | book_id
----+---------
  2 |       1
  3 |       8
(2 rows)

урок из курса

Урок из курса «PostgreSQL с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.

Оплата пока не подключена: премиум и курсы открываются промокодом.