You are on page 1of 92

1

SQL
2

Зміст
Зміст 2
ТЕМА 1: Введення у базу даних 4
Реляційна модель БД 5
Сутність Бази Даних 6
Види зв’язків у базі даних 9
Процедурні розширення SQL 12
Категорії типів даних 12
Типи даних в SQL 14
ТЕМА 2: Вибірка та модифікація даних 16
Вибірка даних з таблиці SELECT 16
Псевдоніми Aliases 19
Вставка строки в таблицю INSERT 23
Редагування даних таблиці UPDATE 26
Видалення даних з таблиці DELETE 28
Функції FUNCTION 31
Збережені процедури PROCEDURE 34
ТЕМА 3: Обмеження та фільтрація даних за допомогою предикатів 36
Вибірка даних з використанням фільтрації 37
Оператори, які використовуються з конструкцією WHERE 38
Оператор логічного “І” AND 39
Оператор логічного “Або” OR 40
Оператор логічного “Не” NOT 41
Оператор BETWEEN 42
Оператор IN 43
Оператор LIKE 45
Вибірка унікальних даних DISTINCT 50
Сортування даних таблиці ORDER BY 51
Вибірка певної кількості записів TOP 53
Об’єднання запитів оператором UNION [ALL] 55
Різниця між UNION та UNION ALL 58
Агрегатні функції 60
Опис і робота з агрегатними функціями 60
ТЕМА 4: Створення об’єктів баз даних 65
3

Створення бази даних 65


Створення таблиці CREATE TABLE 66
Зміна назви колонки 67
Зміна типу даних колонки 68
Додавання колонки у таблицю 69
Видалення колонки з таблиці 70
Видалення таблиці DROP TABLE 71
Створення представлення CREATE VIEW 71
Видалення представлення DROP VIEW 72
Обмеження CONSTRAINT 73
Обмеження первинного ключа (PRIMARY KEY) 73
Обмеження зовнішнього ключа (FOREIGN KEY) 74
Обмеження унікального ключа (UNIQUE) 75
Обмеження на значення (NOT NULL) 75
Перевірочне обмеження (CHECK) 75
Правила найменувань для CONSTRAINT's. 76
Додавання у існуючу таблицю первинного ключа (PRIMARY KEY) 77
Додавання зовнішнього ключа у існуючу таблицю (FOREIGN KEY) 77
Додавання унікального ключа у існуючу таблицю (UNIQUE) 78
Додавання перевірочного ключа у існуючу таблицю (CHECK) 79
Видалення CONSTRAINT 80
ТЕМА 5: Поєднання таблиць. Складні вибірки даних з таблиць 81
Поєднання таблиць JOIN 81
Загальний синтаксис для поєднань 82
Об’єднання по INNER JOIN 83
Об’єднання по LEFT JOIN 84
Об’єднання по RIGHT JOIN 85
Об’єднання FULL JOIN 86
Об’єднання CROSS JOIN 87
Складні вибірки даних з таблиць 89
Підзапит у секції SELECT 89
Підзапит в секції FROM 90
Підзапит в секції WHERE 91
4

ТЕМА 1: Введення у базу даних


База даних – це сукупність пов’язаних даних, організованих за окремими правилами.
Ці правила передбачають загальні принципи опису, маніпулювання та зберігання даних.

Для керування базами даних створені програмні продукти, які являють собою програмне
забезпечення баз даних.
Вони називаються СУБД – система управління базою даних.
Під керуванням мається на увазі можливість індивідуального та колективного доступу до БД,
додавання, редагування, сортування інформації, часткове або повне копіювання інформації,
об’єднання двох або більше баз даних.

СУБД – комплекс мовних та програмних засобів призначених для створення, ведення та


спільного використання баз даних багатьма користувачами.
СУБД MS SQL працює тільки з реляційними базами даних.
Реляційні бази даних більш прості для первинного вивчення. Окрім того, вони
використовуються на всіх хостингах та серверах для масового використання.
У даному курсі буде розглянута СУБД від компанії Microsoft: MS SQL Server.
5

Реляційна модель БД

Реляційна база даних – це сукупність пов’язаних даних, які зберігаються у двомірних


таблицях. Термін «реляційний» означає, що теорія заснована на математичному розумінні
поняття відношення (relation).
Саме дана модель частіш за все використовується при розробці програмних продуктів.

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

Сутність Бази Даних


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

Атрибути сутностей БД, визначення атрибуту, вибір набору атрибутів

Записи про певні параметри кожної з сутностей називаються атрибутами. Наприклад, для
сутності “замовник” буде зберігатися інформація про його найменування, представників,
адреси і т д.
Вибір потрібного комплекту атрибутів - одна з основних проблем при проектуванні баз даних.
Досить часто у реальній БД потрібний комплект атрибутів у підсумку не зберігається - просто
тому, що користувачі не змогли сповістити у процесі збору інформації, що він дійсно потрібен.
Іноді у базу навпаки потрапляють зайві атрибути, заповнення яких потребує додаткового часу.
Досить часто виникає проблема з форматом введених даних, наприклад, на які частини ділити
адресу та що робити з нестандартними випадками.
Загальне правило при виборі набору атрибутів - треба починати з результату та намагатися
спрощувати модель. Перше на що треба відповісти - на які питання користувачів повинна
відповідати ваша база даних?
Далі потрібно забезпечити гнучкість системи. Потреби користувачів можуть змінюватися, їм
буде необхідна додаткова функціональність, виникнуть виключення і т д. Зазвичай,
досягнення більшої гнучкості робиться за рахунок ускладнення БД (та системи вводу
інформації). Але, чим складніша система, тим важче з нею працювати користувачам.
7

● База Даних
Реляційна база даних містить у собі набір таблиць пов’язаних (але не всіх і не завжди)
між собою.
Вид збереження даних визначається заданою структурою (схемою) БД та правилами її
керування.
8

● Таблиця
Таблиця зберігає дані та складається з двох частин:
o Поле – вертикальний стовпчик, котрий так само називається колонкою або
атрибутом таблиці.
o Запис – горизонтальний рядок.

Основні властивості полів таблиці:


Ім’я – кожне поле має своє ім’я і воно повинно бути унікальним в рамках
таблиці.
Тип даних - для кожного поля повинен бути визначений тип даних, які можуть
бути записані у цьому полі: текст, число, дата, час, валюта і т д.
Допустимість відсутності даних - обов’язковість введення даних в поле чи ні.

Окрім таблиць у БД є безліч інших об’єктів, наприклад: пакети, лічильники, функції,


процедури, тригери, індекси тощо.
Але таблиця - це основний об’єкт БД, котрий зберігає інформацію.
9

Види зв’язків у базі даних


Зв’язок (relationship) – це асоціація, яка встановлюється між двома або більше таблицями.
Зв’язок може існувати як між двома різними таблицями, так і між однією самою таблицею
(рекурсивний зв’язок).

Види зв’язків:

● один до одного;
● один до багатьох/багато до одного;
● багато до багатьох.

Зв’язок таблиць на прикладі учбового курсу:


10

Зв’язок таблиць на прикладі клієнтів та платежів:


11

Приклад зв’язку один до одного:

Людина має одну дату народження/одну стать.

Людина Стать/Дата народження

Приклад зв’язку один до багатьох/багато до одного:

Людина має кілька тваринок але тваринка має лише одного власника.

котик Мурчик
Людина
песик Тузик

Приклад зв’язку багато до багатьох:

Студент 1 ходить на курси SQL та JavaScript. Але на курс SQL також ходять Студент 2 та Студент
3, а на JavaScript ходить ще й Студент 3.
Тобто існує різноманітна варіативність комбінацій.

Курс SQL
Студент 1
Пов’язуюча таблиця Курс JavaScript
Студент 2
Студент ID
Курс ID Курс Тестування
Студент 3
Курс Unix
12

Structured Query Language (SQL) – Інформаційно-логічна мова призначена для опису, зміни та
вилучення даних, які зберігаються у реляційних базах даних.

Основний перелік операцій SQL:


● Створення нових таблиць в БД
● Зміна структур таблиць
● Видалення таблиць
● Додавання нових записів у таблицю
● Редагування записів
● Видалення записів
● Вибірка записів з однієї або кількох таблиць у відповідності з заданими умовами

Процедурні розширення SQL


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

● Microsoft SQL Server – T-SQL


● Oracle Database – PL/SQL
● Firebird – PSQL
● PostgreSQL — PL/pgSQL

Категорії типів даних

Типи даних в SQL Server об’єднані у наступні категорії:

● Точні числа
● Приблизні числа (числа з плаваючою крапкою)
● Дата та час
● Символьні строки
● Символьні строки у Юнікод
● Двійкові дані
● Інші типи даних
13

Точні числа: Дата та час:


bigint data
bit datetime2
decimal datetime
int datetimeoffset
money smalldatetime
numeric time
smallint
smallmoney
tinyint
Приблизні числа: Символьні строки:
float char
real text
varchar
Символьні строки в Юнікоді: Двійкові дані:
nchar binary
ntext image
nvarchar varbinary
Інші типи даних:
cursor
hierarchyid
sql_variant
table
timestamp
uniqueidentifier
xml
14

Типи даних в SQL


MS SQL Server підтримує усі основні типи даних, що використовуються у сучасних мовах
програмування. Типи даних в MS SQL Server можна розділити на п’ять основних категорій.

Розмір
Тип Діапазон значень
(байт)
Цілі числа
BIT 1 байт Насправді перший біт у таблиці буде займати один
байт, однак, наступні сім бітів у цій таблиці будуть
зберігатися у тому ж байті
TINYINT 1 байт (0;255)
SMALLINT 2 байти ( –32768; +32767)
INT 4 байти ( –2 147 483 648;+2 147 483 647)
BIGINT 8 байт ( –263 ; +263 –1)
Числа з плаваючою комою
FLOAT (n) Залежить
від (–1,79E +308 ; +1,79E +308)
значення n Залежить від значення n

DECIMAL(p, s) Залежить
от -1038+1 до 1038-1
NUMERIC(p, s) від
значення p Залежить від параметру p
SMALLMONEY 4 байти от -214 748.3648 до 214 748.3647
MONEY 8 байт от -263 до 263-1
Дата та час
DATETIME 8 байтів Діапазон значень від 1 січня 1753 року до 31
грудня 9999 року з точністю до трьох сотих
секунди.
DATETIME2 6-8 байтів Новий тип даних, підтримує точність до 0,1 мс.
SMALLDATETIME 4 байти Діапазон значень від 1 січня 1900 року до 6 червня
2079 року з точність до й хвилини.
DATETIMEOFFSET 8-10 Аналогічно типу DateTime, але зберігає ще зсув
байтів відносно часу UTC i .
DATE 3 байти Зберігає лише дату. Діапазон значень від 1 січня
0001 року до 31 грудня 9999 року
TIME 3-5 байтів Зберігає лише час з точністю до 0,1 мс
Текстові дані
CHAR(n) n байт Рядок фіксованої довжини. Максимальна довжина
строки 8000 символів. Довжина резервується
одразу.
15

Залежить від значення n.


VARCHAR(n) до 231 Рядок приблизної довжини. Максимальна
байт довжина строки 8000, але за використання
ключового слова «max» може зберігати до 231
символів.
Залежить від значення n.
(n + 2) байта
NCHAR(n) (2 * n) Рядок фіксованої довжини в Юнікоді. Максимальна
байт довжина строки 4000 символів. Довжина
резервується одразу.
Залежить від значення n.
NVARCHAR(n) до 2 31 Рядок приблизної довжини в Юнікоді.
байт Максимальна довжина строки 4000 символів, але
за використання ключового слова «max» може
зберігати до 231 символів.
Залежить від значення n.
NCHAR та NVARCHAR зазвичай працює так само як CHAR та VARCHAR.
Єдина відмінність між ними у тому, що NCHAR/NVARCHAR зберігає символи Unicode
(необхідно, якщо вам треба використовувати розширені набори символів), а VARCHAR - ні.
Оскільки символи Unicode потребує більшого об’єму пам’яті, поля NCHAR/NVARCHAR
займають у два рази більше місця.
Двійкові дані
BINARY(n) n байт Дозволяє зберігати двійкові дані розміром до 8000
байт
VARBINARY(n) до 231 Тип даних приблизної довжини, дозволяє зберігати
байт до 8000 байт, але при використанні ключового
слова «max» до 231 байт. (varbinary(max))
16

ТЕМА 2: Вибірка та модифікація даних


В даній темі ми розглянемо основні оператори підмови DML: SELECT, INSERT, UPDATE та
DELETE. Завдяки цим операторам з таблиць вибирається інформація, вставляється, редагується
та видаляється. При цьому самі таблиці як об’єкт, залишаються у базовій структурі. Оператори
працюють тільки з інформацією у таблицях.
Також розглянемо поняття Aliases і такі об’єкти як функція та процедура.
Інформація корисна та постійно використовується на практиці.

SELECT
у

Вибірка даних з таблиці SELECT


Для того, щоб подивитися що зберігається у таблиці, зробимо вибірку усіх даних з таблиці
Clients.
Вибірка даних виконується запитом такої конструкції:

SELECT column_name, column_name


FROM table_name;

де,
SELECT– ключове слово відповідає за вибірку даних
FROM – ключове слово, котре вказує на певну таблицю, з котрої буде відбуватися вибірка
даних
column_name – ім’я колонки (поля), інформація якої буде виведена у вибірці
table_name – ім’я таблиці, з якої вибираємо дані
17

Приклад 1
Запит

SELECT c.ClientName, c.Country


FROM Clients AS c

Результат

Опис

Запит повертає всі записи з таблиці Clients, але виводяться на екран лише поля ClientName
та Country.

Приклад 2
Запит

SELECT *
FROM Clients AS c

Результат

Опис

Запит повертає усі строки з таблиці Clients і виводяться усі колонки завдяки символу: *
18

Примітка:

Шлях до поля може представлятися також наступним чином:

SELECT Clients.ClientName, Clients.Country


FROM Clients

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

SELECT ServerName.DatabaseName.SchemaName.TableName.ColumnName
FROM ServerName.DatabaseName.SchemaName.TableName

де,
ServerName– ім’я сервера
DatabaseName - ім’я бази даних
SchemaName – ім’я схеми
TableName – ім’я таблиці
ColumnName – ім’я поля

Аналогічний запит
Запит

SELECT TrainingCenter.DBO.Clients.ClientName,
TrainingCenter.DBO.Clients.Country
FROM TrainingCenter.DBO.Clients

Результат

Опис

Запит повертає усі записи з таблиці Clients, але виводяться на екран лише поля ClientName
та Country.
19

Можна зробити висновок, що SQL Server сам визначає шлях до поля, якщо ми наприклад,
вказуємо тільки ім’я колонки без вказання повного шляху.
Аля, як бачимо, простіш за все використовувати аліаси/псевдоніми для таблиць/полів (Aliases).
Вони значно спрощують й скорочують написання запитів до БД, а також код стає
зрозумілішим. Аліаси використовуються ПОСТІЙНО, тому потрібно навчитись їх
використовувати, це досить просто.
Також існують випадки, коли без аліасів запит працювати не буде.

AS

Псевдоніми Aliases

Aliases - коротке та зручне для використання ім’я, що використовується замість більш


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

Псевдонім полю реалізується наступним запитом:

SELECT column_name AS alias_name


FROM table_name

Псевдонім таблиці реалізується наступним чином:

SELECT column_name
FROM table_name AS alias_name

де,
SELECT– ключове слово відповідає за вибірку даних
FROM – ключове слово, котре вказує на певну таблицю з котрої буде робитися вибірка даних
column_name – ім’я колонки (поля)
table_name – ім’я таблиці
AS – ключове слово після котрого вказується псевдонім
alias_name– псевдонім
20

Приклад 1
Запит

SELECT ClientName AS Name, Country AS CodeCountry


FROM Clients

Результат

Опис

У даному прикладі полю ClientName присвоєно нове ім’я: Name, а полю Country, присвоєно
нове ім’я CodeCountry.
Але слід розуміти, що присвоєння імені тимчасове, тільки на період виводу інформації на
екран.
Фізично, в БД поле у вказаній таблиці має первинну назву - ClientName и Country.
21

Приклад 2
Запит

SELECT c.ClientName, c.Country


FROM Clients AS c

Результат

Опис

В даному прикладі таблиці Clients привласнений аліас c.


Якщо таблиці привласнюється аліас, то в блоці SELECT необхідно використовувати той же
аліас для відображення колонок, якщо необхідно відобразити на все, а лише деякі колонки.
Як правило, аліас повинен бути коротким та своїм ім’ям асоціюється з назвою самої таблиці.
Наприклад: Clients AS c чи Clients AS cln значно краще асоціюється з таблицею Clients, ніж
якщо написати Clients AS ndtw

Для чого ще потрібен та корисний Aliases, особливо для таблиць?


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

Запит з використанням Aliases:


SELECT *
FROM Clients c
INNER JOIN Phones p ON c.ClientId = p.ClientId
INNER JOIN Adress a ON c.ClientId = a.ClientId
22

Запит без використання Aliases:

SELECT *
FROM Clients
INNER JOIN Phones ON Clients.ClientId = Phones.ClientId
INNER JOIN Adress ON Clients.ClientId = Adress.ClientId

Як бачимо, без аліасу нам треба перед зв’язуванням полів вказувати імена таблиць з котрих
використовуються поля для пов’язки.

Результат для обох прикладів буде однаковий:

Який є ризик якщо не використовувати Aliases?


В усіх таблицях Clients, Phones і Adress, є однакове за назвою поле ClientId, але це поле
унікальне в кожній з таблиць.
Доки ми не зв’язуємо таблиці з однаковими полями проблем не буде. Але якщо написати
запит в якому виконується зв’язування таблиць за однаковими полями без аліасів - виникне
помилка.

Приклад запиту без аліасів при зв’язуванні таблиць за однаковими полями:

SELECT *
FROM Clients
INNER JOIN Phones ON ClientId = ClientId
INNER JOIN Adress ON ClientId = ClientId

Отримуємо помилку: Ambiguous column name 'ClientId'. (Неоднозначна назва


колонки).
Ця помилка каже про те що СУБД не розуміє з якої саме таблиці необхідно пов’язувати поле
ClientId та ClientId, так як це поле є у всіх цих таблицях.

Таким чином робимо висновок: Aliases це зручно. Він спрощує читабельність коду, скорочує
довжину коду, уніфікує поля таблиць. Їх потрібно використовувати у своїх запитах.
Вказані приклади з поєднанням таблиць описані в ТЕМА 5: Об’єднання таблиць. Складні
вибірки даних з таблиць.
23

INSERT INTO

Вставка рядків в таблицю INSERT


Зараз ми навчимося заповнювати таблицю інформацією.
Вставка даних у таблицю здійснюється наступним запитом:

Перший варіант без визначення імен полів:

INSERT INTO table_name


VALUES (value1,value2,value3,...)

Використовується коли заповнюються абсолютно всі поля таблиці.


В цьому випадку не обов’язково після імені таблиці перераховувати поля даної таблиці, в які
будуть вставлятися значення.

Другий варіант із зазначенням імен полів:

INSERT INTO table_name(column1,column2,column3,...)


VALUES (value1,value2,value3,...)

Використовується коли заповнюються вибіркові поля таблиці.


У цьому випадку потрібно перерахувати поля в які будуть вставлятися відповідні значення.
Необхідно розуміти, що йде пряма відповідність поля і значення, а саме, якщо першим
елементом вказана колонка column1, то в неї будуть вставлятися відповідні значення value1,
в column2 буде вставлено значення value2, і т д.

де,
INSERT– ключове слово відповідає за вставку даних
INTO – ключове слово, котре вказує куди буде виконуватись вставка запису
table_name – ім’я таблиці, в яку вставляється запис
column – ім’я колонки (поля)
value – значення, котрим буде заповнюватися новий рядок таблиці для певної колонки
VALUES – ключове слово після якого перераховуються всі значення для нового рядка (значення
перераховуються через кому)
24

Приклад 1
Запит

Вставляємо записи:

INSERT INTO Clients


VALUES ('СПД Гаврюша','804'),
('АТ "Нико холдинг"','804'),
('Сарнавський Антон Петрович','642'),
('SP "Limited"','300'),
('Сачова Татьяна Петровна','376'),
('Горыныч Змей Павлович','643')

Перевіряємо результат:

SELECT *
FROM Clients

Результат

Опис

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

Приклад 2
Запит

Вставляємо записи:

INSERT INTO Adress (ClientId, Adress)


SELECT 1, 'Киев, Прорезная 6А'
UNION ALL
25

SELECT 2, 'Житомир, Михайловская 1'


UNION ALL
SELECT 3, 'Киев, Проспект Победы 65'
UNION ALL
SELECT 9, 'Днепропетровск, Проспект Свободи 5'
UNION ALL
SELECT 10, 'Винниця, Киевська 12'

Перевіряємо результат:

SELECT *
FROM Adress

Результат

Опис

Варіант вставки нових даних у таблицю за допомогою оператора SELECT та UNION ALL.
Запит типу SELECT 1, 'Киев, Прорезная 6А' створює вигляд таблиці з даними, але це лише
віртуальна таблиця де роздільник – кома, ділить значення на колонки.
Далі значення цих колонок відповідно вставляються в поля таблиці Adress.
Оператор UNION ALL по горизонталі об’єднує дані усіх рядків інших SELECT і далі цей масив
даних вставляється у таблицю Adress.
В цьому прикладі після Adress у лапках перераховані поля (ClientId, Adress) в котрі виконується
вставка.
Через SELECT можна використовувати реальний запит з таблиці, котрий вставить значення у
таблицю.
Дані текстового типу вказують в одинарних лапках. Цілі числа та з плаваючою крапкою
пишуться без лапок.
26

UPDATE

Редагування даних в таблиці UPDATE


Редагування даних в таблиці здійснюється запитом наступного типу:

UPDATE table_name
SET column1=value1, column2=value2,...
WHERE some_column=some_value

де,
UPDATE– ключове слово відповідає за редагування/оновлення існуючих даних в таблиці
SET – ключове слово після якого буде вказано в якому/яких полях буде змінюватися значення
column1=value1, column2=value2,…
WHERE – ключове слово, за допомогою якого виконується фільтрація даних
column, some_column – ім’я колонки (поля)
table_name – ім’я таблиці
value, some_value – нове значення, на яке буде змінено попереднє значення

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

SELECT *
FROM Clients
WHERE ClientName = 'Горыныч Змей Павлович'

2. Виконуємо зміну коду країни

UPDATE Clients
SET Country = '804'
WHERE Country = '643'
AND ClientName = 'Горыныч Змей Павлович'
27

3. Перевіряємо зміни даних виконавши запит з п.1.

Результат

Опис

У даному випадку ми обираємо клієнта ClientName = 'Горыныч Змей Павлович', у якого


код країни дорівнював 643.
За допомогою оператора UPDATE виконали зміну коду країни на 804.
При цьому використовували оператор фільтрації WHERE, для того щоб виконати зміну лише
одному клієнту. Якщо не виконати додаткову фільтрацію то всі клієнти таблиці Clients
отримають код країни - 804.
Тому перед виконанням UPDATE завжди треба виконати SELECT з потрібними фільтрами, котрі
відберуть необхідний масив даних.
Після, коли впевнилися, що це саме той масив якому треба виконати зміни, можна починати
писати UPDATE з тими ж умовами фільтрації.
Якщо потрібно виконати зміни інформації по кільком полям, то у поточному запиті після SET
Country = '804', через кому можна писати інші поля й вказувати їм нові значення
28

DELETE

Видалення даних з таблиці DELETE


Видалення даних з таблиці здійснюється наступним запитом:
DELETE
FROM table_name
WHERE column_name = value

де,
DELETE – ключове слово відповідальне за видалення даних з таблиці
FROM – ключове слово, яке вказую на певну таблицю з якої буде видалення
column_name – ім’я колонки (поля)
table_name – ім’я таблиці
WHERE – ключове слово, за допомогою якого робиться фільтрація даних
value – значення, яке використовуєть для фільтрації
29

Приклад
Запит

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

SELECT *
FROM Clients
WHERE ClientName = 'Сачова Татьяна Петровна'

2. Виконуємо видалення запису

DELETE
FROM Clients
WHERE ClientName = 'Сачова Татьяна Петровна'

3. Перевіряємо зміну даних виконавши запит з п.1


Так як даних в таблиці мало, можна зробити спільну вибірку і переконатися, що клієнта
ClientName = 'Сачова Татьяна Петровна' вже немає.

SELECT *
FROM Clients

Результат

1.

3.

Опис

У цьому випадку ми обрали клієнта ClientName = 'Сачова Татьяна Петровна', якого необхідно
видалити. За допомогою оператора DELETE виконали видалення. При цьому використали
оператор фільтрації WHERE щоб видалення виконалось тільки для одного клієнта. Якщо не
виконати додаткову фільтрацію то з таблиці Clients видаляться усі клієнти.
30

Тому перед виконанням DELETE завжди потрібно виконувати SELECT з потрібними фільтрами,
які виберуть необхідний масив даних. Після, коли впевнилися, що це саме той масив який
треба видалити, можна починати написання DELETE з тими самими фільтрами.
Якщо потрібно видалити усі дані з таблиці, умови фільтрації вказувати не потрібно. Запит типу
DELETE FROM Clients виконає видалення усіх даних з таблиці.
31

Функції FUNCTION
Функції — підпрограми, які приймають параметри, виконують дії, наприклад пошук, або
складні обчислення і повертають результат цієї дії у вигляді значення. Повернене значення
може бути одиничним або скалярним значення, або результуючим набором (таблицею).

У мовах програмування звичайно існують два типи програм:


- Збережені процедури
- Функції, визначені користувачем (User Defined Functions, UDF)

Функції можуть бути скалярними чи табличними.


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

Функції бувають вбудовані, створені користувачем.

SQL Server містить багато вбудованих функцій, а також підтримує створення визначених
користувачем функцій.

Типи функцій

Функція Опис

Функції, які повертають Повертають об’єкт, який можна використовувати так само як
набір рядків. табличні посилання в SQL-інструкції.

Агрегатні функції Обробляють колекцію значень і повертає одне результуюче


значення.

Ранжируючі функції Повертають ранжируючі значення для кожного рядка в секції.

Скалярна функція Обробляють і повертають одиночні значення.


(розглядається далі)

В учбовому матеріалі присутні скрипти, в яких є приклади функцій і процедури.


Роздивимось кілька прикладів.
32

Приклад 1
Запит

Створення скалярної функції для отримання суми еквіваленту.

CREATE FUNCTION dbo.getAmountEQ (@Amount FLOAT, @Course FLOAT)


RETURNS FLOAT AS
BEGIN
DECLARE @CourseAmount FLOAT;
SET @CourseAmount = ISNULL(@Amount,0) * ISNULL(@Course,0);
RETURN @CourseAmount;
END;
GO

Викличемо функцію з аргументами:


SELECT dbo.getAmountEQ(15, 26.5)

Результат

Опис

Поточна функція на вхід повинна отримати 2 аргументи, @Amount – суму в номіналі валюти,
@Course – курс, по якому буде підрахована сума еквіваленту.
Логіка даної функції проста, отримана сума множиться * на курс.
Для інформації, функцію викликають (такі слова можна чути у процесі роботи). Тому
необхідно розуміти що нам потрібно робити. А потрібно викликати функцію й отримати
результат.

Функція може бути й без аргументів.

Наприклад, вбудована в MS SQL функція CURRENT_TIMESTAMP

Якщо викликати цю функцію то вона поверне нам поточну дату і час, аж до мілісекунд.
SELECT CURRENT_TIMESTAMP

Функція повертає:
33

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

Чим зручна функція?


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

Функцію можна використовувати не тільки для калькуляції значень, але й як умови в


предикаті.

Приклад 2
Запит

Використання функцій у предикаті

SELECT c.*, dbo.getAmountEQ(15, 26.5) AS func


FROM Clients c
WHERE c.ClientId < dbo.getAmountEQ(15, 26.5)

Результат

Опис

Поточний варіант використання показує результат обчислення функції як колонка AS func і


результат використання обчисленого значення функції як предикат і умові WHERE
c.ClientId < dbo.getAmountEQ(15, 26.5).

У підсумку ми отримуємо всі строки таблиці, тому що всі ідентифікатори ClientId однозначно
менші за число 379,5.

Аналогічним чином функцію можна використовувати в INSERT, UPDATE та DELETE,


підставляючи її як аргумент чи предикат.
34

Збережені процедури PROCEDURE


Збережена процедура (Stored procedure) – програма, котра виконується всередині бази
даних і може робити складні дії на основі інформації, заданої користувачем. Оскільки
Збережені процедури виконуються безпосередньо на сервері бази даних, забезпечується
більш висока швидкодія ніж при виконанні тих же операцій засобами клієнта бази даних.

Збережена процедура об’єднує запити і процедурну логіку (оператори присвоєння, логічного


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

Перевага виконання в базах даних збережених процедур замість окремих команд T-SQL:
- необхідні команди вже існують в базі даних;
всі вони пройшли етап синтаксичного аналізу й знаходяться у виконуваному форматі;
- збережені процедури підтримують модульне програмування, так як дозволяють
розбивати великі задачі на самостійні, більш дрібні й зручні у керуванні задачі;
- збережені процедури можуть визивати інші збережені процедури і функції;
- збережені процедури можуть бути викликані з прикладних програм інших типів;
- як правило, збережені процедури виконуються швидше ніж послідовність окремих
команд;
- збережені процедури простіше використовувати: вони можуть складатися з десятків й
сотень команд, але для їхнього запуску достатньо вказати всього лише ім’я потрібної
збереженої процедури. Це дозволяє зменшити розмір запиту відправленого від клієнта
на сервер, а значить й напругу на мережу.

Збережені процедури викликаються програмою клієнта, іншою процедурою або тригером.


Розробник може керувати правами доступу до збереженої процедури, дозволяючи або
забороняючи її виконання. Змінювати код збереженої процедури дозволяється тільки її
власнику чи члену фіксованої ролі бази даних. При необхідності можна передати права
володіння нею від одного користувача другому.
Збережені процедури — це попередньо відкомпільовані процедури, програми, написані на
T-SQL і знаходяться в базі. Збережені процедури виконуються швидше звичайного T-SQL, так
як вони зберігаються у відкомпільованому вигляді. Збережені процедури можуть допомогти
ізолювати користувачів від базових структур, сховати особливості реалізації будь-якої
можливості.

В учбовому матеріалі присутні скрипти, в яких є приклад функцій і процедури.


35

Розглянемо приклад.
Приклад
Запит

Створення процедури, яка призначена для зміни суми вартості курсу по коду курсу.

CREATE PROCEDURE UpdSumCourse (@Code VARCHAR(8), @Amount money)


AS UPDATE Course
SET Amount = @Amount
WHERE Code = @Code;
GO

Попередньо проаналізуємо дані у таблиці COURSE


SELECT *
FROM COURSE

Виконуємо процедуру з аргументами:


EXECUTE UpdSumCourse 'SQ100', 5999.20;

Перевіряємо результат роботи процедури у таблиці COURSE


SELECT *
FROM COURSE

Результат
До

Після

Опис
Дана процедура виконала оновлення вартості курсу.
Аргументами процедури є код курсу SQ100 і сума 5999.20, на яку змінюється вартість курсу.
Результатом є корегування суми 3045,30 на 5999,20 для курсу з кодом SQ100.
36

ТЕМА 3: Обмеження та фільтрація даних за


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

Предикат – це вираз, результатом якого є значення TRUE, FALSE або UNKNOWN. Предикати
використовуються в умовах під час з'єднання таблиць FROM та інших конструкціях, де потрібне
логічне значення. Вони можуть представляти собою як один вираз, так і будь-яку комбінацію
з необмеженою кількістю виразів, побудовану за допомогою булевих операторів AND, OR або
NOT. Окрім того, у цих комбінаціях може використовуватися SQL-оператор IS, а також круглі
дужки для зміни порядку виконання операцій.
Предикат в мові SQL може приймати одне з трьох значень TRUE (істина), FALSE (хибність)
чи UNKNOWN (невідомість). Виключення складають наступні предикати: NULL (відсутність
значення), EXISTS (існування), UNIQUE (унікальність) і MATCH (співпадіння), які не можуть
приймати значення UNKNOWN.
Предикат порівняння представляє собою два вирази, поєднаних оператором порівняння.
Ми маємо шість традиційних операторів порівняння: =, >, <, >=, <=, <> (аналог !=).
37

WHERE

Вибірка даних з використанням фільтрації


Вище ми розглядали вибірку усіх даних з таблиці за допомогою конструкції:

SELECT *
FROM table_name
Але дуже часто потрібно зробити вибірку даних з таблиці по певним критеріям, тобто
відфільтрувати й отримати тільки потрібні дані.
Це можна зробити за допомогою конструкції мови SQL: WHERE

SELECT column_name,column_name
FROM table_name
WHERE column_name operator value

де,
SELECT – ключове слово відповідає за вибірку даних
FROM – ключове слово, яке вказує на певну таблицю, з якої буде відбуватись вибірка даних.
column_name – ім’я колонки (поля)
table_name – ім’я таблиці
WHERE – ключове слово, за допомогою якого виконується фільтрація даних
operator – оператор фільтрації
value – значення, яке використовується для фільтрації
Приклад
Запит

SELECT *
FROM Clients AS c
WHERE c.Country = '804'

Результат

Опис

У даному випадку була зроблена вибірка усіх значень, у котрих поле Country ='804'. Таким
чином, ми отримали дві строки які підпадають під дану умову.
38

Оператори, які використовуються з конструкцією WHERE


Operator Description
= Дорівнює
<> або != Не дорівнює
> Більше ніж
< Менше ніж
>= Більше ніж або дорівнює
<= Менше ніж або дорівнює
BETWEEN У певному проміжку
LIKE Пошук по шаблону
IN Пошук по кільком можливим значенням
OR Логічне “Або”
AND Логічне “І”
NOT Логічне “Не”

Даний набір операторів розширює можливості фільтрації даних з таблиць БД. Оператор
AND і OR використовується після оператора WHERE, вони доповнюють умови фільтрації
інформації.
39

AND

Оператор логічного “І” AND


Даний оператор використовується у випадках наявності більш ніж одної умови для вибірки
даних. Оператор AND відбирає записи, для яких і перша, і друга умова – правдиві (true).

Приклад
Запит

SELECT *
FROM Clients AS c
WHERE c.Country = '804'
AND c.ClientName = 'АТ “Нико холдинг”'

Результат

Опис

В даному прикладі виконаний пошук усіх значень, у яких Country = '804' і ClientName =
'АТ “Нико холдинг”'. Таким чином, отримуємо один рядок з шести, так як тільки він
задовольняє умови пошуку за двома критеріями одночасно.
40

OR

Оператор логічного “Або” OR


Даний оператор використовується у випадках наявності більш ніж одної умови для вибірки
даних. Оператор OR показує записи, для яких одна з умов, або одразу всі умови правдиві
(true).
І тільки якщо всі умови хибні, то в результаті не відобразиться жодний запис.

Приклад
Запит

SELECT *
FROM Clients AS c
WHERE c.Country = '804'
OR c.ClientName = 'Горинич Змій Павлович'

Результат

Опис

У даному прикладі виконаний пошук усіх значень, у яких Country = '804' або ClientName =
'Горинич Змій Павлович'.
Якщо одна з умов хибна, то інформація по ній не відобразиться у кінцевій вибірці.
Відобразиться лише істинна умова. У поточному випадку знайдено 2 клієнти, у яких код
країни = '804', а також 1 клієнт з ClientName = 'Горыныч Змей Павлович'

Оператор AND має вищий пріоритет ніж OR, тому в першу чергу відпрацюють ті умови, які
об’єднані оператором AND, а потім вже будуть незалежно відпрацьовувати умови через
оператор OR (це у випадку, якщо ці два оператори використовуються в одному запиті).
NOT 41

Оператор логічного “Не” NOT


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

Приклад
Запит

SELECT *
FROM Clients AS c
WHERE NOT c.Country = '804'

Аналогічний запит через оператор <> або !=


SELECT *
FROM Clients AS c
WHERE c.Country <> '804'

SELECT *
FROM Clients AS c
WHERE c.Country != '804'

Результат

Опис

У цьому прикладі виводяться усі значення, в яких Country не дорівнює значенню '804'.
42
BETWEEN

Оператор BETWEEN
Даний оператор використовується для пошуку даних у певному проміжку значень.
Конструкція запиту з цим оператором виглядає наступним чином:

SELECT column_name(s)
FROM table_name
WHERE column_name BETWEEN value1 AND value2;

де,
SELECT– ключове слово відповідає за вибірку даних
FROM – ключове слово, яке вказує на певну таблицю, з якої буде виконуватися вибірка даних
column_name – ім’я колонки (поля)
table_name – ім’я таблиці
WHERE – ключове слово, за допомогою якого робиться фільтрація даних
BETWEEN– оператор фільтрації (включає у себе крайні точки фільтрації значень)
value – значення, яке використовують для фільтрації.

Приклад
Запит

SELECT *
FROM Payment p
WHERE p.Amount BETWEEN 500 AND 811.65

Аналогічний запит можна записати через оператор >= і <=

SELECT *
FROM Payment p
WHERE p.Amount >= 500
AND p.Amount <= 811.65

Результат

Опис
У даному запиті були обрані усі значення у діапазоні от 500 до 811.65 по полю Amount.
Зверніть увагу, що з використанням BETWEEN граничні числа також входять у діапазон.
43

IN
Оператор IN
Даний оператор використовується для пошуку по кільком можливим значенням. Також зручна
заміна оператора OR у деяких випадках. Конструкція запиту з цим оператором виглядає
наступним чином:

SELECT column_name(s)
FROM table_name
WHERE column_name IN (value1, value2,...)

де,
SELECT– ключове слово відповідає за вибірку даних
FROM – ключове слово, яке вказує на певну таблицю, з якої буде виконуватися вибірка даних
column_name – ім’я колонки (поля)
table_name – ім’я таблиці
WHERE – ключове слово, за допомогою якого робиться фільтрація даних
IN – оператор фільтрації
value – значення, які використовуються для фільтрації

Приклад
Запит

SELECT *
FROM Clients AS c
WHERE c.Country IN ('642','300','804')

Результат

Опис

У даному прикладі виводяться усі значення, які містять у полі Country = '642' або '300'
або '804'.
Але варіант з використанням оператора IN на порядок зручніший.
44

Дуже часто оператор IN використовується перед вкладеним запитом, який повертає цілий
масив значень. Масив значень може складатися з сотень тисяч значень, які через кому
(наприклад к: Country IN ('642','300','804')) неможливо вручну написати, на це піде
дуже багато часу.
45

LIKE

Оператор LIKE
Даний оператор використовується для пошуку по шаблону. Конструкція запиту з цим
оператором виглядає наступним чином:

SELECT column_name(s)
FROM table_name
WHERE column_name LIKE pattern

де,
SELECT – ключове слово відповідає за вибірку даних
FROM – ключове слово, яке вказую на певну таблицю, з якої буде відбуватись вибірка даних
column_name – ім’я колонки (поля)
table_name – ім’я таблиці
WHERE – ключове слово, за допомогою якого виконується фільтрація даних
LIKE – оператор фільтрації
pattern – шаблон, який використовується для фільтрації

Шаблони, які використовуються з оператором LIKE:

Шаблон Опис
% Заміна будь-якого числа символів
_ Заміна одного символа
[символ, символ] Набір символів, які включаються в пошук
[символ - символ] Діапазон символів, які включаються в пошук
[^символ, символ] Набір символів, які виключаються з пошуку
[^символ - символ] Діапазон символів, які виключаються з пошуку
46

Приклад 1
Запит

SELECT *
FROM Clients AS c
WHERE c.ClientName LIKE 'с%'

Результат

Опис

У цьому прикладі отримуємо усі значення, де ClientName починається з літери с.


Якщо після лапок стоїть символ/набір символів які треба знайти, а за ним знак %, то це
означає, що буде знайдений рядок, який починається з того символу/набору символів, а
наступні символи після літери с можуть бути будь-якими (за це відповідає символ-маска %).

Приклад 2
Запит

SELECT *
FROM Clients AS c
WHERE c.ClientName LIKE '%т%'

Результат

Опис

В результаті ми отримаємо усі значення, де ClientName має в своїй назві літеру т в будь-
якому сегменті.
Якщо після лапки стоїть знак %, далі символ/набір символів який треба знайти, а за ним знак
%, це означає, що буде знайдений рядок, який починається й закінчується будь-ким символом,
але між ними будуть знайдені усі варіанти в будь-якій позиції тексту з літерою т.
47

Приклад 3
Запит

SELECT *
FROM Clients AS c
WHERE c.ClientName LIKE 'Г__и_ич%'

Результат

Опис

У цьому прикладі отримаємо запис, де ClientName відповідає вказаному шаблону. Під


даний шаблон підходить тільки рядок с Горинич Змій Павлович.
Знак _ означає, що на його місці може бути ОДИН будь-який символ. Якщо невідомо одразу
кілька послідовних символів, можна використати даний символ стільки разів, скільки
невідомих символів потрібно знайти.
Також дуже часто символ _ використовують у випадку пошуку візуально схожих літер с різних
алфавітів. Наприклад: а, о, р, і (кирилиця), візуально схожі на a, o, p, i (латиниця). Тому, щоб
не помилитися з пошуком, схожі літери можна замінити символом _ в рядку пошуку після
оператора LIKE.

Приклад 4
Запит

SELECT *
FROM Clients AS c
WHERE c.ClientName LIKE '[sаг]%'

Результат

Опис
48

У цьому прикладі виводяться усі значення, де ClientName в першому символі свого значення
має літеру s або а або г.
Приклад 5
Запит

SELECT *
FROM Clients AS c
WHERE c.ClientName LIKE '[^sаг]%'

Результат

Опис

У цьому випадку виводяться усі значення, де ClientName в першому символі свого значення
НЕ має літеру s або а або г.

Приклад 6
Запит

SELECT *
FROM Clients AS c
WHERE c.ClientName LIKE '[s-с][а-п]%'

Результат

Опис

У цьому випадку виводяться усі значення, де ClientName в першому символі свого значення
має літери у діапазоні від англійської s до української с, а у другому символі значення має
літери у діапазоні від української а до п. А далі будь-які символи, бо вказана маска %.
49

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

Також у даному прикладі використаний пошук по двом першім літерам. Кожен сегмент який
ми шукаємо, відокремлюється квадратними дужками []. Якщо потрібно шукати по кільком
сегментам в слові, то кожен сегмент можна обробити дужками []. Можна комбінувати
символи _, %, [].
50

DISTINCT

Вибірка унікальних даних DISTINCT


Коли потрібно зробити вибірку унікальних значень, використовується конструкція:
SELECT DISTINCT.
Конструкція запиту для таких задач виглядає наступним чином:

SELECT DISTINCT column_name, column_name


FROM table_name

де,
SELECT– ключове слово відповідає за вибірку даних
DISTINCT – ключове слово, яке вказує на те, що дані у вибірці будуть унікальними по вказаним
полям/полю
column_name – ім’я колонки (поля)
table_name – ім’я таблиці
FROM – ключове слово, яке вказує на певну таблицю, з якої буде робитись вибірка даних

Приклад
Запит

SELECT DISTINCT c.Country


FROM Clients c

Результат

Опис

У таблиці Clients існує 6 записів, але якщо цікавить унікальна інформація про країни клієнтів,
то виконавши цей запит, отримаємо набір унікальних кодів країн з цієї таблиці. Через кому
можна вказувати й інші колонки для відображення, тоді унікальність буде спрацьовувати для
усіх перерахованих колонок одночасно (будуть вишукуватися унікальні комбінації з вказаних
колонок).
ORDER BY 51

Сортування даних таблиці ORDER BY


Конструкція запиту для сортування даних виглядає наступним чином:

SELECT column_name, column_name


FROM table_name
ORDERBY column_name ASC|DESC

де,
SELECT– ключове слово відповідає за вибірку даних
FROM – ключове слово, яке вказує на певну таблицю, з якої буде робитися вибірка даних
ORDER BY – ключове слово за допомогою якого виконується сортування даних
column_name – ім’я колонки (поля)
table_name – ім’я таблиці
ACS – ключове слово, яке вказує на сортування за зростанням (також воно являється
значенням за умовчуванням і може не вказуватися)
DESC – ключове слово, яке вказує на сортування по спаданню

Приклад 1
Запит

SELECT *
FROM Clients c
ORDER BY c.ClientName

Результат

Опис

У цьому прикладі сортування у таблиці Clients виконано по колонці ClientName. Так як не


вказаний порядок сортування, то вона за умовчуванням виконана по зростанню (аналогічно
відпрацює запит з рядком ORDER BY c.ClientName ASC)
52

Приклад 2
Запит

SELECT *
FROM Clients c
ORDER BY c.Country DESC

Результат

Опис

У цьому прикладі сортування в таблиці Clients виконане по колонці Country у зворотному


порядку. Для цього вказаний порядок сортування DESC.

Якщо потрібно сортувати кілька полів, то в рядку ORDER BY необхідно через кому
перерахувати набір полів, які потрібно сортувати (після кожного поля можна вказувати
порядок сортування).

Окрім випадків перерахування полів можна використовувати порядковий номер колонки, але
у цьому є певний ризик.

SELECT *
FROM Clients c
ORDER BY 3 DESC
Цей запит поверне точно такий самий результат як у Прикладі 2.

Стосовно ризику. Якщо в таблиці відбудуться зміни, а саме, вона буде перестворена з іншим
порядком колонок, то написаний раніше запит відсортує хоч і по 3-й колонці, але якщо раніше
то була колонка Country, то після змін таблиці на 3-м місці може бути зовсім інша колонка, що
призведе до сортування по новій колонці.
53

SELECT TOP

Вибірка певної кількості записів TOP


Вибірка певної кількості записів виконується наступним запитом:

SELECT TOP number|percent column_name(s)


FROM table_name

де,
SELECT– ключове слово відповідає за вибірку даних
FROM – ключове слово, яке вказує на певну таблицю, з якої буде робитися вибірка даних
column_name – ім’я колонки (поля)
table_name – ім’я таблиці
TOP – ключове слово, яке вказує на те, що буде показано тільки певну кількість записів
number – кількість рядків, які будуть виведені запитом
percent – ключове слово, яке вказує на те, що буде показано тільки певний відсоток записів
від загальної кількості записів у таблиці

Приклад 1
Запит

Зробимо вибірку 7 записів.


SELECT TOP 7 *
FROM Customer

Результат

Опис

У цьому прикладі виконується вибірка 7 записів.


54

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

Приклад 2
Запит

Виконаємо вибірку 10% записів.


SELECT TOP 10 PERCENT *
FROM Customer

Результат

Опис

У цьому прикладі виконується вибірка 10% записів. В таблиці лише 25 записів. 10% це
приблизно 3 рядки.
Подібні вибірки часто потрібні для попереднього поверхневого аналізу даних у таблиці.
Якщо до даного запиту додати більше умов фільтрації (з WHERE і AND/OR), при цьому, якщо
таблиця буде мати сотні тисяч записів, то вибірка може працювати довше. Але з оператором
TOP, при додатковій фільтрації вибірка відпрацює швидше, хоча й виведе на екран тільки
вказану кількість записів.
55

UNION [ALL]

Об’єднання результатів запитів оператором UNION [ALL]

Оператор UNION [ALL] використовується для об’єднання двох і більше результатів запитів
оператора SELECT в загальний список.
Оператор UNION [ALL] має наступний синтаксис:

SELECT column_name(s)
FROM table1
UNION [ALL]
SELECT column_name(s)
FROM table2

Важливо відмітити, що кожен з операторів SELECT повинен мати у своєму запиті однакову
кількість полів і типи даних цих полів повинні співпадати, інакше виникне помилка при
формуванні результуючої таблиці.
Помилка: All queries combined using a UNION, INTERSECT or EXCEPT operator
must have an equal number of expressions in their target lists.

Усі запити об’єднані з використанням оператора UNION, INTERSECT або EXCEPT, повинні мати
рівну кількість виразів у своїх цільових списках.

Приклад 1
Запит

SELECT *
FROM UserData
UNION ALL
SELECT *
FROM Customer

Результат

Опис
56

У цьому прикладі виконується вибірка двома запитами, які об’єднані оператором UNION ALL.
Але у результаті виникне помилка.
1. Кількість колонок у двох вибірках не співпадає.
2. Відповідні колонки мають різний формат даних.

Для наочності роздивіться вибірки кожного з запитів:

Веж таки бувають випадки, що необхідно об’єднати кілька різних таблиць. В таких випадках
необхідно звести кількість колонок що виводяться до однакової кількості, і відповідні колонки
повинні бути однакового між собою формату.

Приклад 2
Запит

SELECT u.UserDataId, u.FirstName, u.MiddleName, u.LastName, u.Status


FROM UserData u
UNION ALL
SELECT c.Cust_Id, c.Surname, c.Name, c.MiddleName, c.Gender
FROM Customer c

Результат
57

Опис

У цьому прикладі виконується вибірка двома запитами, які об’єднуються оператором UNION
ALL дві різних таблиці.
Помилка не виникає, так як кількість виведених полів і їх типи даних співпадають.
Найменування колонок приймають назви верхнього запиту.
Наглядно видно, що у верхньому запиті є поле Status, а у нижньому Gender. Поля
відповідають за різні за сенсом дані, але вони однакового формату й можуть об’єднатися.

Частіше всього доводиться об’єднувати таблицю саму собою. При цьому кожен з запитів буде
мати однаковий початок з перерахуванням колонок, але у кожного з запитів буде відрізнятися
умовами фільтрації WHERE. Буває так, що неможливо в одному запиті написати вибірку, яка
би задовольнила умови пошуку, тому в кожному з об’єднаних запитів пишеться своя умова, а
результат об’єднується через UNION [ALL].

Приклад 3
Запит

SELECT *
FROM UserData u
WHERE u.UserName IN ('admin','smart')
UNION ALL
SELECT *
FROM UserData u
WHERE u.UserDataId > 6

Результат
58

Опис

У цьому запиті виконана вибірка, яка об’єднує одну і ту саму таблицю UserData, але кожен з
запитів має різні умови.
В даному випадку, якщо потрібно виводити всі поля, поєднана таблиця одна й та сама в двох
запитах, то перераховувати всі поля в секції SELECT не потрібно.

Різниця між UNION та UNION ALL

В цілому, оператор UNION використовується для об’єднання отриманої інформації з двох і


більше таблиць.
Різниця між UNION і UNION ALL полягає у тому, що UNION ALL не видаляє дублюючі строки,
він виводить всі рядки з усіх таблиць, відповідних запитів, і об’єднують їх у загальну таблицю.
Оператор UNION ефективно виконує SELECT DISTINCT у наборі результатів. Тобто, якщо в
результуючій таблиці будуть знайдені дублі записів, то вони будуть видалені, тим самим
інформація буде повністю унікальна в рамках загальної вибірки. Якщо ви знаєте, що усі записи
з об’єднаних таблиць будуть унікальні, замість цього використовуйте UNION ALL, це дасть
більш швидкі результати.

Приклад 4
Запит

SELECT *
FROM UserData u
WHERE u.MiddleName IS NULL
UNION ALL
SELECT *
FROM UserData u
WHERE u.UserDataId > 3

Результат
59

Опис

У даному запиті виконана вибірка, яка об’єднує одну і ту саму таблицю UserData через
UNION ALL, але кожен з запитів має різні умови.
У цьому випадку умови підібрані так, щоб показати, що UNION ALL просто склеює 2 вибірки
без аналізу на дублюючі записи.
З прикладу видно, що користувач test відобразився двічі, тому що в обох запитах він
задовольняє умови пошуку.

Приклад 5
Запит

SELECT *
FROM UserData u
WHERE u.MiddleName IS NULL
UNION
SELECT *
FROM UserData u
WHERE u.UserDataId > 3

Результат

Опис

У даному випадку запиті виконана вибірка, яка об’єднує одну і ту саму таблицю UserData
через UNION, але кожен з запитів має різні умови.
У цьому випадку умови підібрані так щоб показати, що UNION виконує аналіз на дублюючі
записи і запит виводить унікальні записи.
З прикладу видно, що на цей раз користувач test відобразився лише раз, так як дублюючий
рядок був видалений.
60

Агрегатні функції
Агрегатні функції виконують обчислення над групою значень в рамках колонки даних і завжди
повертають одне значення результату цих обчислень. Мова T-SQL підтримує кілька звичайних
агрегатних функцій:

AVG
Обчислює середнє арифметичне значення даних, що містяться у колонці. Значення, над яким
виконується обчислення, повинні бути числовими.

MIN і MAX
Визначає максимальне і мінімальне значення з усіх значень даних, що містяться у колонці.
Значення можуть бути числовими або формату дата (дата/час).

SUM
Визначає загальну суму значень у колонці. Значення, над якими виконується обчислення,
повинні бути числовими.

COUNT
Підраховує кількість значень, відмінних від NULL у колонці. Функція COUNT(*) являється
єдиною агрегатною функцією, яка не виконує обчислення над стовпчиками.

COUNT_BIG
Аналогічна функція COUNT, з тією різницею, що повертає значення даних типу BIGINT.

Опис і робота з агрегатними функціями


З агрегатними функціями часто використовувати оператор HAVING і GROUP BY.
Оператор HAVING являється вказівником на результат виконання агрегатних функцій.

Оператор HAVING має наступний синтаксис:


SELECT *
FROM table_name
HAVING aggregate_function(column_name) operator value

де,
SELECT– ключове слово відповідає за вибірку даних
FROM – ключове слово, яке вказує на певну таблицю, з якої буде робитися вибірка даних
table_name – ім’я таблиці
61

HAVING – оператор фільтрації умов агрегатної функції,


aggregate_function – функція агрегації,
column_name – колонка, дані якої агрегуються функцією, operator – умовний оператор (<,
<=, >, >=, <>, =), який порівнює значення агрегації функції і value ,
value – значення, яке порівнюється з агрегованим значенням.
Оператор HAVING аналогічний оператору WHERE за тим виключенням, що застосовується не
для всього набору стовпчиків таблиці, а для набору створеного оператором GROUP BY і
застосовується завжди тільки після нього.
Якщо речення WHERE визначає предикат для фільтрації рядків по реальним даним таблиці, то
речення HAVING застосовується після групування для визначення аналогічного предиката,
фільтруючого за значенням агрегатних функцій. Тому така перевірка не може міститися у
реченні WHERE.

Оператор GROUP BY використовується для зведення результатів вибірки по одному або


кільком стовпчикам.
Оператор GROUP BY має наступний синтаксис:
GROUP BY column_name(s)

де,
GROUP BY – оператор групування,
column_name(s) – колонки, які групуються.

Загальне правило для агрегатних функцій.


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

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


кількох зв’язаних таблиць.

Приклад 1
Запит

SELECT COUNT(*) AS 'Кількість'


FROM Payment
62

Результат

Опис

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

Приклад 2
Запит

SELECT DebAcc, SUM(AmountUAH) AS 'Сума', COUNT(*) AS 'Кількість'


FROM Payment
GROUP BY DebAcc
ORDER BY COUNT(*) DESC

Результат

Опис
63

У цьому запиті виконана вибірка яка підраховує загальну суму SUM(AmountUAH) та кількість
COUNT(*) платежів у таблиці Payment по полю DebAcc. При цьому використано сортування
ORDER BY по COUNT(*) у зворотному порядку.
Даний приклад використовується доволі часто у роботі для збору статистичної інформації.
Під час групування в полі DebAcc відображаються унікальні значення, а у сумі та кількості
відображується зведена інформація по цьому запису.

Приклад 3
Запит

SELECT Data_doc,
SUM(AmountUAH) AS 'Сума',
COUNT(*) AS 'Кількість',
MIN(AmountUAH) AS 'Мін.сума',
MAX(AmountUAH) AS 'Макс.сума',
AVG(AmountUAH) AS 'Середня сума'
FROM Payment
GROUP BY Data_doc
HAVING SUM(AmountUAH) > 130000
ORDER BY Data_doc

Результат

Опис

У цьому запиті виконана вибірка яка підраховує загальну суму SUM(AmountUAH), кількість
COUNT(*) платежів, мінімальну суму MIN(AmountUAH), максимальну суму MAX(AmountUAH)
і середню суму AVG(AmountUAH) у таблиці Payment згруповану по даті GROUP BY Data_doc.
При цьому використано сортування ORDER BY по Data_doc, а також виконана фільтрація по
агрегованому значення загальної суми HAVING SUM(AmountUAH) > 130000.
Під час групування в полі Data_doc відображуються унікальні значення, а у сумі та кількості
відображується зведена інформація по цьому запису.
64

Аналогічним чином можна групувати та фільтрувати значення з різних об’єднаних


таблиць за допомогою JOIN. Необхідно буде у секції SELECT вивести поля з об’єднаних
таблиць, для деяких полів використовувати функції агрегації, для полів без функції
використовувати групування GROUP BY, за необхідності відфільтрувати оператором
HAVING.
65

ТЕМА 4: Створення об’єктів баз даних


CREATE DATABASE

Створення бази даних


Створення бази даних виконується наступним чином:

CREATE DATABASE dbname

де,
CREATE – ключове слово створення сутності бази даних
DATABASE - ключове слово, яке вказує, що дана сутність – БД
dbname – ім’я бази даних

Приклад:

CREATE DATABASE Test_DB

Результат:
66

CREATE TABLE

Створення таблиці CREATE TABLE

Створення таблиці виконується таким чином:

CREATE TABLE table_name


(
column_name1 data_type(size) NULL or NOT NULL,
column_name2 data_type(size) NULL or NOT NULL,
column_name3 data_type(size) NULL or NOT NULL,
....
)
де,
CREATE – ключове слово створення об’єкту бази даних
TABLE – ключове слово, яке вказує, що даних об’єкт – таблиця
table_name – ім’я таблиці
column_name – ім’я колонки (поля)
data_type – тип даних колонки

Приклад
Запит

CREATE TABLE Students


(
PersonID INT IDENTITY(1,1),
LastName VARCHAR(50),
FirstName VARCHAR(50),
Address VARCHAR(255),
City VARCHAR(50)
)

Перевіряємо таблицю

SELECT *
FROM Students
67

Результат

Опис

Даним запитом створена пуста таблиця Students.

Зміна назви колонки

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

Якщо в таблиці вже існує багато даних, звісно, видаляти весь об’єкт Таблиця, не слід, бо з
видаленням таблиці видаляться всі її дані. В такому випадку слід виконати змінення
найменування колонки, а після перевірити валідність пакетів. Якщо у БД будуть пакети у статусі
Invalid, слід в них перевірити наявність посилання на таблицю, в якій проводили зміни
колонки, знайти стару назву колонки в пакеті і змінити його на нове значення. Після чого
скомпілювати пакет.

Зміна найменування колонки виконується командою з використанням процедури:

EXEC sp_rename 'table_name.column_name', 'column_newname', 'object_type'

де,
EXEC – ключове слово для виконання процедури
sp_rename – назва процедури, яка змінює назву колонки
table_name – ім’я таблиці
column_name – старе ім’я колонки (поля)
column_newname – нове ім’я колонки (поля)
object_type – Тип об’єкту якому змінюють назву

Приклад
Запит

EXEC sp_rename 'Students.City', 'Town', 'COLUMN';

Результат
68

Опис

У цьому прикладі процедура sp_rename з вказанням параметрів виконує зміну назви


колонки City на Town у таблиці Students. При виконанні змін СУБД проінформує:
Caution: Changing any part of an object name could break scripts and stored procedures.
Зміна будь-якої частини імені об’єкту може зруйнувати скрипти та збережені процедури.

Зміна типу даних колонки

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

Зміна типу колонки виконується наступною командою:


ALTER TABLE table_name
ALTER COLUMN column_name NVARCHAR(100)

де,
ALTER – ключове слово для зміни об’єкту бази даних
TABLE – ключове слово, яке вказує, що даний об’єкт – Таблиця
table_name – ім’я таблиці
column_name –ім’я колонки (поля), в якій змінюється тип даних
NVARCHAR(100) – новий тип і розмір даних для колонки column_name

Приклад:

ALTER TABLE Students


ALTER COLUMN Town NVARCHAR(100)
69

Додавання колонки у таблицю

При додаванні колонки у таблицю, колонка додається пустою.

ALTER TABLE table_name


ADD COLUMN column_name

де,
ALTER – ключове слово для зміни об’єкту бази даних
TABLE – ключове слово, яке вказує, що даний об’єкт – Таблиця
table_name – ім’я таблиці
ADD – ключове слово, яке відповідає за додавання об’єкту
COLUMN – назва об’єкту, який буде додаватися
column_name –ім’я колонки, яка буде додана у таблицю

Приклад
Запит

ALTER TABLE Students


ADD Age int NULL

Перевіряємо таблицю

SELECT *
FROM Students

Результат

Опис

Даним записом додана колонка з назвою Age у таблицю Students. Додана колонка буде
останньою.
70

Видалення колонки з таблиці

При видаленні колонки з таблиці ця колонка і весь її зміст буде видалений з таблиці бази
даних. Відмінити цю дію буде неможливо.

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

ALTER TABLE table_name


DROP COLUMN column_name

де,
ALTER – ключове слово для зміни об’єкту бази даних
TABLE – ключове слово, яке вказує, що даний об’єкт – Таблиця
table_name – ім’я таблиці
DROP – ключове слово, яке відповідає за видалення об’єкту
COLUMN – назва об’єкту, який буде видалятися
column_name –ім’я колонки (поля), яка буде видалена

Приклад
Запит

ALTER TABLE Students


DROP COLUMN Town

Результат

Опис

Цим запитом видалена колонка з назвою Town таблиці Students.


71

Видалення таблиці DROP TABLE


DROP TABLE table_name
де,
DROP – ключове слово для видалення об’єкта бази даних
TABLE – ключове слово, яке вказує, що даний об’єкт – Таблиця
table_name – ім’я таблиці, що буде видалена

Командою DROP TABLE Student, видаляється таблиця Student, при цьому видаляється
таблиця цілком, з усією інформацією.

CREATE VIEW

Створення представлення CREATE VIEW

Створення представлення виконується наступним запитом:


CREATE VIEW view_name[(column_list)]
AS select_statement

де,
CREATE – ключове слово створення об’єкту бази даних
VIEW – ключове слово, яке вказує, що даний об’єкт – Представлення
column_list – найменування колонок в Представленні. Якщо не вказувати, то колонки будуть
мати ім’я як в основному запиті(select_statement)
select_statement – запит, з якого формується саме Представлення

Приклад
Запит

Створення VIEW
CREATE VIEW Cust$Acc (FullName, Acc, Curr) AS
SELECT c.NameUser,
a.Account,
a.Currency
FROM Customer c,
Account a
72

WHERE c.Cust_Id = a.Cust_Id

Вибірка з VIEW:
SELECT *
FROM Cust$Acc

Результат

Опис

У цьому прикладі створено представлення Cust$Acc. В тілі представлення знаходиться


стандартний SELECT з поєднанням таблиць (але може бути і без поєднання таблиць).
Представлення часто використовується, особливо для інтерфейсів в ПО. Також, завдяки VIEW
обмежується видимість полів, особливо, якщо користувачу заборонений доступ до таблиць,
які знаходяться у тілі VIEW.
З VIEW неможливо виконати операції INSERT, UPDATE і DELETE.

Видалення представлення DROP VIEW

DROP VIEW view_name


де,
DROP – ключове слово для видалення об’єкта з бази даних
VIEW – ключове слово, яке вказує, що даний об’єкт – Представлення
view_name – ім’я представлення, що буде видалятися

Командою DROP VIEW Cust$Acc, видаляється представлення Cust$Acc, При цьому таблиці, які
були в тілі представлення, не видаляються.
73

Обмеження CONSTRAINT
Задача будь-якого адміністратора і програміста, сумісно забезпечити цілісність та коректність
даних. Наприклад, якщо при заповнені таблиці користувач не вкаже в одному з рядків у списку
продавців прізвище, то коректність даних псується. Дуже важко знайти людину в БД, якщо
невідоме прізвище.
Задача спеціаліста по тестуванню, вивчити структуру БД, таблиць, полів, обмежень, щоб у
своїй роботі правильно звертатися до БД з метою отримати/змінити/видалити дані.
Під даними мається на увазі не тільки дані які знаходяться в таблицях, але і самі об’єкти БД
(table, trigger, constraint тощо).

Ця тема розкриє основні моменти з направлення Обмежень в БД на прикладі об’єкту


CONSTRAINT.

Обмеження являють собою правила, які примусово застосовуються у БД.

CONSTRAINT – деяка внутрішня процедура, що служить для контролю цілісності даних.


Плюси - не потрібно самому писати код і оптимізована за швидкістю.
Мінуси - не дозволяє контролювати достатньо складні умови.

TRIGGER - процедура, яка спрацьовує коли стається деяка подія (наприклад, вставки у
таблицю). Може використовуватися для контролю цілісності даних (більш складні умови, котрі
не можуть забезпечити constraint), для аудиту, розрахунку величин, захисту і тому подібного.
Плюси - більше можливостей, ніж у constraint.
Мінуси - потрібно самому писати код, і як правило, довше працює, ніж constraint.

Роздивимось окремі параметри представлених конструкцій, які пов’язані з обмеженнями


цілісності даних. Обмеження цілісності мають пріоритет над тригерами, правилами і
значеннями за умовчуванням. До обмежень цілісності відносяться обмеження первинного
ключа PRIMARY KEY, обмеження зовнішнього ключа FOREIGN KEY, обмеження
унікальності UNIQUE, обмеження значення NULL, обмеження на перевірку CHECK.

Обмеження первинного ключа (PRIMARY KEY)


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

одного стовпчика, то значення в межах одного стовпчика можуть дублюватися, але будь-яка
комбінація значень усіх стовпчиків первинного ключа повинна бути унікальною.
При створенні первинного ключа SQL Server автоматично створює унікальний індекс для
стовпчиків, які входять у первинний ключ. Він пришвидшує доступ до даних цих стовпчиків при
використанні первинного ключа у запитах.
Таблиця може мати тільки одне обмеження PRIMARY KEY, при чому жоден з включених
у первинний ключ стовпчиків не може приймати значення NULL. При спробі використати у
якості первинного ключа стовпчик (чи групу стовпчиків) для якого обмеження первинного
ключа не виконуються, первинний ключ не буде створений, а система відобразить
повідомлення про помилку.
Оскільки обмеження PRIMARY KEY гарантує унікальність даних, воно часто визначається для
стовпчиків-лічильників. Створення обмежень цілісності PRIMARY KEY можливо як при
створенні так і при зміні в таблиці. Одним з призначень первинного ключа є забезпечення
цілісності посилань даних кількох таблиць. Зрозуміло, що може бути реалізовано тільки при
визначенні відповідних зовнішніх ключів в інших таблицях.

Обмеження зовнішнього ключа (FOREIGN KEY)


Обмеження зовнішнього ключа - це основний механізм для підтримання посилань
цілісності між таблицями реляційної бази даних. Стовпчик дочірньої таблиці, визначений в
якості зовнішнього ключа в параметрі FOREIGN KEY, застосовується для посилання на стовпчик
батьківської таблиці, який у ній є первинним ключем. Ім’я батьківської таблиці і стовпчик
її первинного ключа вказуються в реченні REFERENCES. Дані в стовпчиках, визначених
якості зовнішнього ключа, можуть приймати тільки такі значення, які знаходяться у пов’язаних
з ним стовпчиках первинного ключа батьківської таблиці. Співпадіння імен стовпчиків для
зв’язку дочірньої й батьківської таблиць необов’язкове. Первинний ключ може бути
визначений для стовпчика з одним іменем, в той час як стовпчик, на який накладено
обмеження FOREIGN KEY, може мати зовсім інше ім’я. Єдиною вимогою залишається
відповідність стовпчиків за типом і розміром даних.
На первинний ключ можуть посилатися не тільки стовпчики інших таблиць, але й стовпчики,
розташовані в тій самій таблиці, що й власне первинний ключ. Це дозволяє створювати
рекурсивні структури.
Зовнішній ключ може бути пов’язаний не тільки з первинним ключем іншої таблиці. Він може
бути визначений й для стовпчиків з обмеженням UNIQUE іншої таблиці чи будь-яких інших
стовпчиків, але таблиці повинні знаходитися в одній базі даних.
Стовпчики зовнішнього ключа можуть містити значення NULL, однак перевірка на
обмеження FOREIGN KEY ігнорується. Зовнішній ключ може бути проіндексований, тоді сервер
буде швидше відшукувати потрібні дані. Зовнішній ключ визначається як при створення, так і
при зміненні таблиць.
75

Обмеження цілісності посилань задає вимоги відповідно яким для кожного запису у дочірній
таблиці повинен бути запис у батьківській таблиці. При цьому зміна значення зв’язку
стовпчика в записі батьківської таблиці при наявності дочірнього запису блокуються, рівно так
само як і видалення батьківського запису (заборона каскадної зміни і видалення), що
гарантується параметрами ON DELETE NO ACTION і ON UPDATE NO ACTION, прийнятими за
умовчуванням. Для дозволу каскадного впливу слід використовувати параметри ON DELETE
CASCADE і ON UPDATE CASCADE.

Обмеження унікального ключа (UNIQUE)


Це обмеження задає умови унікальності значення поля (стовпчика) або групи полів
(стовпчиків), що входять в унікальний ключ, по відношенню до інших записів.
Обмеження UNIQUE для стовпчика таблиці схоже на первинний ключ: для кожного рядка
даних в ньому повинні бути унікальні значення. Встановивши для деякого стовпчика
обмеження первинного ключа, можна одночасно встановити для іншого стовпчика
обмеження UNIQUE. Відміна в обмеженні первинного і унікального ключів полягає у тому,
що первинний ключ служить як для упорядкування даних в таблиці, так і для поєднання
зв’язаних між собою таблиць. Окрім того, при використанні обмежень UNIQUE допускається
існування значення NULL, але лише один єдиний раз.

Обмеження на значення (NOT NULL)


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

Перевірочне обмеження (CHECK)


Дане обмеження використовується для перевірки допустимих даних, що вводяться в
конкретний стовпчик таблиці, тобто CHECK забезпечує ще один рівень захисту даних.
Обмеження цілісності CHECK задає діапазон можливих значень для стовпчику чи стовпчиків. В
основі обмежень цілісності CHECK лежить використання логічних виразів.
Допускається застосування кількох обмежень CHECK до одного й того ж стовпчика. В цьому
випадку вони будуть застосовані у тій послідовності, в якій відбувалося їх створення. Можливо
застосування одного й того ж обмеження до різних стовпчиків і використання в логічних
76

виразах значень інших стовпчиків. Вказування параметру NOT FOR REPLICATION наказує не
виконувати перевірочних дій, якщо вони виконуються підсистемою реплікації.

Загальний приклад створення таблиці з одночасним створенням CONSTRAINT (PRIMARY KEY,


FOREIGN KEY, UNIQUE, CHECK)

--Створюємо таблицю рахунків

CREATE TABLE [Account] (


[AccId] int IDENTITY (1,1),
[Cust_Id] int NOT NULL,
[Account] VARCHAR (10) NOT NULL,
[Currency] CHAR(3) NOT NULL,
[Status] CHAR(1) NOT NULL,
[OpenDate] DATE NOT NULL,
[CloseDate] DATE
CONSTRAINT PK_Account PRIMARY KEY ([AccId]),
CONSTRAINT FK_Account_Customer FOREIGN KEY ([Cust_Id]) REFERENCES Customer
([Cust_Id]),
CONSTRAINT FK_Account_Currency FOREIGN KEY ([Currency]) REFERENCES Currency ([R030]),
CONSTRAINT UQ_Acc_Curr UNIQUE ([Account],[Currency]),
CONSTRAINT CK_Account_Status CHECK ([Status] IN ('O', 'C')) -- O = Open, C = Close
)
GO

Правила найменувань для CONSTRAINT

Якщо створюється PRIMARY KEY, то назва повинна починатися з PK і через знак «_» вказуємо
таблицю, в якій він створюється, і ще через знак «_» вказуємо поле поточної таблиці, яке буде
первинним ключем.
Якщо створюється FOREIGN KEY, то назва повинна починатися з FK і через знак «_» вказуємо
таблицю на яку робиться посилання, і ще через знак «_» вказуємо поле поточної таблиці, яке
буде зовнішнім ключем.
Якщо створюється UNIQUE, то назва повинна починатися з UQ і через знак «_» вказуємо
таблицю в якій він створюється, і ще через знак «_» вказуємо поле поточної таблиці, яке буде
містити унікальне значення.
Якщо створюється CHECK, то назва повинна починатися з CK і через знак «_» вказуємо таблицю
в якій він створюється, і ще через знак «_» вказуємо поле поточної таблиці, яке буде містити
набір унікальних значень (набір унікальних значень вказуємо вручну).
77

Додавання у існуючу таблицю первинного ключа (PRIMARY


KEY)
Додавання constraint PRIMARY KEY в таблиці виконується наступним запитом:

ALTER TABLE table_name_a


ADD CONSTRAINT constraint_name PRIMARY KEY(column_name_a)

де,
ALTER – ключове слово для зміни об’єкта бази даних
TABLE – ключове слово, яке вказує, що даний об’єкт – Таблиця
table_name_a – ім’я таблиці, в якій додається CONSTRAINT
ADD – ключове слово, яке означає додавання об’єкту бази даних
CONSTRAINT – об’єкт бази даних, який додається в таблицю table_name_a
constraint_name – назва CONSTRAINT, згідно з правилами найменувань описаних вище
PRIMARY KEY – тип CONSTRAINT’а
column_name_a – ім’я колонки (поля) таблиці table_name_a, яке буде первинним ключем.

Приклад:
--Додаємо первинний ключ на поле Cust_Id в таблиці Customer

ALTER TABLE [Customer]


ADD CONSTRAINT PK_Customer PRIMARY KEY (Cust_Id)

Додавання зовнішнього ключа у існуючу таблицю (FOREIGN


KEY)

Додавання constraint FOREIGN KEY в таблиці здійснюється таким чином:

ALTER TABLE table_name_a


ADD CONSTRAINT constraint_name FOREIGN KEY (column_name_a)
REFERENCES table_name_b (column_name_b)

де,
ALTER – ключове слово для зміни об’єкту бази даних
TABLE – ключове слово, яке вказує, що даний об’єкт – Таблиця
table_name_a – ім’я таблиці, в якій додається CONSTRAINT
78

ADD – ключове слово, що означає додавання об’єкту бази даних


CONSTRAINT – об’єкт бази даних, який додається в таблицю table_name_a
constraint_name – назва CONSTRAINT, згідно правил найменувань, прописаних вище
FOREIGN KEY – тип CONSTRAINT’а
column_name_a – ім’я колонки (поля) таблиці table_name_a, яке буде
посилатися/пов’язуватися з полем column_name_b таблиці table_name_b
REFERENCES – ключове слово, що вказує на посилання до таблиці table_name_b
table_name_b – ім’я таблиці, на яку посилаються CONSTRAINT
column_name_b – ім’я колонки (поля) таблиці table_name_b, на яке посилається поле
column_name_a таблиці table_name_a

Приклад:
--Додаємо зовнішній ключ для поля Country у таблиці Customer на довідник країн Country по полю K040

ALTER TABLE [Customer]


ADD CONSTRAINT FK_Customer_Country FOREIGN KEY ([Country])
REFERENCES Country ([K040])

Додавання унікального ключа у існуючу таблицю (UNIQUE)


Додавання constraint UNIQUE у таблиці здійснюється наступним чином:

ALTER TABLE table_name


ADD CONSTRAINT constraint_name UNIQUE (column_name_1,
column_name_2,…)

де,
ALTER – ключове слово для зміни об’єкту бази даних
TABLE – ключове слово, яке вказує, що даний об’єкт – Таблиця
table_name – ім’я таблиці, в якій додається CONSTRAINT
ADD – ключове слово, що означає додавання об’єкта бази даних
CONSTRAINT – об’єкт бази даних, який додається у таблицю table_name
constraint_name – назва CONSTRAINT, згідно правил найменувань, прописаних вище
UNIQUE – тип CONSTRAINT’а
column_name_1, column_name_2 – ім’я колонки/колонок таблиці table_name, які будуть
відповідати за унікальність значень у цій/цих колонці/колонках.

Приклад:
--Додаємо унікальний ключ у таблиці Account до полів ([Account],[Currency])
79

ALTER TABLE [Account]


ADD CONSTRAINT UQ_Acc_Curr UNIQUE ([Account],[Currency])

Додавання перевірочного ключа у існуючу таблицю (CHECK)


Додавання constraint CHECK у таблиці відбувається таким чином:

ALTER TABLE table_name


ADD CONSTRAINT constraint_name CHECK (column_name IN
('n1','n2','n+'))

де,
ALTER – ключове слово для зміни об’єкту бази даних
TABLE – ключове слово, яке вказує, що даний об’єкт – Таблиця
table_name – ім’я таблиці, в яку додається CONSTRAINT
ADD – ключове слово, що відзначає додавання об’єкта бази даних
CONSTRAINT – об’єкт бази даних, який додається в таблицю table_name
constraint_name – назва CONSTRAINT, згідно правил найменувань, прописаних
CHECK – тип CONSTRAINT’а
column_name – ім’я колонки (поля) таблиці table_name, яке буде містити допустимий набір
значень з сегменту ('n1','n2','n+')
('n1','n2','n+') – сегмент, який містить набір допустимих значень, які можуть знаходитися у
колонці column_name.

Приклад:
--Додаємо ключ CHECK у таблиці Account на поле [Status]

ALTER TABLE [Account]


ADD CONSTRAINT CK_Account_Status CHECK ([Status] IN ('O', 'C'))
80

Видалення CONSTRAINT
Видалення будь-якого типу constraint робиться таким чином:

ALTER TABLE table_name


DROP CONSTRAINT constraint_name

де,
ALTER – ключове слово для зміни об’єкта бази даних
TABLE – ключове слово, яке вказую, що даний об’єкт – Таблиця
table_name – ім’я таблиці, з якої видаляється CONSTRAINT
DROP – ключове слово, що означає видалення об’єкта бази даних
CONSTRAINT – об’єкт бази даних, який видаляється з таблиці table_name
constraint_name – назва CONSTRAINT об’єкту який необхідно видалити

Приклад:
--Видаляємо CONSTRAINT FK_Account_Cust_Id з таблиці Account

ALTER TABLE Account


DROP CONSTRAINT [FK_Account_Cust_Id]
81

ТЕМА 5: Поєднання таблиць. Складні вибірки


даних з таблиць

Поєднання таблиць JOIN


Практично завжди доводиться робити вибірки не з однієї, а з декількох таблиць. Для цього
потрібно об’єднати дві або більше таблиць, які мають одне або кілька загальних полів.
Частіше за все кількість поєднань на 1 менше, ніж кількість поєднуваних таблиць. Але так буває
не завжди, все залежить від архітектури таблиць і ситуацій в яких необхідно виконувати
вибірку даних.
За допомогою JOIN створюється ланцюг поєднаних таблиць у загальну таблицю, після чого
можна до загальної таблиці застосувати додаткові умови фільтрації (WHERE, AND, OR) а також
різні оператори для уточнення пошуку даних.

Розрізнюють наступні види поєднання таблиць (на англ. JOIN):

Вид об’єднання Опис


Повертає всі рядки таблиці, у яких співпадають дані, в
INNER JOIN
обох таблицях.
Повертає всі рядки з лівої таблиці й рядки, що
LEFT JOIN
збіглися з правої таблиці.
Повертає всі рядки з правої таблиці й рядки, що
RIGHT JOIN
збіглися з лівої таблиці.
Повертає усі рядки, що збіглися і не збіглися, в обох
FULL JOIN
таблицях (існує не у всіх реляційних БД)
Повертає перехресні об’єднання усіх рядків однієї
CROSS JOIN
таблиці з другою.

Роздивимось приклади кожного з об’єднань на прикладі двох таблиць: Clients та Adress.


82

Таблиця Clients містить у собі 6 записів:

Таблиця Adress містить у собі 5 записів:

Так як даних в таблиці мало, то ми візуально можемо побачити, що в двох таблицях


співпадають записи з ідентифікатором ClientId - 1, 2 і 3.
А на практиці доведеться мати справу з таблицями в яких сотні тисяч записів. За таких об’ємів
даних неможливо візуально помітити співпадіння ідентифікаторів.
На практиці, різні види JOIN будуть допомагати відбирати саме ті дані, які треба знайти.
При реалізації об’єднання двох і більше таблиць необхідно розуміти, що об’єднані поля
повинні бути одного типу даних, а також повинні мати зв’язок у вигляді CONSTRAINT (FK
PK/UQ). Хоча, інколи приходиться пов’язувати таблиці, у яких фізично немає зв’язку. Тоді
необхідно розуміти як логічно це визначити і які поля необхідно об’єднати.

Загальний синтаксис для поєднань

SELECT column_name(s)
FROM table1
INNER|LEFT|RIGHT|FULL|CROSS JOIN table2
ON table1.column_name = table2.column_name
WHERE table1.column_name =,<>, >, IN value
83

де,
SELECT – ключове слово відповідає за вибірку даних
FROM – ключове слово, яке вказує на певну таблицю, з якої буде робитися вибірка даних
column_name(s) – ім’я колонок (полів)
table1 – ім’я першої таблиці
table2 – ім’я другої таблиці
INNER | LEFT | RIGHT | FULL JOIN – оператор, який поєднує дві таблиці
ON – умовний оператор, після якого вказується правило об’єднання таблиць по ключовим
полям
WHERE – ключове слово, за допомогою якого робиться фільтрація даних
value – значення, яке використовують для фільтрації

Об’єднання по INNER JOIN

Приклад
Запит

SELECT *
FROM Clients c
INNER JOIN Adress a ON c.ClientId = a.ClientId

Аналогічний варіант нижче, це також INNER JOIN. Зв’язок полів виконується після WHERE.

SELECT *
FROM Clients c, Adress a
WHERE c.ClientId = a.ClientId

Результат

Опис

Об’єднання INNER JOIN повертає усі рядки таблиць, у яких співпадають дані в обох таблицях.
Фактично, умовами зв’язку є предикат c.ClientId = a.ClientId, який фільтрує дані двох таблиць
таким чином, що виводить тільки співпадіння значень з таблиць.
84

--Приклад внутрішнього поєднання INNER JOIN трьох таблиць

SELECT *
FROM Clients c
INNER JOIN Adress a ON c.ClientId = a.ClientId
INNER JOIN Phones p ON c.ClientId = p.ClientId

Об’єднання по LEFT JOIN

Приклад
Запит

SELECT *
FROM Clients c
LEFT JOIN Adress a ON c.ClientId = a.ClientId

Результат

Опис

Об’єднання LEFT JOIN повертає усі рядки з лівої таблиці і рядки що співпали з правою
таблиці.
У даному випадку ліва таблиця Clients знаходиться зліва від LEFT JOIN. З неї буде
відбиратися абсолютно вся інформація в примусовому порядку, і та яка співпадає по
об’єднання c.ClientId = a.ClientId і та що не співпадає. А з правої таблиці Adress будуть
відібрані тільки ті рядки, які рівні по з’єднанню c.ClientId = a.ClientId. Для рядків з
правої таблиці Adress, які не рівні з’єднанню по всім відображеним колонкам буде
відображено значення NULL. Це значення повертає сама СУБД, фізично цих значень в таблиці
не існує. Таким чином СУБД нам підказує, що ось ці записи з лівої таблиці не мають відповідних
записів в правій таблиці.
85

--Приклад поєднання LEFT JOIN трьох таблиць

SELECT *
FROM Clients c
LEFT JOIN Phones p ON c.ClientId = p.ClientId
LEFT JOIN Adress a ON c.ClientId = a.ClientId

Об’єднання по RIGHT JOIN

Приклад
Запит

SELECT *
FROM Clients c
RIGHT JOIN Adress a ON c.ClientId = a.ClientId

Результат

Опис

Поєднання RIGHT JOIN повертає всі рядки з правої таблиці й співпавші рядки з лівої таблиці.
В даному випадку перша таблиця Adress знаходиться справа від RIGHT JOIN. З неї буде
відбиратися абсолютно вся інформація, й та, яка співпадає по об’єднанню c.ClientId =
a.ClientId й та, що не співпадає. А з лівої таблиці Clients будуть відібрані тільки ті рядки,
які рівні по об’єднанню c.ClientId = a.ClientId. Для рядків з лівої таблиці Clients, які
не рівні поєднанню, по усім відображеним колонкам буде відображено значення NULL.
Це значення повертає сама СУБД, фізично цих значень в таблиці не існує. Таким чином СУБД
нам підказує, що ось ці записи з правої таблиці не мають відповідних записів в лівій таблиці.

--Приклад поєднання RIGHT JOIN трьох таблиць

SELECT *
FROM Clients c
RIGHT JOIN Phones p ON c.ClientId = p.ClientId
RIGHT JOIN Adress a ON c.ClientId = a.ClientId
86

Об’єднання FULL JOIN

Приклад
Запит

SELECT *
FROM Clients c
FULL JOIN Adress a ON c.ClientId = a.ClientId

Результат

Опис

Об’єднання FULL JOIN повертає всі записи, які співпали і не співпали з обох таблиць.
У даному випадку з лівої таблиці Clients і правої таблиці Adress відібрані рядки у яких
значення співпадають за об’єднанням c.ClientId = a.ClientId.
А також в лівій таблиці Clients відображені значення для яких нема подібних значень у
правій таблиці Adress, і навпаки, в Adress відображені ті значення для яких нема подібних
значень в Clients. Для не знайдених поєднань у протилежній таблиці в колонках буде
відображатися значення NULL.

--Приклад поєднання FULL JOIN трьох таблиць

SELECT *
FROM Clients c
FULL JOIN Adress a ON c.ClientId = a.ClientId
FULL JOIN Phones p ON c.ClientId = p.ClientId
87

Об’єднання CROSS JOIN

Синтаксис для CROSS JOIN відмінний лише тим, що не потрібно писати конструкцію
об’єднання полів ON table1.column_name = table2.column_name

SELECT column_name(s)
FROM table1
CROSS JOIN table2
Або

SELECT column_name(s)
FROM table1, table2

Приклад
Запит

SELECT *
FROM Clients c
CROSS JOIN Adress a

Аналогічний варіант з іншою конструкцією CROSS JOIN. Зв’язок полів не відбувається. Але цей
варіант частіш за все використовується помилково.

SELECT *
FROM Clients c, Adress a

Результат
88

Опис

Об’єднання CROSS JOIN повертає перехресне поєднання усіх рядків однієї таблиці до другої.
Тобто, на кожен рядок однієї таблиці будуть відноситись всі рядки другої таблиці, і так для
кожного рядка. Таким чином, кількість рядків збільшується математично: 6 * 5 = 30 записів.
Даний тип поєднання використовується дуже рідко, але на основі нього слід розуміти, чим
більше рядків в таблицях, тим довше буде виконуватися створення всіх можливих комбінацій
у результуючій вибірці.
Такий результат вибірки часто називають Декартовим добутком.
Декартовий добуток двох множин — це множини, елементами яких являються всі можливі
упорядковані пари елементів початкових множин.

З поєднанням таблиць необхідно бути дуже уважними. Завжди покроково аналізувати


отримані дані й перевіряти тип об’єднання. Якщо між таблицями не буде встановлено зв’язку,
то виникне Декартовий добуток. Чим більше рядків в таблицях, тим довше будуть
генеруватися комбінації значень, що в свою чергу буде серйозно навантажувати фізичний
сервер БД. У результаті вибірка буде некоректна.
CROSS JOIN все ж використовується на практиці (рідко), як правило, для невеликих масивів
даних, коли потрібно згенерувати усі комбінації й обернути їх у циклі чи як вкладений запит.
89

Складні вибірки даних з таблиць


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

Підзапит може знаходитися у трьох місцях основного запиту:


● у секції SELECT
● у секції FROM
● у секції WHERE

Підзапит у секції SELECT

Приклад
Запит

SELECT ug.UserGroupId,
ug.Name AS [UserGroupName],
ug.CourseId,
(SELECT c.Title FROM Course c WHERE c.CourseId = ug.CourseId) AS
[CourseTitle]
FROM UserGroup ug

Результат

Опис

Підзапит (c.Title FROM Course c WHERE c.CourseId = ug.CourseId), що знаходиться


у секції SELECT має внутрішнє поєднання з таблицею UserGroup (c.CourseId =
ug.CourseId). Це забезпечує вивід тільки тих рядків основного запиту з таблиці UserGroup,
у яких ідентифікатори будуть рівні один одному.
Якщо не забезпечити зв’язок між таблицями, то отримаємо меседж такого формату:
Subquery returned more than 1 value. This is not permitted when the
subquery follows =, !=, <, <= , >, >= or when the subquery is used as an
expression.
90

Тобто, підзапит повернув більш ніж одне значення для кожного рядка основного запиту. Такий
синтаксис недопустимий.
В даному випадку підзапит повертає нам додаткову колонку CourseTitle, яка показує на
який курс йде навчатись вся група.

Підзапит в секції FROM

Приклад
Запит

SELECT ug.UserGroupId,
ug.Name AS [UserGroupName],
ug.CourseId,
c.Title AS [CourseTitle]
FROM UserGroup ug,
(SELECT * FROM Course WHERE [Status] = 'A') AS c
WHERE ug.CourseId = c.CourseId

Результат

Опис

Підзапит (* FROM Course WHERE [Status] = 'A'), знаходиться у секції FROM повертає
набір існуючих курсів, у яких [Status] = 'A'. На цей підзапит дивимось як на таблицю, бо
він й справді повертає таблицю даних.

Але необхідно відібрати групи, які відвідують дані курси і у той самий час вивести назву курсу
CourseTitle. Для коректної вибірки здійснюється зв’язок UserGroup з цілим підзапитом
(SELECT * FROM Course WHERE [Status] = 'A'), який у даному випадку виступає свого
роду таблиці. Необхідно підзапиту привласнити аліас і через аліас зв’язати ключові поля двох
таблиць ug.CourseId = c.CourseId.
Без зв’язку буде порушення синтаксису загального запиту.
У підсумку будуть виведені тільки ті групи, які відвідують існуючі курси, тобто активні курси.
91

Підзапит в секції WHERE

Приклад
Запит

SELECT *
FROM Course c
WHERE c.CourseId IN (SELECT ug.CourseId FROM UserGroup ug)

Результат

Опис

Підзапит (SELECT ug.CourseId FROM UserGroup ug), що знаходиться у секції WHERE


повертає набір ідентифікаторів існуючих груп.

Саме цей набір ідентифікаторів існуючих груп буде передаватися у зовнішній запит, і у
випадку, якщо у зовнішньому запиті по поля c.CourseId буде знайдено хоча б одне з
переданих ідентифікаторів, то будуть виведені рядки з таблиці Course, де ідентифікатори
співпадають.
Так як підзапит повертає кілька значень, то необхідно використовувати оператор IN ().
В цьому випадку підзапит грає роль умови.
92

ДЯКУЮ

You might also like