Найчастіше при використанні SQL для вибірки інформації з таблиць, користувач отримує надлишкові дані, які полягають в наявності абсолютно ідентичних повторюваних рядків. Для виключення цієї ситуації використовується аргумент SQL distinct в реченні Select. У даній статті будуть розглянуті приклади використання даного аргументу, а також ситуації, в яких від застосування аргументу краще відмовитися. Перш ніж ми приступимо до розгляду конкретних прикладів, створимо в базі даних пару необхідних таблиць.
Підготовка таблиць
Уявімо, що у нас у базі даних зберігається інформація про шпалерах, представлена у двох таблицях. Це таблиця Oboi (шпалери) з полями: id (унікальний ідентифікатор), type (тип шпалер – паперові, вінілові та ін), color (колір), struct (структура) і ргісе (ціна). Таблиця Ostatki (залишки) з полями id_oboi (посилання на унікальний ідентифікатор таблиці Oboi) та count (кількість рулонів на складі).
Заповнимо таблиці даними. У таблицю із шпалерами додамо 9 записів:
Oboi
id
type
color
struct
price
1
Паперові
Мультиколор
Тиснені
569
2
Паперові двошарові
Беж
Гладка
1148
3
Вінілові
Оранж
Тиснені
504
4
Флізелінові
Беж
Тиснені
10209
5
Паперові двошарові
Беж
Гладка
1506
6
Паперові
Мультиколор
Гладка
954
7
Вінілові
Коричневі
Гладка
372
8
Флізелінові
Білі
Тиснені
9801
9
Тканинні
Рожеві
Гладка
11665
У таблицю із залишками – також дев'ять записів:
Ostatki
id_oboi
count
1
8
2
12
3
24
4
9
5
16
6
7
7
24
8
32
9
11
Приступимо до опису порядку використання distinct в SQL.
Місце в реченні Select distinct
Аргумент distinct слід поміщати відразу після ключового слова Select запиту. Він застосовується до всіх стовпців, вказаними в пропозиції Select, тому що буде виключати з підсумкового результату запиту абсолютно ідентичні рядка. Таким чином, достатньо один раз вказати при написанні SQL-запиту «select distinct». Виняток становить використання distinct всередині агрегатних функцій, що розглянемо трохи пізніше.
Слід пам'ятати, що більшість СУБД і не розпізнає ваш запит виду:
SELECT distinct Ostatki.Count, distinct Oboi.* FROM Oboi INNER JOIN Ostatki ON Oboi.id = Ostatki.id_oboi
Тут кілька разів розглянутий зазначений аргумент або вказаний один раз, але перед другим, третім або іншим выбираемым стовпцем. Ви отримаєте помилку з посиланням на неточності в синтаксисі.
Застосування distinct в стандартних запитах
Очевидно, що при грамотному побудові структури таблиць і їх заповненні, всередині однієї таблиці виключені ситуації, коли зустрічаються абсолютно ідентичні рядка. Тому виконання запиту «Select distinct *» з вибіркою з однієї таблиці практично недоцільно.
Уявімо ситуацію, коли нам необхідно дізнатися, якого типу є у нас шпалери, відразу для зручності виконаємо сортування за типом:
SELECT Oboi.type FROM Oboi order by type
І отримаємо результат:
type
Паперові
Паперові
Паперові двошарові
Паперові двошарові
Вінілові
Вінілові
Тканинні
Флізелінові
Флізелінові
Як бачимо, у таблиці присутні повторювані рядки. Якщо ж ми додамо в пропозицію Select distinct:
SELECT distinct Oboi.type FROM Oboi order by type
то отримаємо результат без повторів:
type
Паперові
Паперові двошарові
Вінілові
Тканинні
Флізелінові
Таким чином, якщо грамотно вносилися дані в таблиці, то відразу по дзвінку або запитом покупців ми зможемо відповісти, що рідких шпалер, стеклообоев і акрилових шпалер в наявності в магазині немає. Враховуючи, що асортимент у магазинах зазвичай не обмежується однією сотнею шпалер, переглянути перелік з неуникальних типів було б досить трудомістке.
Застосування distinct всередині агрегатних функцій
Аргумент SQL distinct можна використовувати з будь-агрегатної функцією. Але для Min і Max його застосування не дасть ніякого ефекту, а при обчисленні суми або середнього значення рідко можна уявити ситуацію, коли не потрібно було б враховувати повтори. Припустимо, ми хочемо дізнатися, наскільки заповнений наш склад, і для цього відправляємо запит, який обчислює загальну кількість рулонів на складі:
SELECT sum(Ostatki.count) FROM Ostatki
Запит видасть відповідь 143. Якщо ж ми змінимо на:
SELECT sum(distinct Ostatki.count) FROM Ostatki
то отримаємо всього 119 адже шпалери під артикулами 3 і 7 знаходяться на складі в однаковій кількості. Однак очевидно, що ця відповідь невірний. Найчастіше в SQL distinct застосовується з функцією Count. Так, без праці ми можемо дізнатися, скільки унікальних видів шпалер у нас взагалі є:
SELECT count(distinct Oboi.type) FROM Oboi
І отримати результат 5 – звичайні паперові двошарові, вінілові, тканинні і бамбукові. Напевно всі бачили рекламу типу: «Тільки у нас більше 20 видів різних шпалер!», під якої мається на увазі, що в даному магазині не пара десятків рулонів, а шпалери найрізноманітніших сучасних типів. Цікаво, що у одному запиті можна зазначати декілька функцій Count як з атрибутом distinct, так і без нього. Тобто це єдина ситуація, коли distinct в Select'е може бути кілька разів.
Коли слід відмовитися від застосування аргументу
Від застосування аргументу SQL distinct слід відмовитися в одному з двох випадків: Ви виконуєте вибірку з таблиць і впевнені в унікальності значень у кожній. У такому разі застосування аргументу недоцільно, адже це додаткове навантаження на сервер або клієнта (залежно від виду СУБД). Ви боїтеся втратити потрібні дані. Пояснимо. Припустимо, начальник просить вас вивести список шпалер, які у вас є, з зазначенням двох стовпців – тип і колір. За звичкою ви вказуєте аргумент distinct:
SELECT distinct Oboi.type, Oboi.color FROM Oboi ORDER BY Oboi.type
І – втрачаєте частину даних:
type
color
Паперові
Мультиколор
Паперові двошарові
Беж
Вінілові
Коричневі
Вінілові
Оранж
Тканинні
Рожеві
Флізелінові
Беж
Флізелінові
Білі
Може скластися враження, що паперових шпалер (звичайних і двошарових) у нас всього по одному виду, хоча насправді навіть в нашій маленькій таблиці їх по два артикула (результат без distinct):
type
color
Паперові
Мультиколор
Паперові
Мультиколор
Паперові двошарові
Беж
Паперові двошарові
Беж
Вінілові
Коричневі
Вінілові
Оранж
Тканинні
Рожеві
Флізелінові
Білі
Флізелінові
Беж
Тому, як і при написанні будь-якого запиту, з аргументом distinct треба бути акуратним і грамотно вирішувати питання з її застосуванням в залежності від поставленої задачі.
Альтернатива distinct
Протилежність аргументу distinct – аргумент All. При його застосуванні повторювані рядки зберігаються. Але оскільки за замовчуванням СУБД так і вважає, що потрібно виводити всі значення, то аргумент All – це скоріше уточнитель, ніж реальний функціональний аргумент.
Сподіваємося, що наше тепер зрозуміло, коли застосовується distinct (SQL). Опис дало вам повну інформацію про доцільність застосування цього аргументу при вирішенні різних завдань. Адже, як виявилося, навіть такий простий аргумент у своєму застосуванні приховує цілком відчутну ймовірність втратити деякі дані і вивести неточну інформацію.