SQL
База данных - средство хранения данных, в котором данные находятся в структурированном и легкодоступном виде.
Есть реляционные и нереляционные БД.
Реляционные БД (SQL)
(От англ. “relation” – связь.)
MySQL
PostgreSQL
MsSQL (MicrosoftSQL)
SQLite
OracleDB
Терминология
- Реляционные = SQL
- Дамп БД – Файл, в который выгружена структура и данные из базы и который можно загрузить в другую базу, что приведёт к полному восстановлению данных
- Записи - строки
- Поля – столбцы
Значение NULL
Специальное значение, которое может быть в поле любого типа, если оно не помечено как NOT NULL
Язык запросов SQL
Structured Query Language – язык структурированных запросов.
Язык, позволяющий работать с SQL DB, в том числе добавлять в них данные, получать их, изменять и удалять их, а также управлять структурой самой БД – структурой таблиц, полей и связей между ними.
Дамп базы данных
Файл, в который выгружена структура и данные из базы и который можно загрузить в другую базу, что приведёт к полному восстановлению данных. Там по сути SQL запросы на создание базы, таблиц, добавление данных и т.п.
Нереляционные БД (NoSQL)
Не используют SQL для запросов и т.д.
Форматы хранения данных в NoSQL-базах:
- Пары “ключ - значение”
Redis
Memcached
AmazonDynamoDB - JSON-документы
mongoDB
CouchDB
elasticsearch - Семейства столбцов
- Графы и т.д.
На этом с NoSQL пока всё. Дальше идет только SQL
Типы полей (данных) в SQL DB
Числовые данные
Тип | Мин. значение | Макс. значение |
---|---|---|
TINYINT | $-128$ | $127$ |
INT | $-2147483647$ | $2147483647$ |
BIGINT | $-2^{63}$ | $-2^{63}-1$ |
FLOAT | $1.17*10^{-38}$ | $3.4*10^{38}$ |
DOUBLE | $2.2*10^{-308}$ | $1.8*10^{308}$ |
Данные о дате и времени
Типа | Формат | Годы (от и до) |
---|---|---|
DATE | YYYY-MM-DD | 1000 - 9999 |
DATETIME | YYYY-MM-DD HH:MM:SS | 1000 - 9999 |
TIMESTAMP | YYYY-MM-DD HH:MM:SS | 1970 - 2038-01-19 |
Строковые данные
Тип | Максимальная длина |
---|---|
CHAR(N) | 255 |
VARCHAR(N) | 255 / 65535 |
TINYTEXT | 255 |
TEXT | 65535 |
MEDIUMTEXT | 16777215 (16Mb) |
LONGTEXT | 4 Gb |
Пространственные данные
Представляют сведения о физическом расположении и форме геометрических объектов. Этими объектами могут быть точки расположения или более сложные объекты, такие как страны/регионы, дороги или озера.
JSON-данные
JSON 🤷♂️
Типы связей в SQL DB
Связь типа one-to-many (один ко многим)
Либо в одной таблице:
Связь типа one-to-one (один к одному)
Редкая достаточно штука:
Связь типа many-to-many (многие ко многим)
Таблица book2order
используется для связывания таблиц book
и order
.
SELECT-запросы
Синтаксис на простом примере
Вывод отдельных полей таблицы
Изменение имён полей в запросах
Запись ниже эквивалента верхней - можно и не использовать AS
.
Изменение данных в SELECT-запросах
Фильтрация данных
Равенство числу
Числа больше или меньше
Строка и неравенство
Даты
Тип | Формат | Пример |
---|---|---|
Дата | YYYY-MM-DD | 2021-01-30 |
Дата и время | YYYY-MM-DD HH:MM:SS | 2021-01-30 22:45:07 |
Диапазон дат и времени
Оператор WHERE и сложные условия
Оператор LIKE
Маски:
Маска | Значение |
---|---|
% | 0 или более символов |
_ | один символ |
id | parent_id | name |
---|---|---|
1 | NULL | Чай |
6 | 1 | Белый чай |
7 | 1 | Ароматизированные чай |
8 | 1 | Черный чай |
9 | 1 | Зеленый чай |
12 | 1 | Чай для детей |
13 | 1 | Связанные чай |
15 | 1 | Чай в шелке |
Сравнение с NULL
id | parent_id |
---|---|
1 | -10 |
2 | 0 |
3 | NULL |
4 | 35 |
Результат:
id | parent_id |
---|---|
3 | NULL |
Результат:
id | parent_id |
---|---|
1 | -10 |
2 | 0 |
4 | 35 |
Объединение нескольких условий
Примеры:
Условие | Пример |
---|---|
LIKE '% чай %' | Зелёный чай с мятой |
LIKE '% чай' | Белый чай Зеленый чай NOT Молочай |
LIKE 'чай %' | Чай с вишней NOT Чайник |
Одно из условий (оператор OR)
Группа значений (оператор IN)
Разные условия (c круглыми скобками)
Сортировка и ограничение кол-ва результатов
Сортировка по возрастанию (два эквивалентных запроса)
Сортировка по убыванию
Множественная сортировка
Ограничение количества записей
Запись с наибольшим значением
LIMIT со сдвигом
Оператор LIMIT
Сдвиг 10
Запятая ,
Кол-во элементов 20
В SQL счет начинается с 1, а не с 0, как в ЯП.
Объединение таблиц, JOIN-ы
По сути JOIN присоединяет две таблицы в одну, это можно наглядно увидеть если SELECT-ить всё (*)
При присоединении нужно прописать точки присоединения после оператора ON
Результат:
name | name |
---|---|
Дарджилинг | Дарджилинг Джиель |
Айс Ти | Айс Ти “Инжирный персик” |
Айс Ти | Айс Ти “Черничный Прованс” |
Айс Ти | Айс Ти “Сочное манго” |
Айс Ти | Айс Ти “Клубничный зефир” |
Айс Ти | Айс Ти “Апельсиновый лед” |
Айс Ти | Айс Ти “Карамельное яблоко” |
Айс Ти | Айс Ти “Инжирный персик” |
Айс Ти | Айс Ти “Черничный Прованс” |
Айс Ти | Айс Ти “Сочное манго” |
Айс Ти | Айс Ти “Клубничный зефир” |
И в SELECT-ах лучше давать имена полям. Можно и таблицам давать алиасы, это хорошая практика. Вот хороший пример:
Типы объединений
Это основные JOIN-ы.
INNER JOIN стоит по дефолту.
LEFT JOIN - это абсолютно всё из левой таблицы, плюс то, что нашлось в правой (то, что удовлетворяет выражению ON). Если не нашлось в правой, то напротив записи из левой будет NULL
RIGHT JOIN - наоборот
FULL JOIN - всё вместе
JOIN-ы пишутся прямо так, как и называются. Можно писать и INNER JOIN вместо обычного. А с LEFT/RIGHT JOIN-ами нужно писать полностью, естественно.
Фильтрация по уникальности и группировка записей
Синтаксис запроса
Выведет только уникальные записи в поле status_id
Уникальность по нескольким полям
Тоже самое применяется на несколько полей.
Troubleshoot
В MySQL CLI есть проблема с ONLY_FULL_GROUP_BY, с которым не работает оператор GROUP BY. Чтобы отключить его, нужно ввести следующий запрос:
И перезайти в mysql. Ответ нашел на stackoverflow по запросу “How to disable ONLY_FULL_GROUP_BY. Ответ на SO
Группировка результатов
Выведет уникальные src_status_id
и все dst_status_id
которые есть у первого.
src_status_id | dst_status_id |
---|---|
1 | 8 |
2 | 3 |
3 | 4 |
4 | 5 |
5 | 6 |
6 | 7 |
GROUP BY по сути почти то же самое, что и DISTINCT, но с помощью GROUP BY можно посчитать кол-во вхождений, например, то есть, можно использовать агрегатные функции.
LIMIT ставил, ибо таблица большая получается. Результат:
category_id | COUNT(*) |
---|---|
2 | 1 |
3 | 480 |
4 | 6 |
5 | 84 |
6 | 84 |
Можно группировать по нескольким полям:
Объединение результатов, оператор UNION
Задача - выбрать заказы, которые:
- Находятся в статусах “NEW” и “APPROVED_BY_STOCK”
- Создатели которых зарегистрировались в феврале 2018-го
- В которых есть любой йогурт
Все эти условия можно выполнить отдельными запросами и получив отдельные таблицы:
Все эти таблицы можно соединить оператором UNION, который будет стоять между запросами:
Запросы INSERT, UPDATE и DELETE
Добавление данных, оператор INSERT
Если в VALUES не прописать значение для какого-то поля, который прописан после INSERT, то будет значение по умолчанию. Но при NOT NULL если не указать значение – будет ошибка.
Для id задают AUTO_INCREMENT, который позволяет при INSERT-е не задавать не задавать само поле id и его значение, ибо AUTO_INCREMENT сам задает id нужное значение (+1)
То есть, можно и так(без id):
Множественная вставка записей
Запросы изменения данных в записях
Посложнее:
Запросы удаления записей из таблицы
Понятие выражения и функции в SQL-запросах
Выражения
id | category_id | name | count | price |
---|---|---|---|---|
851 | 21 | Бразилия | 481 | 597 |
852 | 20 | Капучино Марагоджип | 140 | 587 |
853 | 20 | Кофе для свидания Марагоджип | 37 | 587 |
Результат:
name | cost |
---|---|
Бразилия | 287157 |
Капучино Марагоджип | 82180 |
Кофе для свидания Марагоджип | 21719 |
Функции
Выведет category_id
и кол-во записей с ним:
category_id | count |
---|---|
2 | 1 |
3 | 480 |
4 | 6 |
5 | 84 |
6 | 84 |
7 | 125 |
8 | 10 |
9 | 7 |
Можно сделать то же самое, но с уникальными name
(если они повторяются):
COUNT
- агрегатная функция. Есть еще функции работы со строками, с датами, временем и другие функции.
Условные операторы и булевы выражения
Оператор IF
Синтаксис:
Пример:
Условия с NULL
Результат будет таким:
id | parent_id | name | is_root |
---|---|---|---|
1 | NULL | Чай | YES |
2 | 1 | Дарджиллинг | NO |
3 | 1 | Айс Ти | NO |
4 | 1 | Пуэр | NO |
5 | 1 | Улун | NO |
6 | 1 | Белый чай | NO |
7 | 1 | Ароматизированный чай | NO |
8 | 1 | Черный чай | NO |
9 | 1 | Зеленый чай | NO |
Объединение нескольких условий
Скобки в условиях
При наличии нескольких условий лучше обернуть их скобками - так запрос станет более читаемым.
Вложенные операторы IF
Вместо вложенных условий лучше использовать оператор CASE.
Оператор CASE
Same IF по синтаксису, но нет скобок, запятых и в конце будет оператор END
Функции работы со строками
Длина строки
Функция
CHAR_LENGTH()
просто выведет кол-во символов в строке.
Подстрока
Синтаксис:
Пример:
Результат:
id | name |
---|---|
1 | Айс Ти “Инжирный пер |
2 | Айс Ти “Черничный Пр |
3 | Айс Ти “Сочное манго |
4 | Айс Ти “Клубничный з |
5 | Айс Ти “Апельсиновый |
6 | Айс Ти “Карамельное |
7 | Мао Се |
Конкатенация
Синтаксис:
Предыдущий результат выглядит некрасиво, ибо резко обрываются строки. Можно добавить ”…” к концу строк:
id | name |
---|---|
1 | Айс Ти “Инжирный пер… |
2 | Айс Ти “Черничный Пр… |
3 | Айс Ти “Сочное манго… |
4 | Айс Ти “Клубничный з… |
5 | Айс Ти “Апельсиновый… |
6 | Айс Ти “Карамельное … |
7 | Мао Се |
Конкатенация при группировке
Задача: получить что-то такое:
id | products |
---|---|
1 | Молочный улун, Кофе с корицей |
2 | Капучино апельсиновое, Какао |
3 | Шоколад молочный, Зеленый чай, Капучино |
… | … |
SEPARATOR ', '
стоит добавить в GROUP_CONCAT, иначе в часто результате после запятых не будет пробелов никаких.
Результат:
id | products |
---|---|
1 | Mao Ce |
2 | Итальянская смесь |
3 | Айс Ти «Апельсиновый лед» |
4 | Айс Ти «Черничный Прованс» |
5 | Эфиопия Иргачиф Коке Фармс Органик |
6 | Для нее, Мaо Се |
7 | Айс Ти «Сочное манго», Айс Ти «Черничный Прованс» |
8 | Кленовый сироп |
9 | Айс Ти «Клубничный зефир» |
10 | Волшебный лес |
11 | Спасибо, Айс Ти «Апельсиновый лед» |
12 | Айс Ти «Сочное манго», Айс Ти «Клубничный зефир», Бай Ча Цзингу, Нуга с обсыпкой «Аравия» |
13 | Айс Ти «Апельсиновый лед» |
14 | Айс Ти «Карамельное яблоко» |
15 | Драже «Мускат Голд» |
16 | Айс Ти «Карамельное яблоко», Бай Ча Цзингу |
17 | Айс Ти «Апельсиновый лед» |
18 | Айс Ти «Клубничный зефир» |
19 | Бай Ча Цзингу, Айс Ти «Инжирный персик», Айс Ти «Черничный Прованс» |
Удаление пробелов по краям строки
На 12-й строке можно заметить пробел и до запятой, ибо так бывает, если в конце строки стоит пробел. Можно убрать его функцией TRIM
Синтаксис прост:
Прошлый запрос, но теперь с TRIM
Замена подстрок
Синтаксис:
Чтобы, например, заменить угловые кавычки на вертикальные, нужно:
Сначала выполняется внутренний REPLACE, и внешний берет, как исходник, результат внутренней выполненной REPLACE.
Похоже на работу с пайпами в bash.
Функции работы с датами
Форматы даты и времени
![[#Типы полей (данных) в SQL DB#Данные о дате и времени]]
Функция DATE_FORMAT
Синтаксис:
Обозначение | Значение |
---|---|
%d | День месяца (от 01 до 31 ) |
%m | Месяц (от 01 до 12 ) |
%Y | Год (4 цифры) |
%H | Часы (от 00 до 23 ) |
%i | Минуты (от 00 до 59 ) |
%s | Секунды (от 00 до 59 ) |
%w | День недели (0 - ВС, 6 - СБ) |
%j | День в году (001 - 366 ) |
… | … |
Как сделать формат таким? |
06.12.2020
Решение:
Результат:
id | creationDate |
---|---|
1 | 06.05.2015 |
2 | 06.04.2020 |
3 | 01.01.2001 |
Можно узнать кол-во заказов по месяцам:
month | count |
---|---|
01 | 136 |
02 | 132 |
03 | 146 |
… | … |
Функции вывода свойств даты
Прошлую задачу можно решить и с днями недели:
month | count |
---|---|
1 | 280 |
2 | 298 |
3 | 285 |
4 | 303 |
5 | 291 |
6 | 272 |
7 | 290 |
Получение текущих даты и времени
Это полезно при INSERT-запросах, например, для даты регистрации пользователя.
Разница между двумя датами
Если сегодня 16 декабря
, то результат - 2
Если сегодня 16 декабря
, то результат - -2
Работа с меткой времени (timestamp)
Метка времени - это кол-во секунд, прошедших с 1 января 1970 до определённого момента - даты и времени.
Любую дату можно преобразовать в метку времени и обратно.
Так легче посчитать кол-во секунд, минут, часов, дней и т.д между двумя датами. Делается это обычными делениями и умножениями на, например, кол-во секунд в минутах, часах и т.д
Агрегатные функции
COUNT(\*)
SUM(field)
Так можно узнать общее кол-во товаров на складе
Или общую стоимость всех товаров:
В SUM()
тоже можно вкладывать другие операторы:
MIN(field)
, MAX(field)
, AVG(field)
AVG(field)
(average - средний, усредненный) выводит среднее значение среди записей в поле field
Результат:
minPrice | maxPrice | avgPrice |
---|---|---|
137 | 997 | 359.8794 |
На этом агрегатные функции не заканчиваются, я описал только основные. |
Фильтрация после группировки, оператор HAVING
Группировка c HAVING
HAVING используется для фильтрации результата GROUP BY по заданным логическим условиям.
Чем отличается WHERE от HAVING?
Во-первых, в HAVING и только в нём можно писать условия по агрегатным функциям (SUM, COUNT, MAX, MIN и т. д.). WHERE выполняется до формирования групп GROUP BY.
Главное отличие HAVING от WHERE в том, что в HAVING можно наложить условия на результаты группировки, потому что порядок исполнения запроса устроен таким образом, что на этапе, когда выполняется WHERE, ещё нет групп, а HAVING выполняется уже после формирования групп.
или (эквивалентные запросы):
Несколько условий
Скорость выполнения запросов, индексы
От чего зависит скорость?
- Размер таблицы
- Скорость поиска
Как происходит поиск в таблице?
- Перебором
- “По-умному”
Поиск «по-умному»
Пример: бинарный поиск:
Скорость работы бинарного поиска зависит от кол-ва данных. Например, чтобы найти среди 1 млн, к примеру, чисел понадобится, в худшем случае, 20 операций:
1000000
500000
250000
125000
62500
31250
15625
7812
3906
1953
976
488
244
122
61
30
15
7
3
1
Для ускорения поиска в MySQL есть индексы (ключи).
Типы индексов (ключей) в MySQL
- BTREE (binary tree)
Для поиска по диапазонам - HASH
Для поиска по совпадениям
Установка индексов (ключей)
При создании таблицы:
Установка индекса в уже существей таблице:
Первичный ключ
Задается для идентификации записей (поля id, грубо говоря):
Обычный ключ
Уникальный ключ
Задается для предотвращения дублирования записей:
Отличия UNIQUE от PRIMARY KEY
PRIMARY KEY | UNIQUE | |
---|---|---|
Используется для идентификации записей | Да | Нет |
Может быть одно значение NULL | Нет | Да |
Может быть несколько в таблице | Нет | Да |
Индексы (ключи)
По одному полю
По нескольким полям
С указанием длины
Связи, внешние ключи и ограничения
Нарушение целостности данных
Наличие ссылок на отсутствующие записи в других таблицах.
Решение: установка связей с ограничениями.
Установка связи “one-to-many”
REFERENCES обозначает связь между таблицами. FOREIGN KEY (внешний ключ) как раз и позволяет пользоваться REFERENCES.
Установка связи “one-to-one”
capital_id
должен быть уникальным (если считаем, что у каждой страны только одна столица), поэтому полю capital_id
задается ключ UNIQE
Установка связи “many-to-many”
В такой таблице должен быть установлен первичный ключ, внешние ключи и уникальный индекс, чтобы не дублировались связи.
Именование ключей и ограничений
C помощью CONSTRAINT можно задать имя ключам или ограничениям для упрощения доступа к ним в будущем по имени:
Обеспечение целостности данных
Как обеспечивать целостность данных при их изменении или удалении?
При удалении записи из таблицы good_category в таблице good в соответствующих записях в поле category_id будет значение NULL.
При изменении поля id в таблице good_category произойдёт ошибка в случае, если в таблице good есть хотя бы одна запись, которая ссылается на этот id в таблице good_category.
Действия при нарушении целостности
Ключевое слово | Действие |
---|---|
RESTRICT NO ACTION (по умолчанию) | Ограничение действия, если оно нарушает целостность данных |
CASCADE | Каскадное удаление или изменение связанных записей |
SET NULL | Установка NULL в поле, которые после выполнения действия будет ссылатьтся на несуществующую запись |
Вложенные запросы
Задача: Разделить общие стоимости на две группы
- Сначала нужно посчитать эти общие стоимости:
- Разделить стоимости на общие группы с помощью вложенного запроса (прошлого):
Результат:
highPriceTotal | lowPriceTotal |
---|---|
98532703 | 453541 |
Вложенные запросы (подзапросы) в условиях
Задача: вывести общие стоимости товаров из категорий, в которых более пятидесяти наименований.
Результат вложенного запроса:
Вообще, вложенные запросы требовательны к ресурсам чисто из-за того, что при таком запросе каждый раз будет выполнятся сначала вложенный запрос и уже над ним - все остальные приколы.
Если результат вложенного запроса не меняется, то лучше просто один раз выполнить этот запрос и его результат записать в IN(). Так можно избежать ненужных вложенных запросов.
Получили данные один раз и работаем с ними:
Структурные запросы
Запросы управления базами данных
Показать все БД
Использовать / переключиться на / выбрать БД
Создать БД
Удалить БД
Запросы управления таблицами
Показать все таблицы в текущей/активной БД
Показать структуру таблицы в текущей/активной БД
Создать таблицу
Удалить поле и создать новое в таблице
Удаление всех строк в таблице (Быстрая очистка)
Удаление таблицы
Представления
Представления - виртуальные таблицы, основанные на результате выполнения определённого SQL-запроса.
Пример задачи
Отдельно обращаться к списку заканчивающихся или закончившихся товаров - товаров, которых на складе осталось менее 10-ти штук - с ценой более 200 рублей за штуку.