Запит даних
Процес отримання або команда для отримання даних з бази даних називається запит. У мові SQL для визначення запитів використовується команда SELECT
.
SELECT
вибирає рядки з нуля або більше таблиць. Загальна обробка SELECT
відбувається наступним чином:
Всі запити у списку
WITH
обчислюються. Це фактично слугує тимчасовими таблицями, на які можна посилатися у спискуFROM
.Обчислюються всі елементи списку
FROM
. Якщо у спискуFROM
вказано більше одного елемента, вони об’єднуються разом.Якщо вказано оператор
WHERE
, то всі рядки, які не задовольняють умові, вилучаються з виводу.Якщо вказано речення
GROUP BY
або якщо є виклики агрегатних функцій, то вихідні дані об’єднуються в групи рядків, які збігаються за одним або декількома значеннями, і обчислюються результати агрегатних функцій. Якщо присутній операторHAVING
, то вилучаються групи, які не задовольняють заданій умові.Фактичні вихідні рядки обчислюються за допомогою виразів
SELECT
для кожного рядка або рядка групи.SELECT DISTINCT
видаляє повторювані рядки з результату.SELECT DISTINCT ON
вилучає рядки, які збігаються за всіма вказаними виразами.За допомогою операторів
UNION
,INTERSECT
іEXCEPT
можна об’єднати результати виконання кількох інструкційSELECT
, щоб сформувати єдиний набір результатів.Оператор
UNION
повертає всі рядки, які є в одному або обох наборах результатів.Оператор
INTERSECT
повертає всі рядки, які строго знаходяться в обох наборах результатів.Оператор
EXCEPT
повертає рядки, які є в першому наборі результатів, але відсутні в другому.
Якщо вказано параметр
ORDER BY
, повернуті рядки буде відсортовано у певному порядку.якщо вказано оператор
LIMIT
абоOFFSET
, то інструкціяSELECT
повертає лише підмножину рядків результату.Якщо вказано
FOR UPDATE
,FOR NO KEY UPDATE
,FOR SHARE
абоFOR KEY SHARE
, операторSELECT
блокує вибрані рядки від одночасного оновлення.
Виберіть рядки з таблиці
Простий вид запиту має вигляд:
SELECT * FROM publisher;
Ця команда вилучить усі рядки і всі визначені користувачем стовпці з таблиці publisher
. Список вибору може також вибирати підмножину доступних стовпців або виконувати обчислення за цими стовпцями.
SELECT title, page_count / 2
FROM book;
речення FROM
є простим різновидом табличного виразу: воно зчитує лише одну таблицю. Взагалі, табличний вираз може бути складною конструкцією з базових таблиць, з’єднань і підзапитів. Але можна взагалі не використовувати табличний вираз і використовувати команду SELECT
як калькулятор (або в інший спосіб):
SELECT 40 + 2;
Речення FROM
виводить таблицю з однієї або кількох інших таблиць, наведених у списку посилань на таблиці, розділених комами. Посилання на таблицю може бути ім’ям таблиці або похідною таблицею, наприклад, підзапитом, конструкцією JOIN
або їх складними комбінаціями. Якщо в реченні FROM
вказано більше одного посилання на таблицю, то таблиці з’єднуються перехресним зв’язком. Результатом списку FROM
є проміжна віртуальна таблиця, яка потім може бути піддана перетворенням за допомогою речень WHERE
, GROUP BY
і HAVING
і, нарешті, є результатом загального табличного виразу.
Умови пошуку
Після обробки оператора FROM
кожен рядок отриманої віртуальної таблиці може бути перевірений на відповідність умові пошуку. Якщо результат умови рівний true
, то рядок зберігається у вихідній таблиці, інакше він відкидається. Для перевірки умови пошуку використовується оператор WHERE.
Ось кілька прикладів речення WHERE
:
SELECT ... FROM tbl
WHERE c1 > 5;
SELECT ... FROM tbl
WHERE c1 IN (1, 2, 3);
SELECT ... FROM tbl
WHERE c1 IN (SELECT c1 FROM t2);
SELECT ... FROM tbl
WHERE c1 IN (SELECT c3 FROM t2 WHERE c2 = tbl.c1 + 10);
SELECT ... FROM tbl
WHERE c1 BETWEEN (SELECT c3 FROM t2 WHERE c2 = tbl.c1 + 10) AND 100;
SELECT ... FROM tbl
WHERE EXISTS (SELECT c1 FROM t2 WHERE c2 > fdt.c1);
Дані для групування
Після проходження фільтра WHERE
отримана таблиця може бути згрупована за допомогою речення GROUP BY
і видалена з групи рядків за допомогою речення HAVING
. Речення GROUP BY
використовується для групування тих рядків таблиці, які мають однакові значення у всіх перелічених стовпчиках. Порядок перерахування стовпчиків не має значення. Ефект полягає в об’єднанні кожного набору рядків зі спільними значеннями в один груповий рядок, який представляє всі рядки в групі. Це робиться для того, щоб усунути надмірність у вихідних даних та/або обчислювальних агрегатах, які застосовуються до цих груп.
Виберіть списки
Як показано у попередньому розділі, вираз table у команді SELECT
створює проміжну віртуальну таблицю шляхом можливого об’єднання таблиць, представлень, вилучення рядків, групування тощо. Ця таблиця нарешті передається для обробки за допомогою списку вибору. Список вибору визначає, які саме стовпці проміжної таблиці буде виведено на екран.
Вибір елементів списку
Найпростішим видом вибору є *
, який виводить те, що створює табличний вираз. Інакше, список вибору - це список виразів значень, розділених комами. Наприклад:
SELECT first_name, last_name
FROM author;
Мітки стовпців
Записам, переліченим у списку вибору, можна присвоїти імена для подальшої обробки.
SELECT title, page_count / 2 as sheets FROM book;
Якщо за допомогою AS
не вказано назву вихідного стовпця, система призначить назву стовпця за замовчуванням. Для простих посилань на стовпці - це ім’я стовпця, на який є посилання. Для викликів функцій - це назва функції. Для складних виразів система згенерує загальну назву.
Примітка
Мітки часто називають «псевдонімами».
DISTINCT
Після обробки списку вибірки таблиця результатів може бути піддана вилученню рядків, що повторюються. Для цього безпосередньо після команди SELECT
записується ключове слово DISTINCT
:
SELECT DISTINCT publisher FROM book;
Очевидно, що два рядки вважаються різними, якщо вони відрізняються принаймні в одному стовпчику. Нульові значення вважаються рівними при порівнянні. Крім того, довільний вираз може визначати, які рядки вважати різними.
SELECT DISTINCT ON (expression [, expression ...]) select_list ...
Сортування рядків
Після того, як запит створив вихідну таблицю, її можна відсортувати за бажанням. Якщо сортування не вибрано, рядки будуть повернуті в невизначеному порядку. Фактичний порядок у цьому випадку залежатиме від типів планів сканування та об’єднання, а також порядку на диску, але на нього не можна покладатися.
Речення ORDER BY
задає порядок сортування:
SELECT title, publisher_id FROM book ORDER BY title;
Вираз(и) сортування може бути будь-яким виразом, який буде допустимим у списку вибору запиту.
Якщо вказано більше одного виразу, пізніші значення використовуються для сортування рядків, які є рівними за попередніми значеннями. Кожен вираз може супроводжуватися ключовим словом ASC
або DESC
для встановлення напрямку сортування за зростанням або спаданням. за замовчуванням використовується порядок ASC
.
Опції NULL FIRST
та NULL LAST
можна використовувати для визначення того, чи з’являтимуться нулі до або після ненульових значень у порядку сортування. За замовчуванням, нульові значення сортуються так, ніби вони більші за будь-яке ненульове значення; тобто NULL FIRST
за замовчуванням для порядку DESC
, а NULL LAST
в іншому випадку.
Зверніть увагу, що варіанти впорядкування розглядаються незалежно для кожного стовпця сортування. Наприклад, ORDER BY x, y DESC
означає ORDER BY x ASC, y DESC
, що не те саме, що ORDER BY x DESC, y DESC
.
Вираз сортування також може бути міткою стовпця або номером вихідного стовпця.
оператор ORDER BY
можна застосувати до результату комбінації UNION
, INTERSECT
або EXCEPT
, але в цьому випадку дозволяється сортування лише за іменами або номерами вихідних стовпців, а не за виразами.
Ліміт і зміщення
LIMIT
і OFFSET
дозволяють вам отримати лише частину рядків, згенерованих рештою запиту.
SELECT * FROM publisher LIMIT 10;
Наведений вище запит витягує лише перші 10 рядків з таблиці «publisher».
Якщо вказано граничну кількість рядків, буде повернуто не більше цієї кількості. Але, можливо, і менше, якщо сам запит повертає менше рядків. LIMIT ALL
- це те саме, що опустити пункт LIMIT
, так само як і LIMIT
з аргументом NULL
.
OFFSET
вказує пропустити стільки-то рядків перед початком повернення рядків. OFFSET 0
- це те саме, що опустити вираз OFFSET
, так само як і OFFSET
з аргументом NULL
.
SELECT * FROM publisher LIMIT 10 OFFSET 10;
Наведений вище запит показує 10 видавництв, починаючи з 11-го.
При використанні LIMIT
та/або OFFSET
важливо використовувати речення ORDER BY
, яке обмежує рядки результатів в унікальному порядку. Інакше ви отримаєте непередбачувану підмножину рядків запиту.
WITH
Команда WITH
надає можливість писати допоміжні оператори для використання у великих запитах. Ці оператори, які часто називають CTE, можна розглядати як визначення тимчасових таблиць, які існують лише для одного запиту. Кожен допоміжний оператор у реченні WITH
може бути оператором SELECT
, INSERT
, UPDATE
або DELETE
; а саме речення WITH
приєднується до основного оператора, який може бути оператором SELECT
, INSERT
, UPDATE
, DELETE
або MERGE
.
Вибірка у WITH
Основне значення SELECT
у WITH
полягає в тому, щоб розбивати складні запити на простіші частини.
-- select top revenue months
WITH monthly_revenue AS (SELECT EXTRACT(YEAR FROM date) AS year,
EXTRACT(MONTH FROM date) AS month,
SUM(amount) AS total_amount
FROM revenue
GROUP BY year, month
ORDER BY year, month)
SELECT *
FROM monthly_revenue
WHERE total_amount > (SELECT SUM(total_amount) / 100 FROM monthly_revenue)
ORDER BY total_amount DESC;
Рекурсивні запити
Додатковий модифікатор RECURSIVE
перетворює WITH з простої синтаксичної зручності на функцію, яка виконує речі, неможливі у стандартному SQL. Використовуючи RECURSIVE
, запит WITH
може посилатися на свій власний вивід.
WITH RECURSIVE t(n) AS (VALUES (1) UNION SELECT n + 1 FROM t WHERE n < 10)
SELECT sum(n)
FROM t;
Важливо
При роботі з рекурсивними запитами важливо бути впевненим, що рекурсивна частина запиту в кінцевому підсумку не поверне жодного кортежу, інакше запит зациклиться на невизначений час.
Оператори, що модифікують дані в операторі WITH
Ви можете використовувати більшість операторів модифікації даних (INSERT
, UPDATE
, DELETE
) в WITH
. Це дозволяє виконувати кілька різних операцій одночасно в одному запиті.
WITH deleted_rows AS (
DELETE FROM revenue
WHERE date < '2020-01-01'
RETURNING *)
INSERT
INTO revenue_log
SELECT *
FROM deleted_rows;
Об’єднання запитів
Результати двох запитів можна об’єднати за допомогою операцій об’єднання, перетину та різниці.
query1 UNION [ALL] query2
query1 INTERSECT [ALL] query2
query1 EXCEPT [ALL] query2
UNION
ефективно додає результат query2
до результату query1
(хоча немає жодних гарантій, що це саме той порядок, в якому повертаються рядки). Крім того, вона вилучає з результату повторювані рядки так само, як і DISTINCT
, якщо не використовується UNION ALL
.
WITH north_lib_member AS
(SELECT *
FROM (VALUES (1, 'Mariska', 'Syson'),
(2, 'Robenia', 'Dimitriades'),
(3, 'Loren', 'Goretti'),
(4, 'Dianna', 'Giacomuzzo'),
(5, 'Anett', 'Hayland'),
(6, 'Keeley', 'Grishelyov')) AS tbl(id, first_name, last_name)),
south_lib_member AS
(SELECT *
FROM (VALUES (5, 'Anett', 'Hayland'),
(6, 'Keeley', 'Grishelyov'),
(7, 'Say', 'Kliner'),
(8, 'Charles', 'Ede'),
(9, 'Florentia', 'Core'),
(10, 'Tiffany', 'Tingly'))
AS tbl(id, first_name, last_name))
SELECT *
FROM north_lib_member
UNION
SELECT *
FROM south_lib_member
ORDER BY id;
id |
first_name |
last_name |
---|---|---|
1 |
Mariska |
Syson |
2 |
Robenia |
Dimitriades |
3 |
Loren |
Goretti |
4 |
Dianna |
Giacomuzzo |
5 |
Anett |
Hayland |
6 |
Keeley |
Grishelyov |
7 |
Say |
Kliner |
8 |
Charles |
Ede |
9 |
Florentia |
Core |
10 |
Tiffany |
Tingly |
INTERSECT
повертає всі рядки, які є і в результаті query1
, і в результаті query2
. Повторювані рядки вилучаються, якщо не використовується INTERSECT ALL
.
WITH north_lib_member AS
(SELECT *
FROM (VALUES (1, 'Mariska', 'Syson'),
(2, 'Robenia', 'Dimitriades'),
(3, 'Loren', 'Goretti'),
(4, 'Dianna', 'Giacomuzzo'),
(5, 'Anett', 'Hayland'),
(6, 'Keeley', 'Grishelyov')) AS tbl(id, first_name, last_name)),
south_lib_member AS
(SELECT *
FROM (VALUES (5, 'Anett', 'Hayland'),
(6, 'Keeley', 'Grishelyov'),
(7, 'Say', 'Kliner'),
(8, 'Charles', 'Ede'),
(9, 'Florentia', 'Core'),
(10, 'Tiffany', 'Tingly'))
AS tbl(id, first_name, last_name))
SELECT *
FROM north_lib_member
INTERSECT
SELECT *
FROM south_lib_member
ORDER BY id;
id |
first_name |
last_name |
---|---|---|
5 |
Anett |
Hayland |
6 |
Keeley |
Grishelyov |
EXCEPT
повертає всі рядки, які є в результаті query1
, але не є в результаті query2
. (Це іноді називають різницею між двома запитами.) Знову ж таки, дублікати вилучаються, якщо не використовується EXCEPT ALL
.
WITH north_lib_member AS
(SELECT *
FROM (VALUES (1, 'Mariska', 'Syson'),
(2, 'Robenia', 'Dimitriades'),
(3, 'Loren', 'Goretti'),
(4, 'Dianna', 'Giacomuzzo'),
(5, 'Anett', 'Hayland'),
(6, 'Keeley', 'Grishelyov')) AS tbl(id, first_name, last_name)),
south_lib_member AS
(SELECT *
FROM (VALUES (5, 'Anett', 'Hayland'),
(6, 'Keeley', 'Grishelyov'),
(7, 'Say', 'Kliner'),
(8, 'Charles', 'Ede'),
(9, 'Florentia', 'Core'),
(10, 'Tiffany', 'Tingly'))
AS tbl(id, first_name, last_name))
SELECT *
FROM north_lib_member
EXCEPT
SELECT *
FROM south_lib_member
ORDER BY id;
id |
first_name |
last_name |
---|---|---|
1 |
Mariska |
Syson |
2 |
Robenia |
Dimitriades |
3 |
Loren |
Goretti |
4 |
Dianna |
Giacomuzzo |
Об’єднання даних з різних таблиць
Речення JOIN
об’єднує два елементи FROM
, які для зручності будемо називати «таблицями», хоча насправді це може бути будь-який тип елементів FROM
. Доступні типи з’єднання:
[INNER] JOIN
LEFT [OUTER] JOIN
RIGHT [OUTER] JOIN
FULL [OUTER] JOIN
Примітка
Ключові слова в дужках вище є необов’язковими.
З’єднання між столами
CREATE TABLE publisher
(
id INTEGER PRIMARY KEY,
name VARCHAR(128) NOT NULL CHECK (LENGTH(name) > 0),
website VARCHAR(255) UNIQUE,
email VARCHAR(255),
phone VARCHAR(32) UNIQUE
);
CREATE TABLE book
(
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
synopsis TEXT,
isbn VARCHAR(16),
publisher_id INTEGER NOT NULL REFERENCES publisher ON DELETE RESTRICT,
publication_date DATE,
language VARCHAR(64),
page_count INTEGER,
keywords TEXT
);
Наступні таблиці пов’язані між собою за допомогою обмеження FOREIGN KEY
. Кожен запис у таблиці book
посилається на деякий запис у таблиці publisher
.
Важливо
Оскільки вихідні таблиці містять багато даних, перед об’єднанням запитів буде створено тимчасові таблиці. Повний вміст таблиць можна завантажити за посиланнями: publisher
, book
.
Підмножини з вихідних запитів визначаються як:
SELECT *
FROM (VALUES (1, 'Zoonoodle'),
(3, 'Tanoodle'),
(4, 'Skivee'))
AS publisher(id, name);
id |
name |
---|---|
1 |
Zoonoodle |
3 |
Tanoodle |
SELECT *
FROM (VALUES (22, 'pretium nisl', 2),
(38, 'ac est lacinia nisi venenatis', 3),
(69, 'vel accumsan tellus nisi', 3),
(72, 'nam dui proin', 2),
(85, 'nam tristique', 2))
AS book(id, title, publisher_id);
id |
title |
publisher_id |
---|---|---|
22 |
pretium nisl |
2 |
38 |
ac est lacinia nisi venenatis |
3 |
69 |
vel accumsan tellus nisi |
3 |
72 |
nam dui proin |
2 |
85 |
nam tristique |
2 |
При передачі декількох таблиць в речення FROM
виконується запит CROSS JOIN
(перехресне об’єднання). Це означає, що результуюча таблиця є декартовим добутком двох вихідних таблиць.
WITH publisher AS
(SELECT *
FROM (VALUES (1, 'Zoonoodle'),
(3, 'Tanoodle'),
(4, 'Skivee'))
AS publisher(id, name)),
book AS
(SELECT *
FROM (VALUES (22, 'pretium nisl', 2),
(38, 'ac est lacinia nisi venenatis', 3),
(69, 'vel accumsan tellus nisi', 3),
(72, 'nam dui proin', 2),
(85, 'nam tristique', 2))
AS book(id, title, publisher_id))
-- querying the joined result
SELECT *
FROM book,
publisher;
id |
title |
publisher_id |
id |
name |
---|---|---|---|---|
22 |
pretium nisl |
2 |
1 |
Zoonoodle |
22 |
pretium nisl |
2 |
3 |
Tanoodle |
22 |
pretium nisl |
2 |
4 |
Skivee |
38 |
ac est lacinia nisi venenatis |
3 |
1 |
Zoonoodle |
38 |
ac est lacinia nisi venenatis |
3 |
3 |
Tanoodle |
38 |
ac est lacinia nisi venenatis |
3 |
4 |
Skivee |
69 |
vel accumsan tellus nisi |
3 |
1 |
Zoonoodle |
69 |
vel accumsan tellus nisi |
3 |
3 |
Tanoodle |
69 |
vel accumsan tellus nisi |
3 |
4 |
Skivee |
72 |
nam dui proin |
2 |
1 |
Zoonoodle |
72 |
nam dui proin |
2 |
3 |
Tanoodle |
72 |
nam dui proin |
2 |
4 |
Skivee |
85 |
nam tristique |
2 |
1 |
Zoonoodle |
85 |
nam tristique |
2 |
3 |
Tanoodle |
85 |
nam tristique |
2 |
4 |
Skivee |
За допомогою оператора WHERE рядки, які не відповідають умові, можуть бути вилучені з результату.
SELECT *
FROM book, publisher
WHERE book.publisher_id = publisher.id;
id |
title |
publisher_id |
id |
name |
---|---|---|---|---|
38 |
ac est lacinia nisi venenatis |
3 |
3 |
Tanoodle |
69 |
vel accumsan tellus nisi |
3 |
3 |
Tanoodle |
Це називається INNER JOIN
, що означає, що результуюча таблиця буде містити тільки ті рядки з лівої та правої таблиць, які мають відповідні записи один одному.
Той самий результат можна отримати за допомогою оператора JOIN
. Всі опції JOIN
є лише зручністю для нотації, оскільки вони не роблять нічого такого, чого не можна було б зробити за допомогою звичайних FROM
і WHERE
.
SELECT *
FROM book b
JOIN publisher p ON b.publisher_id = p.id;
INNER JOIN
використовується за замовчуванням при з’єднанні двох таблиць одна з одною.
Праве та ліве з’єднання
LEFT OUTER JOIN
повертає всі рядки кваліфікованого декартового добутку (тобто всі об’єднані рядки, які задовольняють його умову), плюс по одній копії кожного рядка лівої таблиці, для якого не було рядка правої таблиці, що задовольняє умову об’єднання.
SELECT *
FROM book b
LEFT JOIN publisher p ON b.publisher_id = p.id;
id |
title |
publisher_id |
id |
name |
---|---|---|---|---|
22 |
pretium nisl |
2 |
null |
null |
38 |
ac est lacinia nisi venenatis |
3 |
3 |
Tanoodle |
69 |
vel accumsan tellus nisi |
3 |
3 |
Tanoodle |
72 |
nam dui proin |
2 |
null |
null |
85 |
nam tristique |
2 |
null |
null |
І навпаки, RIGHT OUTER JOIN
повертає всі об’єднані рядки, плюс один рядок для кожного невідповідного правого рядка. Це просто зручність позначення, оскільки ви можете перетворити його на LEFT OUTER JOIN
, помінявши місцями ліву та праву таблиці.
SELECT *
FROM book b
RIGHT JOIN publisher p ON b.publisher_id = p.id;
id |
title |
publisher_id |
id |
name |
---|---|---|---|---|
38 |
ac est lacinia nisi venenatis |
3 |
3 |
Tanoodle |
69 |
vel accumsan tellus nisi |
3 |
3 |
Tanoodle |
null |
null |
null |
4 |
Skivee |
null |
null |
null |
1 |
Zoonoodle |
Full outer join
SELECT *
FROM book b
FULL JOIN publisher p ON b.publisher_id = p.id;
id |
title |
publisher_id |
id |
name |
---|---|---|---|---|
22 |
pretium nisl |
2 |
null |
null |
38 |
ac est lacinia nisi venenatis |
3 |
3 |
Tanoodle |
69 |
vel accumsan tellus nisi |
3 |
3 |
Tanoodle |
72 |
nam dui proin |
2 |
null |
null |
85 |
nam tristique |
2 |
null |
null |
null |
null |
null |
4 |
Skivee |
null |
null |
null |
1 |
Zoonoodle |