SQL
SQL⚑
Що таке SQL⚑
SQL (Structured Query Language) — це мова для роботи з реляційними базами даних, що використовується для створення, модифікації, управління та отримання даних. Вона є стандартом для більшості сучасних реляційних СУБД, таких як MySQL, PostgreSQL, SQLite та інші.
- SQL дозволяє створювати структуру бази даних через команди створення таблиць, індексів, та зв’язків між таблицями.
- Основні операції SQL включають
SELECT
для вибірки даних,INSERT
для додавання,UPDATE
для оновлення таDELETE
для видалення записів. - SQL підтримує складні запити за допомогою умов
WHERE
, сортуванняORDER BY
, агрегації данихGROUP BY
, а також функцій для роботи з підзапитами та об'єднання таблиць (JOIN
). - Мова SQL є декларативною, тобто користувач описує, що він хоче отримати або змінити, а не як саме це потрібно зробити.
- У сучасних застосунках SQL часто використовується через ORM (Object-Relational Mapping) інструменти, такі як SQLAlchemy для Python, що дозволяє взаємодіяти з базою даних через об'єкти класів замість прямого написання SQL-коду.
Приклад базового запиту SQL для вибірки всіх користувачів з бази даних:
Оператори SELECT
, INSERT
, UPDATE
та DELETE
⚑
Призначення операторів
SELECT
для вибірки данихINSERT
для додавання записівUPDATE
для оновлення записівDELETE
для видалення записів
Оператор SELECT
використовується для вилучення даних з бази даних. Він дозволяє вибирати певні стовпці з таблиці, фільтрувати дані за умовами, сортувати, групувати і виконувати інші операції над даними.
Оператор INSERT
використовується для вставки нових даних у таблицю. Він дозволяє вказувати значення для стовпців або вставляти значення з іншої таблиці.
Оператор UPDATE
використовується для зміни даних у таблиці. Він дозволяє оновлювати значення стовпців у певних рядках таблиці. Можна вказувати умови для фільтрації даних, які потрібно оновити.
Оператор DELETE
використовується для видалення даних з таблиці. Він дозволяє видаляти певні рядки таблиці на основі умов.
SQL Constraints⚑
Обмеження (Constraints) використовуються для визначення правил для даних у таблиці.
Обмеження використовуються для обмеження типу даних, які можуть входити в таблицю. Це забезпечує точність і достовірність даних у таблиці. Якщо існує будь-який конфлікт між обмеженням і дією з даними, дія переривається.
Обмеження можуть бути на рівні стовпця або таблиці. Обмеження рівня стовпця застосовуються до стовпця, а обмеження рівня таблиці застосовуються до всієї таблиці.
Перелік обмежень
NOT NULL
- гарантує, що стовпець не може мати значенняNULL
UNIQUE
– гарантує, що всі значення в стовпці відрізняютьсяPRIMARY KEY
- комбінаціяNOT NULL
іUNIQUE
. Унікально визначає кожен рядок у таблиціFOREIGN KEY
- однозначно ідентифікує рядок/запис в іншій таблиціCHECK
- гарантує, що всі значення в стовпці задовольняють певну умову
PK - Primary Key, FK -Foreign Key, UNIQUE - Unique constraint⚑
Primary Key (Первинний Ключ) - це унікальний ідентифікатор (стовпець або комбінація стовпців), який визначає конкретний запис в базі даних. Він гарантує унікальність кожного запису в таблиці і дозволяє ефективно виконувати пошук та звертатися до конкретних даних. Кожна таблиця може мати лише один первинний ключ.
Foreign Key (Зовнішній Ключ) - це поле в таблиці бази даних, яке вказує на Primary Key іншої таблиці. Він встановлює зв'язок між двома таблицями, дозволяючи одній таблиці посилатися на дані в іншій. Це використовується для створення зв'язків між таблицями та забезпечення цілісності даних в базі даних. Таблиця може мати кілька зовнішніх ключів, що вказують на її зв'язок із різними таблицями.
UNIQUE
– обмеження гарантує унікальність значень - всі значення стовпця відрізняються один від одного. Обмеження первинного ключа має автоматичне обмеження на унікальність. В таблиці можна мати багато унікальних обмежень, але тільки одне обмеження первинного ключа таблиці.
CREATE TABLE users (
id int NOT NULL,
email varchar(255) UNIQUE, -- all the emails shoud be unique
CONSTRAINT pk_user PRIMARY KEY (id)
);
Оператор та види JOIN
⚑
Оператор JOIN
в SQL використовується для об'єднання даних з двох або більше таблиць на основі визначених умов. JOIN
дозволяє комбінувати дані з різних таблиць в один результат.
Умова з'єднання вказується в реченні ON
. Ця умова визначає, які рядки двох вихідних таблиць вважаються "відповідними" один одному.
Слова INNER
та OUTER
не є обов'язковими у всіх формах. За замовчуванням передбачається INNER(внутрішнє з'єднання), а при вказанні
LEFT
, RIGHT
і FULL
- зовнішнє з'єднання.
Види джойнів
JOIN
(INNER JOIN
)LEFT JOIN
(LEFT OUTER JOIN
)RIGHT JOIN
(RIGHT OUTER JOIN
)FULL JOIN
(FULL OUTER JOIN
)-
CROSS JOIN
-
INNER JOIN
- Повертає ті рядки, які мають відповідні значення в обох таблицях.
SELECT Orders.order_id, Customers.customer_name
FROM Orders
JOIN Customers ON Orders.customer_id = Customers.customer_id;
LEFT OUTER JOIN
- Повертає всі рядки з лівої (першої) таблиці та відповідні значення з правої (другої) таблиці. Якщо відповідних значень немає, повертаєтьсяNULL
.
SELECT Customers.customer_name, Orders.order_id
FROM Customers
LEFT JOIN Orders ON Customers.customer_id = Orders.customer_id;
RIGHT OUTER JOIN
- Повертає всі рядки з правої (другої) таблиці та відповідні значення з лівої (першої) таблиці. Якщо відповідних значень немає, повертаєтьсяNULL
. Це з'єднання є протилежним до лівого (LEFT JOIN
).
SELECT Customers.customer_name, Orders.order_id
FROM Customers
RIGHT JOIN Orders ON Customers.customer_id = Orders.customer_id;
FULL OUTER JOIN
- Повертає всі рядки з обох таблиць, і якщо відповідних значень немає, повертаєтьсяNULL
. Фактично це одночаснийLEFT
таRIGHT JOIN
.
SELECT Customers.customer_name, Orders.order_id
FROM Customers
FULL JOIN Orders ON Customers.customer_id = Orders.customer_id;
CROSS JOIN
- Повертає декартовий добуток (комбінацію) всіх рядків із першої таблиці та всіх рядків із другої таблиці.
Латеральний JOIN, (LATERAL JOIN
) дозволяє використовувати результати попереднього запиту як джерело даних для подальших операцій JOIN, що дозволяє створювати більш складні і гнучкі запити.
Основна властивість латерального JOIN полягає в тому, що він дозволяє включати підзапити, які "бачать" дані з рядків, отриманих від зовнішніх таблиць. Це дозволяє використовувати значення з поточного рядка як параметр для підзапиту, що в свою чергу дозволяє гнучко маніпулювати даними.
SELECT e.employee_name, t.task_name
FROM employees e
LEFT JOIN LATERAL (
SELECT task_name
FROM tasks
WHERE tasks.employee_id = e.employee_id
LIMIT 5 -- Limit the number of tasks per employee, for example
) t ON true;
Підзапити⚑
Підзапити SQL - це запити, вкладені всередині інших запитів.
Підзапити можуть бути використані для створення складних і гнучких запитів, які вимагають більш складної логіки або операцій з даними. Підзапити можуть використовуватися в різних частинах SQL-запиту, таких як SELECT
, FROM
, WHERE
, HAVING
та інших.
Підзапити можуть бути менш ефективними з точки зору продуктивності, особливо під час роботи з великими обсягами даних. Вони можуть призвести до підвищеного часу виконання запитів та навантаження на базу даних. В таких випадках можна спробувати замінити підзапит на JOIN
.
Підзапит в операторі SELECT
SELECT product_name, (SELECT AVG(price) FROM prices WHERE product_id = products.id) as avg_price
FROM products;
Підзапит в операторі FROM
SELECT product_name, price
FROM products
JOIN (SELECT product_id, AVG(price) as avg_price FROM prices GROUP BY product_id) as subquery
ON products.id = subquery.product_id
WHERE price > subquery.avg_price;
Підзапит в операторі WHERE
SELECT order_id, customer_id
FROM orders
WHERE customer_id IN (SELECT customer_id FROM customers WHERE customer_type = 'premium');
Агрегатні функції і GROUP BY
⚑
Агрегатні функції SQL використовуються для обчислення агрегатних значень на основі даних в стовпцях таблиці.
Основні агрегатні функції SQL
COUNT
SUM
AVG
MIN
MAX
COUNT
Повертає кількість рядків або значень у стовпці. Може використовуватися з модифікатором DISTINCT
для розрахунку унікальних значень.
SUM
Повертає суму значень у стовпці. Може використовуватись лише з числовими стовпцями.
AVG
Повертає середнє значення значень у стовпці. Може використовуватись лише з числовими стовпцями.
MIN
Повертає мінімальне значення у стовпці. Може використовуватись з будь-яким типом даних.
MAX
Повертає максимальне значення у стовпці. Може використовуватись з будь-яким типом даних.
Оператори GROUP BY, HAVING використовуються спільно з агрегатними функціями для виконання агрегації на рівні груп або для створення зведених звітів.
GROUP BY
Використовується для угруповання результатів по одному або декільком стовпцям.
HAVING
Використовується для фільтрації груп після застосування GROUP BY на основі умов, заданих у HAVING.
У чому різниця між операторами HAVING
та WHERE
?⚑
Оператор HAVING
використовується у зв'язці з GROUP BY
, щоб фільтрувати рядки на основі агрегатних функцій. У свою чергу оператор WHERE
фільтрує рядки перед групуванням. Простіше кажучи WHERE
використовується для звуження пошуку на рівні рядків, а HAVING
вибирає конкретні результати після групування.
SELECT department, COUNT(*) AS total_employees
FROM employees
GROUP BY department
HAVING total_employees > 10;
UNION
та UNION ALL
⚑
UNION
та UNION ALL
використовуються для об’єднання результатів кількох запитів у один набір даних. - UNION
видаляє дублікати зі з'єднаного набору результатів. Використовується, коли потрібен унікальний список значень, при цьому виконує додаткову перевірку для усунення дублікатів, що може уповільнити виконання запиту на великих наборах даних. - UNION ALL
зберігає всі записи, включаючи дублікати.
Обидва оператори вимагають, щоб кількість і типи стовпців у всіх запитах, що об'єднуються, збігалися.
-- Union example, duplicates will be removed
SELECT column_name FROM table1
UNION
SELECT column_name FROM table2;
-- Union All example, duplicates will be kept
SELECT column_name FROM table1
UNION ALL
SELECT column_name FROM table2;
Функції в SQL⚑
Рядкові функції
CONCAT()
- Об'єднання двох або більше рядків.
SELECT CONCAT('Hello', 'World');
-- Результат: 'HelloWorld'
LENGTH()
- Повертає довжину рядка.
SELECT LENGTH('Hello');
-- Результат: 5
UPPER()
- Перетворення рядка у верхній регістр.
SELECT UPPER('hello');
-- Результат: 'HELLO'
LOWER()
- Перетворення рядка на нижній регістр.
SELECT LOWER('WORLD');
-- Результат: 'world'
SUBSTRING()
- Вилучення підрядка з рядка.
SELECT SUBSTRING('HelloWorld', 6, 5);
-- Результат: 'World'
Математичні функції
ABS()
- Повертає абсолютне значення числа.
SELECT ABS(-5);
-- Результат: 5
ROUND()
- Округлення числа до заданої кількості знаків після коми.
SELECT ROUND (3.14159, 2);
-- Результат: 3.14
CEILING()
- Округлення числа у велику сторону до найближчого цілого.
SELECT CEILING (3.2);
-- Результат: 4
FLOOR()
- Округлення числа в меншу сторону до найближчого цілого.
SELECT FLOOR(3.8);
-- Результат: 3
RAND()
- Генерація випадкового числа.
SELECT RAND();
-- Результат: випадкове число від 0 до 1
Інші функції
COALESCE()
- Повертає перше ненульове значення зі списку значень.
SELECT COALESCE(column1, column2, column3, 'N/A') FROM table1;
NULLIF()
- Повертає NULL, якщо два значення дорівнюють.
SELECT NULLIF(column1, 0) FROM table1;
CASE()
- Виконує умовну логіку SQL запиту.
SELECT column1, CASE WHEN column1 > 0 THEN 'Positive' ELSE 'Negative' END FROM table1;
UPSERT
⚑
UPSERT
(update or insert) у SQL — це операція, яка дозволяє вставляти нові рядки у таблицю або оновлювати існуючі, якщо такі вже присутні. Це корисно для забезпечення цілісності даних та уникнення дублювання записів. Залежно від бази даних, синтаксис UPSERT може відрізнятися.
- У PostgreSQL UPSERT реалізується через
INSERT ... ON CONFLICT
. Ви вказуєте, що робити у разі конфлікту (наприклад, оновити дані). - У MySQL використовують
INSERT ... ON DUPLICATE KEY UPDATE
. Це оновлює рядок, якщо ключ уже існує. - У SQLite UPSERT доступний через
INSERT ... ON CONFLICT
. - У Oracle використовують
MERGE
для реалізації UPSERT-поведінки.
Приклад для PostgreSQL
INSERT INTO users (id, name, email)
VALUES (1, 'John Doe', 'john.doe@example.com')
ON CONFLICT (id) -- identifies the field causing the conflict
DO UPDATE SET
name = EXCLUDED.name,
email = EXCLUDED.email;
-- "EXCLUDED" is a special reference to the data being attempted to be inserted
Цей запит вставить новий запис, якщо id
не існує, або оновить name
та email
, якщо запис із таким id
уже присутній.
Що робить SELECT FOR UPDATE?⚑
SELECT FOR UPDATE
– це SQL-конструкція, яка використовується для блокування рядків, що вибираються під час виконання запиту, щоб уникнути конкурентних змін даних. Ця конструкція зазвичай застосовується в транзакціях для забезпечення консистентності даних.
Вона виконує ексклюзивне блокування рядків, які відповідають умовам запиту, і дозволяє тільки одному процесу або транзакції читати або змінювати ці рядки. Інші транзакції, що спробують виконати SELECT FOR UPDATE
для цих же рядків, будуть чекати, поки поточна транзакція завершиться (коміт або ролбек).
Зазвичай використовується, коли є потреба прочитати дані, провести певні обчислення та потім оновити ці дані без ризику, що інша транзакція зможе змінити їх між читанням і записом. Це важливо для уникнення проблем, таких як race conditions.
Недоліком є те, що використання SELECT FOR UPDATE
може призвести до зменшення швидкодії за рахунок блокування ресурсів та можливих deadlock-ів. Тому його треба застосовувати з обережністю в системах з високою конкуренцією запитів.
BEGIN;
-- Selecting rows for update
SELECT * FROM orders
WHERE status = 'pending'
FOR UPDATE;
-- Performing update based on previous select
UPDATE orders
SET status = 'processing'
WHERE status = 'pending';
COMMIT;
Що таке віконні функції і як вони працюють?⚑
Віконні функції - це засіб в SQL, який дозволяє виконувати обчислення на підмножині рядків з результатами запиту і керувати їх порядком та групуванням. Вони корисні для виконання аналізу даних на рівні рядків і дозволяють враховувати взаємозв'язані дані при обчисленнях.
У PostgreSQL можна використовувати віконні функції, використовуючи ключове слово OVER
. Наприклад, давайте припустимо, що у нас є таблиця orders
зі стовпцями order_date
, customer_id
і order_amount
. Ми можемо використовувати віконну функцію SUM
для обчислення кумулятивної суми замовлень для кожного клієнта впродовж часу.
SELECT
order_date,
customer_id,
order_amount,
SUM(order_amount) OVER (PARTITION BY customer_id ORDER BY order_date) AS cumulative_order_amount
FROM
orders;
У цьому запиті ми використовуємо функцію SUM(order_amount) OVER (PARTITION BY customer_id ORDER BY order_date)
. Частина PARTITION BY customer_id
розділяє дані на групи за customer_id
, а ORDER BY order_date
визначає порядок сортування за датою замовлення.
Таким чином, для кожного рядка ми отримаємо значення кумулятивної суми суми замовлень для відповідного клієнта на даний момент.
Досягти того ж результату, що і у вище наведеному прикладі, можна використовуючи GROUP BY
.
SELECT
order_date,
customer_id,
order_amount,
SUM(order_amount) AS cumulative_order_amount
FROM
orders
GROUP BY
customer_id, order_date, order_amount
ORDER BY
customer_id, order_date;
Обидва запити дають однаковий результат - кумулятивну суму сум замовлень для кожного клієнта на основі дати замовлення. Однак, використання віконних функцій дозволяє уникнути необхідності вказувати всі стовпці з запиту в розділі GROUP BY
. Використання OVER
і PARTITION BY
дозволяє більш гнучко визначити, як розподілити дані для обчислень, і отримати більш зрозумілий і компактний код.
В більшості випадків використання віконних функцій є більш ефективним та швидким способом порівняно з використанням GROUP BY
, особливо при роботі з великими наборами даних. Віконні функції дозволяють виконувати обчислення на підмножині рядків без потреби створювати окремі групи. Це може призвести до кращої продуктивності, оскільки система управління базами даних може оптимізувати розрахунки в межах віконного фрейму.
При використанні підходу GROUP BY
база даних має створювати відмінні групи на основі стовпців групування, що може включати додаткові кроки сортування та агрегації. Цей процес може стати більш витратним з точки зору ресурсів зі збільшенням обсягу даних.
Агрегатні віконні функції ROW_NUMBER
, RANK
, DENSE_RANK
⚑
Агрегатні віконні функції SQL дозволяють агрегувати дані всередині певного вікна або діапазону рядків, визначеного за допомогою віконного виразу. Віконні функції можуть бути корисними для аналізу даних, таких як ранжування, обчислення відмінностей між значеннями, знаходження першого або останнього значення всередині вікна та інших аналітичних операцій.
Віконні функції
ROW_NUMBER
RANK
DENSE_RANK
ROW_NUMBER()
Повертає порядковий номер рядка всередині вікна. Нумерація починається з першої.
Наступний запит обирає ідентифікатор, ім'я та оцінку студентів з таблиці "students" та призначає їм порядкові номери всередині вікна, відсортованого за зменшенням оцінок.
RANK()
Повертає ранг (порядковий номер) рядка всередині вікна, пропускаючи дублікати, та пропускаючи наступний порядковий номер у разі, якщо кілька рядків мають одне й те саме значення.
Наступний запит обирає ідентифікатор, ім'я та оцінку студентів з таблиці "students" і призначає їм ранги всередині вікна, відсортованого за спаданням оцінок, пропускаючи дублікати оцінок.
DENSE_RANK()
Повертає щільний ранг (порядковий номер) рядка всередині вікна, не пропускаючи дублікати, і не пропускаючи наступний порядковий номер у випадку, якщо кілька рядків мають те саме значення.
Наступний запит обирає ідентифікатор, ім'я та оцінку студентів з таблиці "students" та призначає їм щільні ранги всередині вікна, відсортованого за спаданням оцінок, без пропуску дублікатів оцінок.
sqlCopy code
SELECT id, name, score, DENSE_RANK() OVER (ORDER BY score DESC) AS dense_rank_num
FROM students
SQL ін'єкції - що таке, як захиститись?⚑
SQL-ін'єкції — це один із найпоширеніших типів атак на базу даних, коли зловмисник вставляє шкідливий SQL-код у запит, що виконується сервером. Це може призвести до витоку даних, модифікації бази або навіть повного видалення даних.
Основна причина SQL-ін'єкцій — неправильна обробка даних, які надходять від користувача, та їх безпосереднє використання у SQL-запитах. Наслідки атак можуть бути дуже серйозними: викрадення конфіденційної інформації, несанкціоновані зміни даних, створення облікових записів з підвищеними привілеями тощо.
Приклад небезпечного коду
user_input = "1; DROP TABLE users;" # Malicious input
query = f"SELECT * FROM users WHERE id = {user_input};"
cursor.execute(query) # This executes malicious SQL code
Методи захисту: - Використання параметризованих запитів або підготовлених виразів (prepared statements). Це дозволяє відокремити дані від SQL-логіки.
user_input = "1; DROP TABLE users;" # This will now be treated as plain data
query = "SELECT * FROM users WHERE id = %s;"
cursor.execute(query, (user_input,)) # Using parameterized query to prevent SQL injection
- Застосування ORM (наприклад, Django ORM або SQLAlchemy), яке автоматично обробляє вхідні дані.
- Валідація та фільтрація вхідних даних. Обмеження типів даних (наприклад, числові значення).
- Мінімізація прав користувачів бази даних. Акаунт, через який додаток підключається до бази, повинен мати лише необхідні привілеї.
- Використання веб-фаєрволів (WAF) для моніторингу та блокування шкідливих запитів.
- Логування та моніторинг запитів допоможуть вчасно виявити підозрілу активність.
View, Materialised View⚑
У SQL View (представлення) та Materialized View (матеріалізоване представлення) — це способи роботи з даними, що полегшують доступ до складних запитів або великих наборів даних. Вони мають схожість, але виконують різні завдання.
Ключові відмінності
- View завжди показує актуальні дані, тоді як Materialized View може відображати застарілі дані до моменту рефрешу.
- View не займає додаткового місця в пам’яті, а Materialized View зберігає результати фізично.
- Materialized View краще підходить для великих обсягів даних, якщо запит часто повторюється і важливіша швидкість, ніж актуальність.
View
- View — це віртуальна таблиця, яка базується на результаті SQL-запиту. Дані у View завжди актуальні, оскільки витягуються з базової таблиці під час виконання запиту.
- View не зберігає дані фізично, а лише визначення запиту.
- Використовується для спрощення складних запитів, підвищення безпеки (обмеження доступу до певних колонок або рядків) та створення абстракцій над базою даних.
- Оновлення даних через View можливе, якщо виконуються певні умови (наприклад, відсутність агрегатів у запиті).
CREATE VIEW active_users AS
SELECT id, name, email
FROM users
WHERE is_active = true;
-- Запит до View
SELECT * FROM active_users;
Materialized View
- Materialized View — це таблиця, яка зберігає результат SQL-запиту фізично. Дані в Materialized View не оновлюються автоматично; потрібно виконувати рефреш для їх оновлення.
- Використовується для покращення продуктивності, особливо для часто виконуваних складних запитів або агрегатів на великих наборах даних.
- Рефреш може бути виконаний вручну або автоматично залежно від налаштувань.
- Materialized View може мати індекси для прискорення запитів.
CREATE MATERIALIZED VIEW monthly_sales AS
SELECT product_id, SUM(sales) AS total_sales
FROM sales
GROUP BY product_id;
REFRESH MATERIALIZED VIEW monthly_sales; -- Refresh Materialized View
SELECT * FROM monthly_sales; -- Query Materialized View
EXPLAIN
та EXPLAIN ANALYZE
⚑
EXPLAIN
- це команда, яка надає інформацію про план виконання запиту. Вона показує, як база даних планує виконати запит, включаючи індекси, порядок виконання операцій та очікувані витрати, що допомагає в оптимізації запитів. Сам запит у разі не виконується.
EXPLAIN ANALYZE
виконує пояснюваний вираз, навіть якщо це insert, update або delete.
Приклад використання команди EXPLAIN
EXPLAIN SELECT * FROM employees WHERE age > 30;
Seq Scan on employees (cost=0.00..12.50 rows=3 width=100)
Filter: (age > 30)
База даних буде виконувати послідовне сканування (Seq Scan) таблиці employees і застосовувати фільтр age > 30. Поле cost показує передбачувані витрати виконання операції термінах часу і ресурсів.
EXPLAIN
допомагає виявити потенційні проблеми у запитах, такі як відсутність індексного сканування, що може вказувати на необхідність додавання індексів або переписування запитів для покращення продуктивності.
Використання EXPLAIN
особливо корисне при роботі зі складними запитами або великими обсягами даних, оскільки дозволяє заздалегідь зрозуміти, як виконуватиметься запит, і які оптимізації можуть знадобитися.