Рівень 20
Мова SQL та базові SQL-запити
БАГАТОТАБЛИЧНІ БАЗИ ДАНИХ
Рівень 20
Забагато зайвих варіантів
Грег віддає Реджі довгий список варіантів. Кілька тижнів по тому Реджі телефонує Грегові й каже, що від його списку жодної користі: жодна з кандидаток не має з ним нічого спільного.
Повністю ігнорувати захоплення не можна. Має бути інший, кращий спосіб...
Використати лише перше захоплення
Грег віддає Реджі довгий список варіантів. Кілька тижнів по тому Реджі телефонує Грегові й каже, що від його списку жодної користі: жодна з кандидаток не має з ним нічого спільного.
Давай на хвилину відволічемося, юний падаване, спробуємо розв'язати цю задачу, щоб порадувати Магістра та здобути нові знання. Напиши свій варіант відповіді на поставлене запитання, а потім звір його з правильним.
Використайте функцію SUBSTRING_INDEX, щоб виділити перше захоплення зі стовпця interests.

Тож Грег пише запит, який допоможе Реджі знайти свою пару. У запиті використано функцію SUBSTRING_INDEX, а першим захопленням має бути "тварини"

SELECT * FROM my_contacts
WHERE gender = 'Ж'
AND status = 'Не замужем'
AND state = 'МА'
AND seeking LIKE '%Мужчина%'
AND birthday > '1950-28-08'
AND birthday < '1960-28-08'
AND SUBSTRING_INDEX(interests,' ,' , 1) = 'животные';
Пара для Реджі
Нарешті! Грег знайшов пару для Реджі:
Трагічна невідповідність
Реджі домовився з Алексіс про побачення, і Грег з нетерпінням чекав на його розповідь. Він уже почав уявляти нову таблицю my_contacts, яка стане початком нової соціальної мережі.
Наступного дня Реджі стоїть під дверима Грега, і він дуже розлючений.
Реджі кричить: "Звісно, вона цікавиться тваринами. Але ти не сказав мені, що вона робить з них опудала! Там повсюди мертві тварини!"
Мозковий штурм
Який вигляд матиме наступний запит Грега після створення кількох стовпців із захопленнями?
Створення нових стовпців Interest

Грег розуміє, що написати правильний запит до одного стовпця із захопленнями надто складно. Доводиться використовувати LIKE, що іноді призводить до хибних збігів.
Але Грег уміє користуватися командою ALTER для зміни таблиць, а також розбивати текстові рядки, тож вирішує створити кілька стовпців із захопленнями й помістити кожне захоплення в окремий стовпець. Він вважає, що чотирьох стовпців буде досить.
Блиснімо не лише металокерамікою, а й інтелектом, записавши на папері свій варіант розв'язання цього завдання. А потім звіримося з варіантом Магістра.
Використовуючи команду ALTER і функцію SUBSTRING_INDEX, змініть таблицю так, щоб вона складалася з перелічених стовпців. Кількість запитів не обмежується.
contact_id
last_name
first_name
phone
email
gender
birthday
profession
city
state
status
interest 1
interest 2
interest 3
interest 4
seeking
contact_id
last_name
first_name
phone
email
gender
birthday
profession
city
state
status
interest 1
interest 2
interest 3
interest 4
seeking
Починаємо знову

Грег почувається винним через невдачу Реджі й вирішує спробувати ще раз. Для початку він дістає з таблиці запис Реджі:
Подумаймо й запишемо відповідь для цього запиту, а потім звіримося з правильним варіантом від Магістра.
Грег пише запит, який має повернути Реджі відповідну пару. Він починає з простих стовпців — gender, status, state, seeking і birthday — І ЛИШЕ ПОТІМ береться за стовпці interest. Запишіть його запит.
Усе марно...
Додавання нових стовпців ніяк не допомогло розв'язати основну проблему: структура таблиці ускладнює написання запитів до неї. Зверніть увагу: у кожній версії таблиці порушується правило атомарності даних.
... Одну хвилинку!

А що, коли створити окрему таблицю, в якій зберігається лише інформація про захоплення?
Мозковий штурм
Яку користь принесе створення нової таблиці? І як зв'язати дані з нової таблиці з наявною?
Однієї таблиці недостатньо
Отже, якщо ми обмежимося роботою з поточною таблицею, доброго рішення не існує. Ми намагалися обійти недоліки структури даних різними способами, навіть змінюючи структуру всієї таблиці. Жоден спосіб не спрацював.
Рамки однієї таблиці виявилися надто вузькими. Насправді нам потрібні додаткові таблиці, які працюють у поєднанні з поточною, дозволяючи зв'язати одну людину з кількома захопленнями. При цьому наявні дані буде повністю збережено.
Неатомарні стовпці з наявної таблиці слід перемістити до нових таблиць.
Багатотаблична база даних з інформацією про клоунів
Пам'ятаєте нашу таблицю з інформацією про клоунів із попереднього рівня? Проблема з клоунами дедалі зростає, тому ми перетворили одну таблицю на зручніший набір із кількох таблиць.
На кількох найближчих сторінках ми покажемо, чому таблицю було розбито саме так, а не інакше, і що означають усі ці стрілки та ключі. А після цього ви зможете за тими самими принципами розбити таблицю Грега.
Мозковий штурм
Як ви думаєте, що означають лінії зі стрілками? А зображення ключів?
Схема бази даних clown_tracking
Подання всіх структур бази даних (таблиць, стовпців тощо) і логічних зв'язків між ними називається схемою.
Наочне подання бази даних допоможе вам уявити, як пов'язані між собою її компоненти, проте схему можна записати й у вигляді тексту.
Опис даних (стовпців і таблиць) вашої бази даних, зокрема всіх взаємопов'язаних об'єктів і зв'язків між ними, називається СХЕМОЮ.
Спрощене подання таблиць
Ви побачили, як перетворили таблицю з інформацією про клоунів. Тепер спробуймо зробити те саме з таблицею my_contacts.
Обидва способи добре підходять для окремих таблиць, але коли потрібно побудувати діаграму з кількох таблиць, доводиться шукати щось інше.
Нижче показано спрощене подання таблиці my_contacts.
Діаграма допомагає відокремити структуру таблиці від даних, що зберігаються в ній.
Як з однієї таблиці зробити дві
Ми знаємо, що написати запит для пошуку інформації в стовпці interests у його поточному вигляді досить складно, адже в одному стовпці може зберігатися одразу кілька значень. Утім, створення кількох окремих стовпців дещо спростило наше завдання.
Праворуч зображено таблицю my_contacts у її поточному стані. Стовпець interests не атомарний, і існує лише один справді хороший спосіб зробити його атомарним: нам знадобиться нова таблиця, у якій зберігатимуться всі захоплення.
Для початку намалюймо кілька діаграм, які покажуть, який вигляд матимуть нові таблиці. Лише коли нова схема буде готова, можна переходити до створення нових таблиць або зміни даних.
Видаляємо стовпець interests і розміщуємо його в окремій таблиці.
Стовпець interests переміщується до нової таблиці.
У новій таблиці interests зберігатимуться всі захоплення з таблиці my_contacts (окремий запис для кожного захоплення).
Додаємо стовпці, за якими можна буде дізнатися, які захоплення належать тій чи іншій людині з таблиці my_contacts.
Ми винесли захоплення з таблиці my_contacts, але як визначити, кому які захоплення належать? Потрібно взяти інформацію з таблиці my_contacts і розмістити її в таблиці interests так, щоб ці дві таблиці були пов'язані між собою.
Наприклад, для цього можна включити стовпці first_name і last_name до таблиці interests.
Мозковий штурм
Ми рухаємося в правильному напрямку, але first_name і last_name — не найкращий спосіб зв'язування таблиць.
Чому?
Зв'язування таблиць

У першої версії зв'язаних таблиць був один серйозний недолік: ми намагалися використати для зв'язування поля first_name і last_name. А що, коли в таблиці my_contacts з'являться записи з однаковими значеннями first_name і last_name?
Дві таблиці мають зв'язуватися через унікальний стовпець. На щастя, оскільки ми вже узялися до нормалізації, у my_contacts такий стовпець уже є: це первинний ключ.
Ми можемо зберігати значення первинного ключа з таблиці my_contacts у таблиці interests. І що ще краще, за цим стовпцем можна буде визначити, які захоплення належать тій чи іншій людині з таблиці my_contacts. Такий спосіб зв'язування називається зовнішнім ключем.
ЗОВНІШНІЙ КЛЮЧ — це стовпець таблиці, у якому зберігаються значення ПЕРВИННОГО КЛЮЧА іншої таблиці.
Що потрібно знати про зовнішні ключі

Розумію, зовнішній ключ дозволить мені зв'язати дві таблиці. Але яка користь від значень NULL у зовнішньому ключі? Чи можна зробити так, щоб зовнішній ключ завжди був пов'язаний із батьківським ключем?
Значення NULL у зовнішньому ключі означає, що в батьківській таблиці не існує відповідного значення первинного ключа.
Проте ми можемо зробити так, щоб зовнішній ключ приймав лише осмислені значення, які існують у батьківській таблиці. Для цього слід скористатися обмеженням.
Обмеження зовнішнього ключа
Хоча ви можете створити таблицю зі стовпцем, який виконуватиме функції зовнішнього ключа, такий стовпець справді стане зовнішнім ключем лише тоді, коли ви призначите його таким у команді CREATE або ALTER. Ключ створюється в структурі, що називається обмеженням.
Створення ЗОВНІШНЬОГО КЛЮЧА як обмеження таблиці дає певні переваги.
Якщо ви спробуєте порушити правило, то отримаєте повідомлення про помилку; так запобігається випадковому порушенню зв'язків між таблицями.
Під час вставлення зовнішній ключ прийматиме лише значення, що існують у первинному ключі батьківської таблиці. Ця вимога називається цілісністю даних.
Нагадаємо, що ПЕРВИННИЙ КЛЮЧ (primary key) є одним із прикладів унікальних індексів і застосовується для унікальної ідентифікації записів таблиці. Жодні два записи таблиці не можуть мати однакових значень первинного ключа. Первинний ключ зазвичай скорочено позначають як PK (primary key).
Зовнішній ключ має бути пов'язаний з унікальним значенням із батьківської таблиці.
Це значення необов'язково має бути значенням первинного ключа, але воно неодмінно має бути унікальним.
Розділення захоплень

А тепер найскладніше: ми скористаємося іншою функцією, щоб видалити з поточного значення interests дані, скопійовані до стовпця interest1. Після цього можна буде продовжити заповнення решти стовпців за тим самим принципом.
Функція SUBSTR отримує текст стовпця interests і повертає його задану частину. Ми виділяємо символи, які були скопійовані до interest1, а також ще два символи (кому та пробіл).
Оновлення стовпців
Після виконання команди UPDATE таблиця матиме такий вигляд, як показано нижче.
Однак роботу ще не закінчено. Тепер потрібно зробити те саме для стовпців interest2, interest3 та interest4.
Юний падаване, закріпімо нові знання: напиши запит на аркуші та звір його з правильним варіантом від Магістра.
Заповніть пропуски в команді update. (Підказка. З кожним викликом SUBSTR текст стовпця «interests» стає коротшим.)
Виведення списку
Нарешті захоплення розділено по різних стовпцях. Для виведення можна скористатися простою командою SELECT, але не для всіх одразу. І ця команда не дозволить легко зібрати їх в один підсумковий набір, оскільки захоплення зберігаються в чотирьох стовпцях. Результат виглядатиме приблизно так.
Звісно, ми можемо написати чотири окремі команди SELECT для виведення всіх значень:
SELECT interest1 FROM my_contacts; SELECT interest3 FROM my_contacts;
SELECT interest2 FROM my_contacts; SELECT interest4 FROM my_contacts;
Залишилося лише зрозуміти, як вставити результат виконання цих команд у нову таблицю. На щастя, це можна зробити, причому не одним способом, а щонайменше трьома!
Перевірмо наші знання та поміркуймо над запитанням нижче. І порадуймо Магістра правильною відповіддю!
Пригадайте команду select для стовпця profession, яку ми писали раніше:
SELECT profession FROM my_contacts GROUP BY profession
ORDER BY profession;
Далі подано ТРИ СПОСОБИ використання команд select для автоматичного заповнення нової таблиці interests. Поміркуйте над командами select, insert і create. Потім перегляньте описи трьох способів. Ваше завдання — не вгадати правильний синтаксис, а обдумати наявні можливості.
Кому потрібні псевдоніми таблиць?

Вам і потрібні! Ми зараз займемося з'єднаннями та вибіркою даних із кількох таблиць. Без псевдонімів вам доведеться вводити імена таблиць знову і знову, і це вам швидко набридне.
Псевдоніми таблиць створюються майже так само, як і псевдоніми стовпців. Псевдонім таблиці вказується після першого використання імені таблиці в запиті з ключовим словом AS. У наступному прикладі він повідомляє, що таблиця my_contacts надалі буде доступна також за іменем mc.
Псевдоніми таблиць також називають паралельними іменами.
І я маю використовувати "AS" щоразу, коли потрібно створити псевдонім?
Ні, існує скорочений синтаксис призначення псевдонімів.
Просто не вказуйте ключове слово AS. Наступний запит робить те саме, що й запит на початку сторінки.
Усе, що ви хотіли знати про внутрішні з'єднання

Кожен, кому доводилося чути про SQL, напевно чув слово "з'єднання". Ця тема не така складна, як може здатися на перший погляд. Ми покажемо вам, що таке з'єднання, як вони працюють, у яких ситуаціях їх слід застосовувати і в якій ситуації застосовується та чи інша різновидність з'єднань.
Але почнемо ми з розгляду найпростішої різновидності з'єднань (яка навіть не є повноцінним з'єднанням!).
Вона відома під різними назвами. У цій книзі ми називатимемо її перехресним з'єднанням, хоча також трапляються терміни "перехресний добуток" і "декартове з'єднання".
...ось звідки насправді беруться таблиці результатів.

Припустимо, є дві таблиці: з іменами дітей і з назвами іграшок, які є в цих дітей. Ваше завдання — дізнатися, які іграшки можна подарувати кожній дитині.
Перехресні з'єднання

Результат наступного запиту є перехресним з'єднанням. Ми запитуємо дані з обох таблиць: стовпець toy з таблиці toys і стовпець boy з таблиці boys.
Перехресне з'єднання створює пару з кожного значення першої таблиці та кожного значення з другої таблиці.
Перехресне з'єднання (CROSS JOIN) повертає комбінації кожного запису першої таблиці з кожним записом другої таблиці.
Результат з'єднання складається з 20 записів (5 іграшок * 4 хлопчики), тобто з усіх можливих комбінацій.
Поширені запитання
І навіщо мені це потрібно?

Про перехресні з'єднання важливо знати, тому що під час експериментів із з'єднаннями можна випадково отримати перехресний результат. Це допоможе вам виправити неправильно написаний запит. Перевірте, таке трапляється. Крім того, перехресні з'єднання іноді використовують для тестування швидкодії РСКБД та її конфігурації. Їх обробка займає порівняно багато часу, що спрощує аналіз і порівняння.

А якщо використати запит такого вигляду: SELECT * FROM toys CROSS JOIN boys; Що станеться при використанні SELECT * ?

Спробуйте самі. Ви отримаєте ті самі 20 записів, але в них будуть усі 4 стовпці.
Внутрішнім з'єднанням (INNER JOIN) називається перехресне з'єднання, з результатів якого частину записів виключено за умовою запиту.

Що станеться при перехресному з'єднанні двох дуже великих таблиць?

Ви отримаєте величезну кількість записів. З перехресним з'єднанням краще не експериментувати: за такого гігантського обсягу даних, що повертаються, ваш комп'ютер може "зависнути"!

Чи існує інший синтаксис для таких запитів?
Так, існує. Замість ключових слів CROSS JOIN можна поставити кому:
SELECT toys.toy, boys.boy
FROM toys, boys;

Раніше я чув термін "внутрішнє з'єднання". Це те саме, що й перехресне з'єднання?

Перехресне з'єднання є різновидом внутрішнього з'єднання. По суті, внутрішнє з'єднання — це перехресне з'єднання, з результатів якого деякі записи виключено за критерієм запиту. Внутрішнє з'єднання незабаром буде описано докладніше, а поки що просто запам'ятайте це!

Мозковий штурм
Як ви думаєте, який результат поверне наступний запит:
SELECT b1.boy, b2.boy
FROM boys AS b1 CROSS JOIN boys AS b2;
Спробуйте самі.
Відкрийте своє внутрішнє з'єднання

Зрозумів! Отже, я можу зв'язати нові таблиці з новою версією my_contacts. Мені не потрібно писати десяток SELECT, достатньо включити таблиці у внутрішнє з'єднання.
Усе тільки починається.
Думаєте, це все? Ми розглянемо лише одну різновидність одного типу з'єднань. І вам ще належить дізнатися багато всього про цей та інші види з'єднань, перш ніж ви зможете ефективно й розумно застосовувати їх на практиці.
Внутрішнє з'єднання комбінує записи двох таблиць відповідно до заданої умови. Стовпці включаються до вихідного набору лише в тому випадку, якщо з'єднаний запис задовольняє умову. Давайте уважніше розглянемо синтаксис.
Внутрішнє з'єднання комбінує записи з двох таблиць відповідно до заданої умови.
Внутрішнє з'єднання в дії: еквіз'єднання
Розгляньмо наведені нижче таблиці. У кожного хлопчика є лише одна іграшка. Зв'язок належить до типу "один до одного", а toy_id — зовнішній ключ.
Усе, що потрібно, — визначити, яка іграшка належить кожному з хлопчиків. Ми можемо скористатися внутрішнім з'єднанням з оператором = для пошуку збігів зовнішнього ключа boys з первинним ключем toys.
Еквівалентне з'єднання — це внутрішнє з'єднання з перевіркою рівності.
Внутрішнє з'єднання в дії: нееквівалентне з'єднання
Нееквівалентне з'єднання повертає записи, у яких задані значення стовпців не рівні. Для прикладу розглянемо ті самі дві таблиці boys і toys. Використовуючи нееквівалентне з'єднання, ми можемо точно дізнатися, яких іграшок немає в кожного з хлопчиків (такий результат зручніший під час пошуку подарунка на день народження).
Нееквівалентне з'єднання перевіряє неспівпадіння значень.
Останнє внутрішнє з'єднання: природне з'єднання
Залишилася лише одна різновидність внутрішніх з'єднань — так звані природні з'єднання. Природні з'єднання можливі лише в тому випадку, якщо стовпець, за яким виконується з'єднання, має однакові імена в обох таблицях. Давайте ще раз розглянемо ці дві таблиці.
Як і раніше, ми хочемо дізнатися, яка іграшка є в кожного з хлопчиків. Природне з'єднання розпізнає збіг імен стовпців у двох таблицях і поверне відповідні комбінації.
Природне з'єднання зв'язує записи за значеннями стовпців з однаковими іменами.
Вкладені запити?
Грег поступово починає розуміти можливості з'єднань. Він бачить, що розбиття бази даних на таблиці має сенс, а працювати з добре спроєктованими таблицями не так уже й складно. Грег навіть планує розширити базу даних gregs_list.
Але мені досі часто доводиться вводити один запит, а потім використовувати його результати на вході іншого запиту, хоча зручніше було б розмістити один запит усередині іншого. Та це лише мрії...
Запит усередині іншого запиту?
Хіба так можна?
Відверта розмова про псевдоніми таблиць і стовпців
Інтерв'ю тижня:
СЕНСАЦІЯ! РОЗСЛІДУВАННЯ! Псевдоніми SQL: що вони приховують?
Galaxy QA Academy: Ласкаво просимо, Псевдонім таблиці та Псевдонім стовпця. Ми раді, що ви сьогодні з нами. Сподіваємося, ви допоможете нам прояснити деякі непорозуміння.
Псевдонім таблиці: Ще б пак, я теж дуже радий. І ви можете для стислості називати нас ПТ і ПС під час цього інтерв'ю (сміється).
Galaxy QA Academy: Ха-ха! Так, це буде доречно. Отже, ПС, почнемо з вас. Навіщо така таємничість? Ви щось намагаєтеся приховати?
Псевдонім стовпця: Аж ніяк! Якщо на те пішло, я намагаюся все прояснити. Адже зараз я говорю за нас обох, правда, ПТ?
ПТ: Звісно. У випадку ПС і так зрозуміло, що він намагається зробити: він бере довгі або надлишкові імена стовпців і спрощує роботу з ними. Просто для зручності. Крім того, він надає таблиці результатів із зрозумілими іменами стовпців. Зі мною справа трохи інакша.
QA Academy: Мусимо визнати, що ми не так добре знайомі з вами, ПТ. Ми бачили, як ви працюєте, але ще не до кінця розуміємо, що саме ви робите. Адже коли вас використовують у запитах, ви не відображаєтеся в результатах.
ПТ: Так, це правда. Але, на мою думку, ви не вловлюєте мого вищого призначення.
QA Academy: Вище призначення? Цікаво, продовжуйте.
ПТ: Я існую для того, щоб спростити написання запитів.
ПС: І ще ти допомагаєш мені із з'єднаннями, ПТ.
QA Academy: Нічого не розумію. Може, наведете приклад?
ПТ: Давайте розглянемо синтаксис. Гадаю, вам буде цілком зрозуміло, що я роблю:
SELECT mc.last_name, mc.first_name,
p.profession
FROM my_contacts AS mc
INNER JOIN
profession AS p
WHERE mc.contact_id = p.id;
QA Academy: Зрозуміло! Скрізь, де мені довелося б вводити my_contacts, достатньо ввести mc. А profession замінюється на p. Так набагато простіше і значно зручніше, коли мені доводиться включати два імені стовпців в один запит.
ПТ: Особливо коли таблиці мають схожі імена. Спрощення допомагає не лише написати потрібний запит, а й зрозуміти його, коли ви повернетеся до нього через якийсь час.
QA Academy: Щиро дякуємо, ПТ і ПС. Нам було дуже... е... куди вони поділися?
Нові інструменти
Після глави 8 ви можете будувати з'єднання, як справжній SQL-професіонал. Нижче перелічено основні поняття цієї глави. Повний список інструментів наведено в додатку 3.
Запити всередині запитів
І всі помітять, який я... (Як це називається? Витонченість? Вишуканість? Елегантність?)
Мені, будь ласка, запит із двох частин. З'єднання — це чудово, але іноді виникає потреба звернутися до бази даних одразу з кількома запитаннями. Або взяти результат одного запиту й використати його як вхідні дані іншого запиту. У цьому вам допоможуть підзапити, які також називають підпорядкованими запитами. Вони запобігають дублюванню даних, роблять запити динамічнішими й навіть можуть допомогти вам потрапити на вечірку у вищому товаристві. (А може, й ні, але два з трьох — теж непогано!)
Грег береться до пошуку роботи

До цього моменту база даних gregs_list була суто безкорисливою справою. Вона допомогла Грегові підбирати пари для своїх друзів, але заробітку не приносила.
Раптом Грег збагнув, що міг би відкрити власне кадрове агентство, у якому підбирав би людям зі свого списку різні варіанти роботи.
Маючи нові функціональні можливості, я можу створити власне кадрове агентство.
Грег знає, що для знайомих, які зацікавляться його пропозицією, в базу даних доведеться додати нові таблиці. Замість того щоб розміщувати інформацію в my_contacts, він вирішує створити окремі таблиці зі зв'язками "один до одного" з двох причин.
По-перше, не всі учасники списку my_contacts зацікавлені в його послугах. Окрема таблиця дозволяє позбутися значень NULL у my_contacts.
По-друге, якщо колись Грег найме людей, які допомагатимуть йому вести бізнес, інформація про доходи може виявитися конфіденційною. У цьому разі Грег надаватиме доступ до таких таблиць лише тим, кому він справді необхідний.
У списку Грега з'являються нові таблиці
Грег додав до своєї бази даних нові таблиці для зберігання інформації про бажану посаду й діапазон заробітку, а також про поточну посаду й заробіток. Крім того, Грег створює просту таблицю для зберігання інформації про наявні вакансії.
Грег використовує внутрішнє з'єднання
Грег отримав інформацію про чудову вакансію й тепер намагається знайти кандидатів на неї у своїй базі даних. Він хоче знайти найкращий збіг, адже якщо його кандидата наймуть, він отримає премію.
Падаване, перевіримо, як ти зрозумів нову тему, і закріпимо твої знання. Спробуй написати такий запит
Напишіть запит для вибірки з бази даних кандидатів, які задовольняють поставлені умови.
Два запити перетворюються на запит із підзапитом
Фактично ми лише об'єднуємо два запити в один. Перший запит називається зовнішнім, а другий — внутрішнім.
Підзапити: коли одного запиту недостатньо
Підзапит — це не що інше, як запит усередині іншого запиту. "Охопний" запит називається зовнішнім, а "вкладений" — внутрішнім запитом, або підзапитом.
Оскільки підзапит використовує оператор =, він повертає одне значення, один запис з одного стовпця (іноді його називають "коміркою", але в SQL використовують термін скалярне значення). Це значення порівнюється зі стовпцями в умові WHERE.
Підзапит у дії
Подивімося, як працює аналогічний запит до таблиці my_contacts. РСКБД зчитує скалярне значення з таблиці zip_code і порівнює його зі стовпцями в умові WHERE.
Поширені запитання
Чому не можна зробити те саме за допомогою з'єднань?

Можна, але дехто вважає, що з підзапитами працювати простіше, ніж зі з'єднаннями. Добре мати свободу вибору синтаксису.
Той самий запит можна реалізувати так:
SELECT last_name, first_name
From my_contacts mc
NATURAL JOIN zip_code zc
WHERE zc.city = 'Мемфис'
AND zc.state = 'TN'
Внутрішній чи зовнішній?
ЗОВНІШНІЙ ЗАПИТ
Знаєш, Внутрішній Запите, ти мені взагалі-то не потрібен. Я чудово обійдуся й без тебе.
Так, звісно. Ти даєш мені один маленький результат, а користувачам потрібні дані, і до того ж БАГАТО. Я даю їм ці дані. Думаю, якби тебе не було, їх би це цілком влаштувало.
Не доведеться, якщо додати умову WHERE.
Потрібен, ще й як. Яка користь від одного стовпця одного запису? Він просто не містить достатньо інформації.
Звісно, але я працюю сам по собі.
ВНУТРІШНІЙ ЗАПИТ
А я й без тебе обійдуся. Думаєш, це так весело — давати тобі конкретний, точний результат лише для того, щоб ти перетворив його на набір відповідних записів? Кількість не замінює якість, знаєш.
Ні, я надаю твоїм результатам певну подобу спеціалізації. Без мене тобі доведеться порпатися в усіх даних таблиці.
Я І Є твоя умова WHERE, до того ж гранично конкретна. Власне, ти мені не такий уже й потрібен.
Гаразд. Можливо, нам усе ж варто працювати разом. Я визначаю напрямок пошуку твоїх результатів.
Як і я.
Правила для підзапитів
Нижче перелічено деякі правила, яким мають відповідати підзапити. Заповніть пропуски словами з наступного набору (деякі слова можна використовувати кілька разів).
Корельований підзапит з NOT EXISTS
Дуже поширений сценарій використання корельованого підзапиту — пошук у зовнішньому запиті всіх записів, яким не відповідають записи в пов'язаній таблиці.
Припустімо, Грег хоче розширити коло клієнтів своєї служби пошуку роботи. Для цього він збирається розіслати повідомлення всім людям з my_contacts, чиї дані ще не містяться в таблиці job_current. Для пошуку записів він використовує умову NOT EXISTS.
Корельований підзапит з NOT EXISTS

За аналогією з IN і NOT IN у підзапитах також можна використовувати ключові слова EXISTS і NOT EXISTS. Наведений нижче підзапит повертає дані з my_contacts, у яких значення contact_id хоча б один раз трапляється в таблиці contact_interest.
Напишіть запити для отримання відповідей на такі запитання (використовуйте з'єднання та некорельовані підзапити там, де це доречно). Використовуйте схему бази даних gregs_list.
Виведіть усі посади із зарплатою, що дорівнює найбільшій зарплаті з таблиці job_listings.
Служба пошуку роботи Грега приймає замовлення

Грег цілком освоївся з вибіркою даних за допомогою підзапитів. Він навіть навчився користуватися ними в командах INSERT, UPDATE і DELETE.
Він зняв невеликий офіс і збирається влаштувати вечірку, щоб відсвяткувати початок нової справи.
Цікаво, чи вдасться мені знайти свого першого працівника в таблиці job_desired...
Поширені запитання
Отже, підзапит можна вкласти в інший підзапит?

Безумовно. Кількість рівнів вкладення підзапитів обмежена, але в більшості РСКБД вона значно перевищує практичну "стелю"
Як найкраще побудувати підзапит усередині підзапиту?
Спробуйте написати маленькі підзапити для різних частин запитання. Придивіться до них і спробуйте скомбінувати. Якщо ви намагаєтеся знайти людей із такою самою зарплатою, як у найвисокооплачуванішого веб-дизайнера, розбиття запиту може виглядати так:
Знайти найвисокооплачуванішого веб-дизайнера
Знайти людей, які заробляють х
після чого підставити перший запит замість х.

Підзапити мені не подобаються, чи можу я використовувати замість них з'єднання?
У більшості випадків так, можете, але спочатку необхідно дещо дізнатися про з'єднання.
Ліве, праве...

З іншого боку, зовнішнє з'єднання значно більше залежить від відношень між двома таблицями, ніж усі розглянуті раніше типи з'єднань.
Ліве зовнішнє з'єднання (LEFT OUTER JOIN) перебирає всі записи лівої таблиці й шукає для кожного відповідність серед записів правої таблиці. Зокрема, це зручно, коли між лівою та правою таблицями існує зв'язок типу "один до багатьох".
Щоб зрозуміти логіку зовнішнього з'єднання, необхідно знати, яка таблиця розташована "ліворуч", а яка "праворуч".
У лівому зовнішньому з'єднанні таблиця, що йде після FROM, але ПЕРЕД JOIN, вважається "лівою", а таблиця, що йде ПІСЛЯ JOIN, вважається "правою".
У лівому зовнішньому з'єднанні для КОЖНОГО ЗАПИСУ ЛІВОЇ таблиці шукається відповідність серед записів правої таблиці.
Приклад лівого зовнішнього з'єднання
За допомогою лівого зовнішнього з'єднання ми можемо дізнатися, яка іграшка належить тій чи іншій дівчинці.
Нижче наведено синтаксис лівого зовнішнього з'єднання на прикладі таблиць, які вже використовувалися. Таблицю girls указано першою після FROM, тому вона вважається лівою таблицею; далі йдуть ключові слова LEFT OUTER JOIN; і нарешті, таблиця toys вважається правою таблицею.
І все? Питається, чого ми досягли? Виходить, зовнішнє з'єднання нічим не відрізняється від внутрішнього.
Відрізняється: зовнішнє з'єднання повертає запис незалежно від того, чи є в нього збіг у таблиці.
Відсутність збігів позначається значенням NULL. У нашому прикладі з дівчатками та іграшками NULL у результатах означає, що ця іграшка не належить жодній із дівчаток. Дуже цінна інформація!

Значення NULL у результатах лівого зовнішнього з'єднання означає, що права таблиця не містить значень, які відповідають лівій таблиці.
Напишіть запити для отримання відповідей на такі запитання (використовуйте з'єднання та некорельовані підзапити там, де це доречно). Використовуйте схему бази даних gregs_list.
Виведіть імена та прізвища людей із зарплатою вище середньої.
Праве зовнішнє з'єднання
Праве зовнішнє з'єднання майже повністю аналогічне лівому зовнішньому з'єднанню, окрім того, що воно порівнює праву таблицю з лівою. Наступні два запити повертають абсолютно однакові результати.
Праве зовнішнє з'єднання шукає в лівій таблиці відповідності для правої таблиці.
І все? Питається, чого ми досягли? Виходить, зовнішнє з'єднання нічим не відрізняється від внутрішнього.
Відрізняється: зовнішнє з'єднання повертає запис незалежно від того, чи є в нього збіг у таблиці.
Відсутність збігів позначається значенням NULL. У нашому прикладі з дівчатками та іграшками NULL у результатах означає, що ця іграшка не належить жодній із дівчаток. Дуже цінна інформація!
Створення нової таблиці

Ми можемо створити таблицю з переліком усіх клоунів та ідентифікаторів їхніх начальників. Ось як виглядає ієрархія з ідентифікаторами.
У новій таблиці для кожного клоуна вказано ідентифікатор його начальника з таблиці clown_info.
Самопосилальний зовнішній ключ
До таблиці clown_info слід додати новий стовпець з інформацією про те, хто є начальником того чи іншого клоуна. У новому стовпці зберігатиметься ідентифікатор начальника. Ми назвемо його boss_id, як у таблиці clown_boss.
У таблиці clown_boss стовпець boss_id був зовнішнім ключем. Після додавання до clown_info цей стовпець усе одно залишається зовнішнім ключем, хоча й розташований в іншій таблиці. Такі зовнішні ключі, що посилаються на інше поле тієї самої таблиці, називаються самопосилальними.
Ми вважаємо, що Містер Сніфлз є власним начальником, тому в нього значення boss_id збігається з id.
Самопосилальним зовнішнім ключем називається первинний ключ таблиці, що використовується в тій самій таблиці для іншої мети.
САМОПОСИЛАЛЬНИЙ зовнішній ключ — це первинний ключ таблиці, що використовується в тій самій таблиці для інших цілей.
З'єднання таблиці із самою собою
Припустімо, ми хочемо вивести список усіх клоунів та їхніх начальників. Список усіх клоунів з ідентифікаторами начальників легко вивести запитом SELECT:
SELECT name, boss_id FROM clown_info;
Але нам потрібні пари: ім'я клоуна та ім'я його начальника.
Напишіть запити для отримання відповідей на такі запитання (використовуйте з'єднання та некорельовані підзапити там, де це доречно). Використовуйте схему бази даних gregs_list.
Знайдіть усіх веб-дизайнерів, у яких поштовий індекс (zip code) збігається з поштовим індексом будь-якої вакансії веб-дизайнера з таблиці job_listings.
Об'єднання
Існує ще один спосіб отримання об'єднаних результатів таблиць — так звані об'єднання (ключове слово UNION).
Об'єднання зводить в одну таблицю результати двох і більше запитів на основі того, що вказано в запиті SELECT. Об'єднання можна трактувати як значення всіх запитів, що "перетинаються".
Грег помічає, що в результаті немає дублікатів, проте посади перелічено не по порядку, тож він намагається повторити запит, додавши умову ORDER BY до кожної команди SELECT.
Мозковий штурм
Як ви думаєте, що сталося під час виконання нового запиту?
Правила об'єднань у дії
Кількість стовпців у командах SELECT має бути однаковою. Не можна вибрати два стовпці однією командою, а ще один стовпець — іншою.
Мозковий штурм
Як ви думаєте, що станеться, якщо стовпці, які об'єднуються, належать до різних типів даних?
UNION ALL
UNION ALL працює точно так само, як UNION, за винятком того, що він повертає всі значення зі стовпців замість одного екземпляра з кожної групи дублікатів.
До цього моменту в наших об'єднаннях використовувалися стовпці зі збіжним типом даних. Проте в деяких ситуаціях може виникнути потреба створити об'єднання з різнотипних стовпців.
Коли ми кажемо, що типи даних мають бути сумісними один з одним, це означає, що за потреби їх можна привести до спільного типу; а якщо цього зробити не вдасться, виконання запиту призведе до помилки.
Припустімо, в об'єднанні тип INTEGER поєднується з типом VARCHAR. Оскільки дані VARCHAR не можна перетворити на ціле число, в отриманих результатах тип INTEGER буде перетворено на VARCHAR.
Кінець SQL-рівнів
Тепер ти справжній SQL-джедай
Практичне завдання рівня
Вау, ти зробив це! Дійшов до самого кінця курсу, і це твоє останнє практичне завдання, виконай його з честю джедая SQL. Але я думаю, що після стількох спільних вправ ти напишеш усі навчальні запити з першого разу.
Теоретичний тест рівня SQL-3
Форма для надсилання створених вами SQL-запитів




































































