Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
15
Добавлен:
07.02.2016
Размер:
197.12 Кб
Скачать

Лабораторна робота №3

ТЕМА РОБОТИ: Зв'язок таблиць.

МЕТА РОБОТИ: Навчитися використати декілько таблиць і зв'язки між ними.

Теоретичні відомості Запити з використанням декількох таблиць

Торкаючись питань проектування баз даних, ми з'ясували, що бази даних - це безліч взаємозалежних сутностей або відносин (таблиць) у термінології реляційних СУБД. При проектуванні прагнуть створювати таблиці, у кожній з яких утримувалася б інформація про один тип сутностей. Це полегшує модифікацію бази даних і підтримку її цілісності. Але такий підхід важко засвоюється починаючими проектантами, які намагаються прив'язати проект до майбутніх додатків і так організувати таблиці, щоб у кожній з них зберігалося все необхідне для реалізації можливих запитів. Типове питання: як же одержати відомості про те, де купити продукти для готування того або іншого блюда й визначити його калорійність і вартість, якщо потрібні дані "розсипані" по сімох різних таблицях? Чи не краще мати одну велику таблицю, що містить всі відомості бази даних ПАНСІОН ?

Навіть при відсутності засобів одночасного доступу до багатьом таблицям небажаний проект, у якому інформація про багатьох типах сутностей перемішана в одній таблиці. SQL же має чудовий механізм для одночасної або послідовної обробки даних з декількох взаємозалежних таблиць. У ньому реалізовані можливості "з'єднувати" або "поєднувати" кілька таблиць і так називані "вкладені підзапити". Наприклад, щоб одержати перелік постачальників продуктів, необхідних для готування Сирників, можливий запит

SELECT Продукт, Ціна, Назва, Статус

FROM Продукти, Склад, Блюда, Поставки, Постачальники

WHERE Продукти.ПР = Склад.ПР

AND Склад.БЛ = Блюда.БЛ

AND Поставки.ПР = Склад.ПР

AND Поставки.ПС = Постачальники.ПС

AND Блюдо = 'Сирники'

AND Ціна IS NOT NULL;

Продукт

Ціна

Назва

Статус

Яйця

1.8

ПОРТОС

кооператив

Яйця

2.

КОРЮШКА

кооператив

Сметана

3.6

ПОРТОС

кооператив

Сметана

2.2

ОГУРЕЧИК

ферма

Сир

1.

ОГУРЕЧИК

ферма

Борошно

0.5

УРОЖАЙ

коопторг

Цукор

0.94

ТУЛЬСЬКИЙ

універсам

Цукор

1.

УРОЖАЙ

коопторг

Він отриманий у такий спосіб: СУБД послідовно формує рядки декартового добутку таблиць, перерахованих у фразі FROM, перевіряє, чи задовольняють дані сформованого рядка умовам фрази WHERE, і якщо задовольняють, то включає у відповідь на запит ті поля, які перераховані у фразі SELECT.

Варто підкреслити, що в SELECT й WHERE (щоб уникнути двозначності) посилання на всі (*) або окремі стовпці можуть (а іноді й повинні) уточнюватися ім'ям відповідної таблиці, наприклад, Поставки.ПС, Постачальники.ПС, Меню.*, Склад.БЛ, Блюда.* і т.п.

Очевидно, що за допомогою з'єднання нескладно сформувати запит на обробку даних з декількох таблиць. Крім того, у такий запит можна включити будь-які частини пропозиції SELECT. Отже, з'єднання дозволяють обробляти безліч взаємозалежних таблиць як єдину таблицю, у якій перемішана інформація про велику кількість сутностей. Тому починаючий проектант бази даних може спокійно створювати маленькі нормалізовані таблиці, тому що він завжди може одержати з них будь-яку "більшу" таблицю.

Крім механізму з'єднань в SQL є механізм вкладених підзапитів, що дозволяє об'єднати кілька простих запитів у єдиній пропозиції SELECT. Іншими словами, вкладений підзапит - це вже знайомий нам підзапит (з невеликими обмеженнями), що вкладений в WHERE фразу іншого вкладеного підзапита або WHERE фразу основного запиту.

Для ілюстрації вкладеного підзапита повернемося до попереднього приклада й спробуємо одержати перелік тих постачальників продуктів для Сирників, які поставляють потрібні продукти за мінімальну ціну.

SELECT Продукт, Ціна, Назва, Статус

FROM Продукти, Склад, Блюда, Поставки, Постачальники

WHERE Продукти.ПР = Склад.ПР

AND Склад.БЛ = Блюда.БЛ

AND Поставки.ПР = Склад.ПР

AND Поставки.ПС = Постачальники.ПС

AND Блюдо = 'Сирники'

AND Ціна = ( SELECT MIN(Ціна)

FROM Поставки X

WHERE X.ПР = Поставки.ПР );

Результат запиту має вигляд

Продукт

Ціна

Назва

Статус

Яйця

1.8

ПОРТОС

кооператив

Цукор

0.94

ТУЛЬСЬКИЙ

універсам

Борошно

0.5

УРОЖАЙ

коопторг

Сметана

2.2

ОГУРЕЧИК

ферма

Сир

1.

ОГУРЕЧИК

ферма

Тут за допомогою підзапита, розміщеного в трьох останніх рядках запиту, описується процес визначення мінімальної ціни кожного продукту для Сирників і пошук постачальника, що пропонує цей продукт за таку ціну.

Запити, що використають з'єднання

Декартовий добуток таблиць

У літературі показано, що з'єднання - це підмножини декартова добутку. Тому що декартово добуток n таблиць - це таблиця, що містить всі можливі рядки r, такі, що r є зчепленням якого-небудь рядка з першої таблиці, рядка із другої таблиці, ... і рядка з n-й таблиці (а ми вже навчилися виділяти за допомогою SELECT будь-яку підмножина реляційної таблиці), те залишилося лише з'ясувати, чи можна за допомогою SELECT одержати декартовий добуток. Для одержання декартова добутку декількох таблиць треба вказати у фразі FROM перелік таблиць, що перемножують, а у фразі SELECT - всі їхні стовпці.

Так, для одержання декартова добутку Вид_блюд і Трапези треба видати запит

SELECT Вид_блюд.*, Трапези.*

FROM Вид_блюд, Трапези;

Одержимо таблицю, що містить 5 х 3 = 15 рядків:

В

Вид

Т

Трапеза

З

Закуска

1

Сніданок

З

Закуска

2

Обід

З

Закуска

3

Вечеря

С

Суп

1

Сніданок

С

Суп

2

Обід

С

Суп

3

Вечеря

Г

Гаряче

1

Сніданок

Г

Гаряче

2

Обід

Г

Гаряче

3

Вечеря

Д

Десерт

1

Сніданок

Д

Десерт

2

Обід

Д

Десерт

3

Вечеря

Н

Напій

1

Сніданок

Н

Напій

2

Обід

Н

Напій

3

Вечеря

В іншому прикладі, де перемножуються таблиці Меню, Трапези, Вид_блюд, Блюда:

SELECT Меню.*, Трапези.*, Вид_блюд.*, Блюда.*

FROM Меню, Трапези, Вид_блюд, Блюда;

утвориться таблиця (рис 2.5), що містить 21 х 3 х 5 х 33 = 10395 рядків.

З перших 39 рядків цієї таблиці лише дві актуальних (відзначені "*"): у них збігаються номера блюд таблиць Меню й Блюда. В інших - повна нісенітниця: до закусок віднесені супи й напої, на сніданок пропонується незапланований суп і т.д.

Еквиз’єднання таблиць

Якщо з декартова добутку забрати непотрібні рядки й стовпці, то можна одержати актуальні таблиці, що відповідають кожному із з'єднань.

Меню

Трапези

Вид_блюд

Блюда

Т

В

БЛ

Т

Трапеза

В

Вид

БЛ

Блюдо

В

Основа

Вихід

Праця

1

З

3

1

Сніданок

З

Закуска

1

Салат літній

З

Овочі

200.

3

1

З

3

1

Сніданок

З

Закуска

2

Салат м'ясний

З

М'ясо

200.

4

1

З

3

1

Сніданок

З

Закуска

3

Салат вітамінний

З

Овочі

200.

4

. . .

1

З

3

1

Сніданок

З

Закуска

12

Суп молочний

С

Молоко

500.

3

1

З

3

1

Сніданок

З

Закуска

13

Бастурма

Г

М'ясо

300.

5

. . .

1

З

3

1

Сніданок

З

Закуска

32

Кава чорний

Н

Кава

100.

1

1

З

3

1

Сніданок

З

Закуска

33

Кава на молоці

Н

Кава

200.

2

1

З

6

1

Сніданок

З

Закуска

1

Салат літній

З

Овочі

200.

3

1

З

6

1

Сніданок

З

Закуска

2

Салат м'ясний

З

М'ясо

200.

4

1

З

6

1

Сніданок

З

Закуска

3

Салат вітамінний

З

Овочі

200.

4

1

З

6

1

Сніданок

З

Закуска

4

Салат рибний

З

Риба

200.

4

1

З

6

1

Сніданок

З

Закуска

5

Паштет з риби

З

Риба

120.

5

1

З

6

1

Сніданок

З

Закуска

6

М'ясо з гарніром

З

М'ясо

250.

3 *

. . .

Рис. 3.1. Ілюстрація декартового добутку

Очевидно, що відбір актуальних рядків забезпечується уведенням у запит WHERE фрази, у якій установлюється відповідність між:

  • кодами трапез (Т) у таблицях Меню й Трапези (Меню.Т = Трапези.Т),

  • кодами видів блюд (В) у таблицях Меню й Вид_блюд (Меню.В = Вид_блюд.В),

  • номерами блюд (БЛ) у таблицях Меню й Блюда (Меню.БЛ = Блюда.БЛ).

Такий скоректований запит

SELECT Меню.*, Трапези.*, Вид_блюд.*, Блюда.*

FROM Меню, Трапези, Вид_блюд, Блюда

WHERE Меню.Т = Трапези.Т

AND Меню.В = Вид_блюд.В

AND Меню.БЛ = Блюда.БЛ;

дозволить одержати еквіз’єднання таблиць Меню, Трапези, Вид_блюд і Блюда:

Т

БЛ

Т

Трапеза

В

Вид

БЛ

Блюдо

В

Основа

Вихід

Праця

1

З

3

1

Сніданок

З

Закуска

3

Салат вітамінний

З

Овочі

200.

4

1

З

6

1

Сніданок

З

Закуска

6

М'ясо з гарніром

З

М'ясо

250.

3

1

Г

19

1

Сніданок

Г

Гаряче

19

Омлет з луком

Г

Яйця

200.

5

. . .

3

Г

16

3

Вечеря

Г

Гаряче

16

Драчена

Г

Яйця

180.

4

3

Н

30

3

Вечеря

Н

Напій

30

Компот

Н

Фрукти

200.

2

3

Н

31

3

Вечеря

Н

Напій

31

Молочний напій

Н

Молоко

200.

2

Природне з'єднання таблиць

Легко помітити, що в еквизеднання таблиць увійшли дублікати стовпців, по яких проводилося з'єднання (Т, У и БЛ). Для виключення цих дублікатів можна створити природне з'єднання тих же таблиць:

SELECT Т, В, БЛ, Трапеза, Вид, Блюдо, Основа, Вихід, Праця

FROM Меню, Трапези, Вид_блюд, Блюда

WHERE Меню.Т = Трапези.Т

AND Меню.В = Вид_блюд.В

AND Меню.БЛ = Блюда.БЛ;

Реалізація природного з'єднання таблиць має вигляд

Т

В

БЛ

Трапеза

Вид

Блюдо

Основа

Вихід

Праця

1

З

3

Сніданок

Закуска

Салат вітамінний

Овочі

200.

4

1

З

6

Сніданок

Закуска

М'ясо з гарніром

М'ясо

250.

3

1

Г

19

Сніданок

Гаряче

Омлет з луком

Яйця

200.

5

...

3

Г

16

Вечеря

Гаряче

Драчена

Яйця

180.

4

3

Н

30

Вечеря

Напій

Компот

Фрукти

200.

2

3

Н

31

Вечеря

Напій

Молочний напій

Молоко

200.

2

Композиція таблиць

Для виключення всіх стовпців, по яких проводиться з'єднання таблиць, треба створити композицію

SELECT Трапеза, Вид, Блюдо, Основа, Вихід, Праця

FROM Меню, Трапези, Вид_блюд, Блюда

WHERE Меню.Т = Трапези.Т

AND Меню.В = Вид_блюд.В

AND Меню.БЛ = Блюда.БЛ;

Яка має вид

Трапеза

Блюдо

Вид

Основа

Вихід

Праця

Сніданок

Салат вітамінний

Закуска

Овочі

200.

4

Сніданок

М'ясо з гарніром

Закуска

М'ясо

250.

3

Сніданок

Омлет з луком

Гаряче

Яйця

200.

5

. . .

Вечеря

Драчена

Гаряче

Яйця

180.

4

Вечеря

Компот

Напій

Фрукти

200.

2

Вечеря

Молочний напій

Напій

Молоко

200.

2

Тета-з'єднання таблиць

У базі даних ПАНСІОН важко підібрати нескладний приклад, що ілюструє тета-з'єднання таблиць. Тому сконструюємо такий надуманий запит:

SELECT Вид_блюд.*, Трапези.*

FROM Вид_блюд, Трапези

WHERE Вид Трапеза;

що дозволяє вибрати з отриманого в декартова добутку таблиць Вид_блюд і Трапези лише ті рядки, у яких значення трапези "менше" (за алфавітом) значення виду блюда:

В

Вид

Т

Трапеза

З

Закуска

1

Сніданок

С

Суп

1

Сніданок

С

Суп

2

Обід

Н

Напій

1

Сніданок

З'єднання таблиць із додатковою умовою

При формуванні з'єднання створюється робоча таблиця, до якої застосовні всі операції: відбір потрібних рядків з'єднання (WHERE фраза), упорядкування одержуваного результату (ORDER BY фраза) і агрегатування даних (SQL-функції й GROUP BY фраза).

Наприклад, для одержання переліку блюд, пропонованих у меню на сніданок, можна сформувати запит на основі композиції:

SELECT Вид, Блюдо, Основа, Вихід, 'Номер -', БЛ

FROM Меню, Трапези, Вид_блюд, Блюда

WHERE Меню.Т = Трапези.Т

AND Меню.В = Вид_блюд.В

AND Меню.БЛ = Блюда.БЛ

AND Трапеза = ’Сніданок’;

Одержимо

Вид

Блюдо

Основа

Вихід

'Номер -'

БЛ

Закуска

Салат вітамінний

Овочі

200.

Номер -

3

Закуска

М'ясо з гарніром

М'ясо

250.

Номер -

6

Гаряче

Омлет з луком

Яйця

200.

Номер -

19

Гаряче

Пудинг рисовий

Крупу

160.

Номер -

21

Напій

Молочний напій

Молоко

200.

Номер -

31

Напій

Кава чорний

Кава

100.

Номер -

32

З'єднання таблиці зі своєю копією

У ряді додатків виникає необхідність одночасної обробки даних якої-небудь таблиці й однієї або декількох її копій, створюваних на час виконання запиту.

Наприклад, при створенні списків студентів (таблиця Студенти) можливий повторне уведення даних про якого-небудь студента із присвоєнням йому другого номера залікової книжки. Для виявлення таких помилок можна з'єднати таблицю Студенти з її тимчасовою копією, установивши в WHERE фразі рівність значень всіх однойменних стовпців цих таблиць крім стовпців з номером залікової книжки (для останніх треба встановити умову нерівності значень).

Тимчасову копію таблиці можна сформувати, указавши ім'я псевдоніма за ім'ям таблиці у фразі FROM. Так, за допомогою фрази

FROM Блюда X, Блюда Y, Блюда Z

будуть сформовані три копії таблиці Блюда з іменами X, Y й Z.

Як приклад з'єднання таблиці з нею самої сформуємо запит на висновок таких пар блюд таблиці Блюда, у яких збігається основа, а назва першого блюда пари менше (за алфавітом), чим номер другого блюда пари. Для цього можна створити запит з однією копією таблиці Блюда (Копія):

SELECT Блюдо, Копія.Блюдо, Основа

FROM Блюда, Блюда Копія

WHERE Основа = Копія.Основа

AND Блюдо < Копія.Блюдо;

або двома її копіями (Перша й Друга):

SELECT Перша.Блюдо, Друга.Блюдо, Основа

FROM Блюда Перша, Блюда Друга

WHERE Перша.Основа = Друга.Основа

AND Перша.Блюдо < Друга.Блюдо;

Одержимо результат виду

Перша.Блюдо

Друга.Блюдо

Основа

Морква з рисом

Помідори з луком

Овочі

Морква з рисом

Салат літній

Овочі

Морква з рисом

Салат вітамінний

Овочі

Помідори з луком

Салат вітамінний

Овочі

Помідори з луком

Салат літній

Овочі

Салат вітамінний

Салат літній

Овочі

Бастурма

Бефстроганов

М'ясо

Бастурма

М'ясо з гарніром

М'ясо

Бефстроганов

М'ясо з гарніром

М'ясо

Вкладені підзапити

Види вкладених підзапитів

Вкладений підзапит - це підзапит, ув'язнений у круглі дужки й вкладений в WHERE (HAVING) фразу пропозиції SELECT або інших пропозицій, що використають WHERE фразу. Вкладений підзапит може містити у своєї WHERE (HAVING) фразі інший вкладений підзапит і т.д. Неважко догадатися, що вкладений підзапит створено для того, щоб при відборі рядків таблиці, сформованої основним запитом, можна було використати дані з інших таблиць (наприклад, при відборі блюд для меню використати дані про наявність продуктів у коморі пансіонату).

Існують прості й корельовані вкладені підзапити. Вони включаються в WHERE (HAVING) фразу за допомогою умов IN, EXISTS або однієї з умов порівняння ( = | < | < | <= | | = ). Прості вкладені підзапити обробляються системою "знизу нагору". Першим обробляється вкладений підзапит самого нижнього рівня. Безліч значень, отримана в результаті його виконання, використається при реалізації підзапита більше високого рівня й т.д.

Запити з корельованими вкладеними підзапитами обробляються системою у зворотному порядку. Спочатку вибирається перший рядок робочої таблиці, сформованої основним запитом, і з неї вибираються значення тих стовпців, які використаються у вкладеному підзапиті (вкладених підзапитів). Якщо ці значення задовольняють умовам вкладеного підзапита, то обраний рядок включається в результат. Потім вибирається другий рядок і т.д., поки в результат не будуть включені всі рядки, що задовольняють вкладеному підзапиту (послідовності вкладених підзапитів).

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

Прості вкладені підзапити

Прості вкладені підзапити використаються для подання безлічі значень, дослідження яких повинне здійснюватися в якому-небудь предикаті IN, що ілюструється в наступному прикладі: видати назву й статус постачальників продукту з номером 11, тобто помідорів.

Результат:

SELECT Назва, Статус

FROM Постачальники

WHERE ПС IN

( SELECT ПС

FROM Поставки

WHERE ПР = 11 );

Назва

Статус

СИТНИЙ

ринок

УРОЖАЙ

коопторг

ЛІТО

агрофірма

КОРЮШКА

кооператив

Як ми вже відзначали при обробці повного запиту система виконує насамперед вкладений підзапит. Цей підзапит видає безліч номерів постачальників, які поставляють продукт із кодом ПР = 11, а саме безліч (1, 5, 6, 8). Тому первісний запит еквівалентний такому простому запиту:

SELECT Назва, Статус

FROM Постачальники

WHERE ПС IN (1, 5, 6, 8);

Підзапит із декількома рівнями вкладеності можна проілюструвати на тім же прикладі. Нехай потрібно довідатися не постачальників продукту 11, як це робилося в попередніх запитах, а постачальників помідорів, що є продуктом з номером 11. Для цього можна дати запит

SELECT Назва, Статус

FROM Постачальники

WHERE ПС IN

( SELECT ПС

FROM Поставки

WHERE ПР IN

( SELECT ПР

FROM Продукти

WHERE Продукт = 'Помідори' ));

У цьому випадку результатом самого внутрішнього підзапита є тільки одне значення (11). Як уже було показано вище, підзапит наступного рівня у свою чергу дає в результаті безліч (1, 5, 6, 8). Останній, самий зовнішній SELECT, обчислює наведений вище остаточний результат. Взагалі допускається будь-яка глибина вкладеності підзапитів.

Той же результат можна одержати за допомогою з'єднання

SELECT Назва, Статус

FROM Постачальники, Поставки, Продукти

WHERE Постачальники.ПС = Поставки.ПС

AND Поставки.ПР = Продукти.ПР

AND Продукт = 'Помідори';

При виконанні цього компактного запиту система повинна одночасно обробляти дані із трьох таблиць, тоді як у попередньому прикладі ці таблиці обробляються по черзі. Природно, що для їхньої реалізації потрібні різні ресурси пам'яті й часу, однак цього неможливо відчути при роботі з обмеженим обсягом даних в ілюстративній базі ПАНСІОН.

Використання однієї й тієї ж таблиці в зовнішньому й вкладеному підзапиті

Видати номера постачальників, які поставляють хоча б один продукт, що поставляє постачальником 6.

Результат:

SELECT DISTINCT ПС

FROM Поставки

WHERE ПР IN

( SELECT ПР

FROM Поставки

WHERE ПС = 6);

ПС

1

3

5

6

8

Відзначимо, що посилання на Поставки у вкладеному підзапиті означає не те ж саме, що посилання на Поставки в зовнішньому запиті. У дійсності, два імені Поставки позначають різні значення. Щоб цей факт став явним, корисно використати псевдоніми, наприклад, X й Y:

SELECT DISTINCT X.ПС

FROM Поставки X

WHERE X.ПР IN

( SELECT Y.ПР

FROM Поставки Y

WHERE Y.ПС = 6 );

Тут X й Y - довільні псевдоніми таблиці Поставки, обумовлені у фразі FROM і використовувані як явні уточнювачі у фразах SELECT й WHERE. Нагадаємо, що псевдоніми визначені лише в межах одного запиту.

Вкладений підзапит із оператором порівняння, відмінним від IN

Видати номера постачальників, що перебувають у тім же місті, що й постачальник з номером 6.

Результат:

SELECT ПС

FROM Постачальники

WHERE Місто =

( SELECT Місто

FROM Постачальники

WHERE ПС = 6 );

ПС

1

4

6

У подібних запитах можна використати й інші оператори порівняння (<, <=, <, = або ), однак, якщо вкладений підзапит повертає більше одного значення й не використається оператор IN, буде виникати помилка.

Корельовані вкладені підзапити

Видати назва й статус постачальників продукту з номером 11.

SELECT Назва, Статус

FROM Постачальники

WHERE 11 IN

( SELECT ПР

FROM Поставки

WHERE ПС = Постачальники.ПС );

Такий підзапит відрізняється від розглянутого тим, що вкладений підзапит не може бути оброблений перш, ніж буде оброблятися зовнішній підзапит. Це пов'язане з тим, що вкладений підзапит залежить від значення Постачальники.ПС а воно змінюється в міру того, як система перевіряє різні рядки таблиці Постачальники. Отже, з концептуальної точки зору обробка здійснюється в такий спосіб:

  1. Система перевіряє перший рядок таблиці Постачальники. Припустимо, що це рядок постачальника з номером 1. Тоді значення Постачальники.ПС буде в цей момент має значення, рівне 1, і система обробляє внутрішній запит

( SELECT ПР

FROM Поставки

WHERE ПС = 1 );

одержуючи в результаті безліч (9, 11, 12, 15). Тепер система може завершити обробку для постачальника з номером 1. Вибірка значень Назва й Статус для ПС=1 (СИТНИЙ і ринок) буде проведена тоді й тільки тоді, коли ПР=11 буде належати цій безлічі, що, мабуть, справедливо.

  1. Далі система буде повторювати обробку такого роду для наступного постачальника й т.д. доти, поки не будуть розглянуті весь рядки таблиці Постачальники.

Подібні підзапити називаються коррелированными, тому що їхній результат залежить від значень, певних у зовнішньому підзапиті. Обробка коррелированного підзапита, отже, повинна повторюватися для кожного значення витягає із зовнішнього підзапита, а не виконуватися раз і назавжди.

Розглянемо приклад використання однієї й тієї ж таблиці в зовнішньому підзапиті й коррелированном вкладеному підзапиті.

Видати номера всіх продуктів, що поставляють тільки одним поставщиком.

Результат:

SELECT DISTINCT X.ПР

FROM Поставки X

WHERE X.ПР NOT IN

( SELECT Y.ПР

FROM Поставки Y

WHERE Y.ПС <> X.ПС );

X.ПР

17

Дія цього запиту можна пояснити в такий спосіб: "По черзі для кожного рядка таблиці Поставки, скажемо X, виділити значення номера продукту (ПР), якщо й тільки якщо це значення не входить у деякий рядок, скажемо, Y, тієї ж таблиці, а значення стовпця номер постачальника (ПС) у рядку Y не дорівнює його значенню в рядку X".

Відзначимо, що в цьому формулюванні повинен бути використаний принаймні один псевдонім - або X, або Y.

Запити, що використовують EXISTS

Квантор EXISTS (існує) - поняття, запозичене з формальної логіки. У мові SQL предикат із квантором існування представляється вираженням EXISTS (SELECT * FROM ...).

Таке вираження вважається щирим тільки тоді, коли результат обчислення "SELECT * FROM ..." є непустою безліччю, тобто коли існує який-небудь запис у таблиці, зазначеної у фразі FROM підзапита, що задовольняє умові WHERE підзапита. (Практично цей підзапит завжди буде коррелированным безліччю.)

Розглянемо приклади. Видати назви постачальників, що поставляють продукт із номером 11.

Результат:

SELECT Назва

FROM Постачальники

WHERE EXISTS

( SELECT *

FROM Поставки

WHERE ПС= Постачальники.ПС

AND ПР = 11 );

Назва

СИТНИЙ

УРОЖАЙ

КОРЮШКА

ЛІТО

Система послідовно вибирає рядки таблиці Постачальники, виділяє з них значення стовпців Назва й ПС, а потім перевіряє, чи є вірною умова існування, тобто існуе в таблиці Поставки хоча б один рядок зі значенням ПР=11 і значенням ПС, рівним значенню ПС, обраному з таблиці Постачальники. Якщо умова виконується, то отримане значення стовпця Назва включається в результат.

Припустимо, що перші значення полів Назва й ПС рівні, відповідно, 'СИТНИЙ' й 1. Тому що в таблиці Поставки є рядок із ПР=11 і ПС=1, то значення 'СИТНИЙ' повинне бути включене в результат.

Хоча цей перший приклад тільки показує інший спосіб формулювання запиту для завдання, розв'язуваної й іншими шляхами (за допомогою оператора IN або з'єднання), EXISTS являє собою одну з найбільш важливих можливостей SQL. Фактично будь-який запит, що виражається через IN, може бути альтернативним образом сформульований також за допомогою EXISTS. Однак зворотне висловлення несправедливо.

Видати Назва й Статус постачальників, що не поставляють продукт із номером 11.

Результат:

SELECT Назва, Статус

FROM Постачальники

WHERE NOT EXISTS

( SELECT *

FROM Поставки

WHERE ПС = Постачальники.ПС

AND ПР = 11 );

Назва

Статус

ПОРТОС

кооператив

ШУШАРЫ

радгосп

ТУЛЬСЬКИЙ

універсам

ОГУРЕЧИК

ферма

Функції в підзапиті

Тепер, після знайомства з різними формулюваннями вкладених підзапитів і псевдонімами легше зрозуміти текст й алгоритм реалізації запиту на одержання тих постачальників продуктів для Сирників, які поставляють ці продукти за мінімальну ціну:

SELECT Продукт, Ціна, Назва, Статус

FROM Продукти, Склад, Блюда, Поставки, Постачальники

WHERE Продукти.ПР = Склад.ПР

AND Склад.БЛ = Блюда.БЛ

AND Поставки.ПР = Склад.ПР

AND Поставки.ПС = Постачальники.ПС

AND Блюдо = 'Сирники'

AND Ціна = ( SELECT MIN(Ціна)

FROM Поставки X

WHERE X.ПР = Поставки.ПР );

Природно, що це корельований підзапит: тут спочатку визначається мінімальна ціна продукту, що входить до складу Сирників, і тільки потім з'ясовується його постачальник.

Об'єднання (UNION)

Реляційна операція "Об'єднання", що дозволяє одержати відношення, що складається із всіх рядків, що входять в одне або обоє поєднуваних відносини. Але при цьому вихідні відносини або їхні поєднувані проекції повинні бути сумісними по об'єднанню. Для SQL це означає, що дві таблиці можна поєднувати тоді й тільки тоді, коли:

  1. вони мають однакове число стовпців, наприклад, m;

  2. для всіх i (i = 1, 2, ..., m) i-й стовпець першої таблиці й i-й стовпець другої таблиці мають у точності однаковий тип даних.

Наприклад, видати назви продуктів, у яких немає жирів, або вхідних до складу блюда з кодом БЛ = 1:

 

Результат:

Продукт

SELECT Продукт

FROM Продукти

WHERE Жири = 0

UNION

SELECT Продукт

FROM Соста

WHERE БЛ = 1

Майонез

Лук

Помідори

Зелень

Яблука

Цукор

Із цього простого приклада видно, що надлишкові дублікати завжди виключаються з результату UNION. Тому, хоча в розглянутому прикладі Помідори, Зелень й Яблука вибираються обома із двох складові пропозиції SELECT, в остаточному результаті вони з'являються тільки один раз.

Пропозицією з UNION можна об'єднати будь-яке число таблиць (проекцій таблиць). Так, до попереднього запиту можна додати (перед крапкою з коми) конструкцію

UNION

SELECT Продукт

FROM Продукти

WHERE Ca < 250

що дозволяє додати до списку продуктів Масло, Рис, Борошно й Кава. Однак той же результат можна одержати простою зміною фрази WHERE першої частини вихідного запиту

WHERE Жири = 0 OR Ca <250

Реалізація операцій реляційної алгебри пропозицією SELECT

За допомогою пропозиції SELECT можна реалізувати будь-яку операцію реляційної алгебри.

Селекція (горизонтальна підмножина) таблиці створюється з тих її рядків, які задовольняють заданим умовам. Приклад:

SELECT *

FROM Блюда

WHER Основа = 'Молоко'

AND Вихід 200;

Проекція (вертикальна підмножина) таблиці створюється із зазначених її стовпців (у заданому порядку) з наступним виключенням надлишкових дублікатів рядків. Приклад:

SELECT DISTINCT Блюдо, Вихід, Основа

FROM Блюда;

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

SELECT Блюдо, Основа, Вихід

FROM Блюда

WHER Основа = 'Овочі'

UNION

SELECT Блюдо, Основа, Вихід

FROM Блюда

WHER В = 'Г';

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

SELECT БЛ

FROM Склад

WHERE БЛ IN

( SELECT БЛ

FROM Меню);

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

SELECT БЛ

FROM Склад

WHERE БЛ NOT IN

( SELECT БЛ

FROM Меню);

Декартово добуток таблиць і різні види з'єднань були докладно розглянуті раніше.

Соседние файлы в папке Учебное пособие