SQL - DmitryGontarenko/usefultricks GitHub Wiki
- Основные понятия
- Связи
- Нормализация
- Index
- SQL-операторы
- Ограничения целостности данных
- Schema
- Three-valued logic
- Диалекты языка SQL
- PROCEDURE
- TRIGGER
База данных – совокупность данных, организованных по определенным правилам.
СУБД – это программное обеспечение, для создания, управления и использования базы данных.
Реляционная база данных — база данных, основанная на реляционной модели данных (двумерных таблиц (строка и столбец) с определенными ограничениями).
SQL (structured query language – «язык структурированных запросов») – декларативный язык программирования. Он предназначен для описания, изменения и извлечения данных, хранимых в реляционных базах данных.
Таблица - это совокупность связанных данных, хранящихся в структурированном виде в базе данных.
Ячейка - место, где строка и столбец пересекаются.
Поле - это столбец таблицы, содержащий определенные значения.
Запись - это строка таблицы, содержащая набор.
Ключ - это колонка или колонки, которые не имеют дубликатов в рамках одной таблицы.
Первичный ключ - столбец (или набор столбцов), служащий для уникальной идентификации каждой строки (т.е. обеспечивает целостность объекта). Первичный ключ не может содержать NULL значения, посколько это не дает возможности идентифицировать объект. Если первичный ключ состоит из нескольких столбцов, он называется составным первичным ключом.
Первичный ключ создается с помощью оператора PRIMARY KEY - пример.
Уникальный ключ (потенциальный ключ, candidate key) - столбец (или набор столбцов), обеспечивающий уникальность данных. Если одна или несколько частей ключа содержит NULL значения, то дублирование допускается.
Уникальный ключ создается с помощью оператора UNIQUE - пример.
Внешний ключ - столбец, указывающий на строку в другой таблицы, связанную с данной строкой.
Первичный ключ может быть естественным или суррогатным.
Естественный ключ (natural key) - создается естественным образом из полей таблицы. Например, если у нас есть таблица USER
, которая идентифицируются по имени пользователя, столбец NAME
и будет естественным первичным ключом.
Но такой ключ имеет несколько минусов:
- в случае некорректного выбора естественного ключа, при росте количества записей в таблице, могут возникнуть проблемы - например, невозможность сохранить двух пользователей с одинаковыми именами;
- атрибуты естественного ключа время от времени могут меняться - например, пользователь изменит имя или фамилию. В этом случае возникает необходимость "каскадных изменений" - для сохранения ссылочной целостности система должна внести соответствующие изменения во все значения внешних ключей, ссылающихся на изменяемый. Это может приводить к существенным накладным расходам.
Суррогатный ключ (surrogate key) - это искуственно созданный ключ. Обычно является автоматически сгенерированным полем, которое никак не связанно с информационным содержанием записи. Зачастую в роли суррогатного ключа выстапает автоинкрементное число целочисленного типа (int/long) или GUID/UUID.
Таким образом, суррогатный ключ гарантирует уникальность (в отличии от естественного ключа) и поэтому чаще всего именно он является идентификатором (ID) записи в таблице.
Связь 1:1 (один-к-одному) - тип связи между сущностями А и В, когда каждому экземпляру сущности А соответствует только один экземпляр сущности В и наоборот.
Связь 1:М (один-ко-многим) - тип связи между сущностями А и В, когда одному экземпляру сущности А соответствует 0, 1 или несколько экземпляров сущности В, однако каждому экземпляру сущности В соответствует только один экземпляр сущности А.
Связи M:N (многие-ко-многим) - тип связи между сущностями А и В, при котором каждому экземпляру сущности А может соответствовать 0, 1 или несколько экземпляров сущности В и наоборот.
Нормальная форма - требования, предъявляемые к структуре таблиц реляционных баз данных для устранения избыточных зависимостей между атрибутами полями таблицы.
Цели нормализации - исключить избыточное дублирование данных, которое является причиной аномалий, возникших при добавлении, редактировании и удалении строк таблицы.
Первая нормальная форма (NF1):
- Нельзя добавлять дублирующие строки.
- Ячейки должны хранить только атомарное (неделимое) значение.
Вторая нормальная форма (NF2):
- Таблица должна находиться в 1НФ.
- Все не ключевые столбцы должны зависеть от всего составного первичного ключа.
Третья нормальная форма (NF3):
- Таблица должна находиться во 2НФ.
- Каждый не ключевой столбец не должен зависеть от другого не ключевого столбца.
- Любой не ключевой столбец должен зависеть только от столбца первичного ключа.
- Отсутствие транзитивных зависимостей (столбы не должны являться функцией от набора не ключевых столбцов).
Индекс - это объект базы данных, который связан с таблицей или представлением (view), создаваемый с целью повышения производительности поиска данных. Индекс содержит ключи, построенные из одного или нескольких столбцов таблицы. Ускорение работы с использованием индексов достигается за счет того, что ключи индекса хранятся в виде структуры, оптимизированной под поиск - сбалансированное дерево (B-tree).
Сразу после создания таблицы, она не имеет индексов и выглядит как куча (heap) данных. Записи не имеют определенного порядка хранения.
Без индекса будет выполняться поиск по всем строкам таблицы (full scan), что может значительно влиять на производительность.
Индекс создается при создании столбцов primary key, unique или index.
Недостатки индексов:
- индексы занимают дополнительное место на диске и в оперативных памяти. При каждом создании индекса происходит сохранение ключей, которые могут иметь многоуровневую структуру. И чем больше/длинее ключ, тем больше размер индекса;
- замедляются операции вставки, обновления и удаления записей. Т.к. после таких операций структура индекса каждый раз должна перестраиваться.
Операторы определения данных (Data Definition Language, DDL):
CREATE
- создание базы данных и ее объектов (таблица, индекс, представления, процедура хранения, функция, триггеры)
ALTER
- изменяет структуру существующей базы данных.
DROP
- удаление объектов из базы данных.
COMMENT
- добавление комментариев в словарь данных.
RENAME
- переименовать объект.
TRUNCATE
- удалить все записи из таблицы, включая все пробелы, выделенные для записей, удалены.
Операторы манипуляции данными (Data Manipulation Language, DML):
SELECT
- получение данных из базы данных.
INSERT
- вставить данные в таблицу.
UPDATE
- обновляет существующие данные в таблице.
DELETE
- удаление всех записей из таблицы базы данных.
MERGE
, UPSERT
- операции вставки или обновления.
CALL
- вызов подпрограммы процедуры.
PLAN EXPLAIN
- интерпретация пути доступа к данным.
LOCK
- блокировка транзакций.
Операторы определения доступа к данным (Data Control Language, DCL):
GRANT
- позволяет пользователям получать доступ к базе данных.
REVOKE
- вывести права доступа пользователей, заданные с помощью команды GRANT.
DENY
- задает запрет, имеющий приоритет над разрешением.
Операторы управления транзакциями (Transaction Control Language, TCL):
COMMIT
- совершает транзакцию.
ROLLBACK
- откат транзакции в случае возникновения каких-либо ошибок.
SAVEPOINT
- откат транзакционных точек внутри групп.
SET TRANSACTION
- указать характеристики для транзакции.
Если выполняются DDL или DCL операторы, то Oracle автоматически выполняет COMMIT
до и после команды. В противном случае, фиксировать транзакцию надо вручную.
CREATE
служит для создания объектов базы данных. Наиболее общими для большинства СУБД будут такие команды, как создание базы данных, создание таблицы или роли. Например:
CREATE DATABASE shop_db;
CREATE ROLE user;
CREATE TABLE tb_customer (
id int,
name varchar(255)
);
ALTER
служит для добавления или изменений объектов базы данных. Наиболее часто применяется для изменений свойств таблицы или ролей.
ALTER TABLE tb_employees ADD address varchar(50);
ALTER TABLE tb_employees DROP COLUMN address;
ALTER TABLE tb_product RENAME COLUMN price TO car_price;
ALTER TABLE tb_book ADD CONSTRAINT fk_book_author FOREIGN KEY (author_id) REFERENCES tb_author(id);
ALTER TABLE tb_book DROP CONSTRAINT fk_book_author;
ALTER TABLE tb_author ALTER COLUMN name SET NOT NULL;
ALTER TABLE tb_author ALTER COLUMN name DROP NOT NULL;
Изменения типа данных колонок таблицы более индивидуально для каждый БД, ниже будет представлен пример для PostgreSQL:
ALTER TABLE tb_employee
ALTER COLUMN address TYPE int USING(age::integer);
UPDATE
изменяет существующие данные в таблице. С помощью этого оператора могут быть заданы значения для любого количества столбцов.
При отсутствии предложения WHERE
будут обновлены все строки таблицы.
Выражение может относится как к текущим значениями в изменяемое таблицы, так и к значениям из других столбцов. Например, уменьшим цену на все ноутбуки на 10%:
UPDATE tb_laptop SET price = price * 0.9;
Для вычисления значений столбцов можно использовать подзапрос. Например, установим для сотрудника с идентификатором 3, максимальную зарплату:
UPDATE tb_employees SET salary = (
SELECT MAX(salary) FROM tb_employees)
WHERE id = 3;
DELETE
удаляет одну или несколько строк из таблицы базы данных. Если предложение WHERE
отсутствует, удаляются все строки из таблицы.
DELETE FROM tb_employees WHERE id = 12;
TRUNCATE
очищает все строки в таблице. Логически схожа с операцией DELETE
без WHERE
, но в зависимости от СУБД имеет свои отличия и ограничения.
TRUNCATE TABLE tb_employees;
DROP
служит для удаления баз данных или ее объектов, таких как таблицы, ролей и т.д. Необязательным аргументом для данного оператора может служить IF EXISTS
, с ним удаление произойдет только в том случае, если объект уже существует.
DROP DATABASE shop;
DROP TABLE IF EXISTS tb_employees;
DROP ROLE user;
SELECT
возвращает строки из базы данных и позволяет делать выборку одной или нескольких строк или столбцов из одной или нескольких таблиц. Этот оператор имеет следующую структуру:
SELECT
[DISTINCT | DISTINCTROW | ALL]
FROM table_references
[WHERE where_definition]
[GROUP BY col_name]
[HAVING where_definition]
[ORDER BY [ASC | DESC]]
INSERT
добавляет одну или несколько строк в таблицу базы данных.
INSERT INTO tb_author (id, name, age) VALUES (5, 'George Orwell', 32);
Список столбцов не является обязательным и в том случае, если он отсутствует, список вставляемых значений должен быть полный, т.е. обеспечивать значения для всех столбцов таблицы.
Если задать список столбцов, то можно изменить порядок их следования.
INSERT INTO tb_author (name, age, id) VALUES ('George Orwell', 32, 5);
Для автоинкрементируемых полей явного указания столбца не требуется, оно будет установлено автоматически.
Если у некоторых столбцов установлено значение по умолчанию DEFAULT
или не указано ограничения NOT NULL
, и при этом они не указаны в предложении INSER INTO
, значением для таких столбцов будет значение по умолчанию или NULL соответственно.
Но в операции все же можно указать DEFAULT
явно. В первом случае отдельно для каждого столбца, во втором для всех столбцов будет указано значение по умолчанию:
INSERT INTO tb_author (id, name, age) VALUES (DEFAULT, 'Franklin Herbert', 6);
INSERT INTO tb_author DEFAULT VALUES;
WHERE
определяет условия, которые должны быть выполнены для всех возвращаемых строк. Количество предикатов, которое может содержать условия поиска, не ограничено.
SELECT * FROM tb_employees WHERE age >= 18;
Предикатом называют любое выражение, результатом которого являются значения TRUE, FALSE или UNKNOWN. Предикаты могут представлять собой как одно, так и любую комбинацию из неограниченного количества выражений, построенных с помощью булевых операторов AND
, OR
, IS
или NOT
. Исключение составляют предикаты: NULL
, EXISTS
, UNIQUE
и MATCH
, которые не могут принимать значения UNKNOWN.
Предикат сравнения представляет собой два выражения, соединяемых оператором сравнения. Имеется шесть традиционных операторов сравнения: =, >, <, >=, <=, <>.
Данные числового типа сравниваются в соответствии с их алгебраическим значением.
Данные строкового типы сравниваются в соответствии с их алфавитной последовательностью.
Данные типа дата/время сравниваются в хронологическом порядке.
Данные типа INTERVAL (временной интервал) преобразуются в соответствующие типы, а затем сравниваются как обычные числовые значения.
SELECT * FROM tb_products
WHERE NOT (type='laptop') AND price < 50000;
IN
проверяет на принадлежность ко множеству значений.
SELECT * FROM tb_employees WHERE age IN (18, 20, 35);
BETWEEN
проверяет на принадлежность к диапазону значений, соединяемых ключевым словом AND
. Как и для предиката сравнения, выражения должны быть совместимы по типу.
SELECT * FROM tb_employees WHERE age BETWEEN 20 AND 25;
LIKE
используется в предложении WHERE
и определяет, совпадает ли указанная символьная строка с заданным шаблоном. Для формирования шаблона используются специальные символы:
%
- замещает последовательность любого количества символов.
SELECT * FROM tb_car WHERE name LIKE 'Ma%';
Выражение LIKE%
эквивалентно отсутствию оператора WHERE
.
_
- представляет собой один символ.
SELECT * FROM tb_car WHERE name LIKE 'Ma_erati';
В том случае, если нам необходимо произвести поиск по значению, которое начинается с символа "%", следует использовать escape
последовательность. Например:
SELECT * FROM tb_product
WHERE name
LIKE '\%%' ESCAPE '\';
EXISTS
принимает значение TRUE, если подзапрос возвращает любое количество строк, иначе его значения равно FALSE. Для NOT EXISTS
все наоборот.
SELECT * FROM tb_author
WHERE id = 3 AND EXISTS (
SELECT name FROM tb_author
WHERE name = 'Charlotte Bronte');
IS
[NOT]NULL
позволяет проверить отсутствие (наличие) значений в полях таблицы.
SELECT * FROM tb_product WHERE price IS NULL;
USE
изменяет контекст базы данных на указанную базу данных.
USE shop_db;
CREATE TABLE tb_orders (
...
С помощью COMMENT
можно добавлять комментарии к таблицам и полям (столбцам) базы данных.
COMMENT ON TABLE tb_book IS 'таблица книг';
COMMENT ON COLUMN tb_book.price IS 'поле цены';
ORDER BY
сортирует данные, возвращенные запросом. ASC
сортировка по возрастанию, используется по умолчанию.DESC
сортировка по убыванию.
SELECT * FROM tb_employees
ORDER BY name ASC, age DESC;
DISTINCT
используется для возврата только уникальных значений. Все повторяющиеся значения будут удалены из результирующей таблицы.
SELECT DISTINCT name
FROM tb_employees;
В SQL для полей и таблиц можно определять псевдонимы, они задаются с помощью оператора AS
, но его можно опустить. Такая возможность полезна, если имя источника данных слишком длинное.
SELECT a.id, a.first_name AS name
FROM tb_author AS a;
Так же в предложении SELECT
существует возможность вычисления значений столбцов в результирующем наборе. Например, можно вывести цену в долларах для всего списка товаров:
SELECT name, price_rub * 65 AS price_usd
FROM product;
JOIN
оператор является реализацией операции соединения реляционных моделей. Входит в предложение FROM
. Имеет несколько видов:
(INNER) JOIN
- в результирующую таблицу попадают только те строки, которые удовлетворяют условиям соединения.
LEFT (OUTER) JOIN
- в результирующую таблицу попадают все строки из "левой таблица", данными для "правой таблицы", в случае не выполнения условий соединения, будут служить NULL.
RIGHT (OUTER) JOIN
- работает аналогично LEFT JOIN
с точностью до наоборот.
FULL (OUTER) JOIN
- возвращает все строки "левой" и "правой таблицы". Если для каких-то строк условия соединения выполняются, то они объединяются в одну строку, в противном случае значением будет NULL.
CROSS JOIN
- в результирующей таблицы каждая строка "левой таблицы" объединяется со всеми строкам "правой таблицы".
В большинстве СУБД слова INNER
и OUTER
можно опустить.
SELECT a.id, a.name, b.id AS book_id, b.name AS book_name
FROM tb_author a
RIGHT JOIN tb_book b ON a.id = b.author_id
В данном примере результирующая таблица будет содержать данные из таблиц author и book, причем результат будет содержать все строки из таблицы book, но если автор какой-либо книги не указан, то значением строки из таблицы author будет NULL.
Если название столбцов, по которым происходит объединение таблиц, совпадают, то вместо ON
можно использовать USING
.
SELECT a.id, a.name, b.id AS book_id, b.name AS book_name
FROM tb_author a
JOIN tb_book b
USING(id)
Оператор WHERE
применяется после JOIN
, пример:
SELECT a.systemname, b.systemname
FROM doctypeparam a
JOIN doctype b on a.doctypeid = b.doctypeid
WHERE a.systemname='STEP_SIZE';
UNION[ALL]
служит для объединения запросов. Оператор UNION
объединяет выходные строки каждого из запросов в один результирующий набор. Если определен параметр ALL
, то сохраняет все дубликаты входящих строк, в противном случае остаются только уникальные строки.
Запросы должны возвращать одинаковое количество столбцов и быть совместимы по типу данных. В результирующем наборе используются имена столбцов, заданные в первом запросе. Предложение ORDER BY
применяется к результату соединения, поэтому оно может быть указано только в конце составного запроса.
SELECT model, price
FROM tb_pc
UNION
SELECT model, price
FROM tb_laptop
ORDER BY DESC
SQL поддерживает агрегатные функции, такие как count
(количество), sum
(сумма), avg
(среднее), max
(максимальное значение) и min
(минимальное значение).
SELECT SUM(age) FROM tb_employees;
В этом примере результирующая таблица будет иметь одно поле, ячейка которого будет содержать суммарный возраст всех сотрудников.
Агрегатные функции нельзя использовать с оператором WHERE
, но можно использовать вложенный запрос:
SELECT * FROM tb_employees
WHERE age = (SELECT MAX(age) FROM tb_employees);
Все столбы SELECT
, не вошедшие в агрегатные функции, должны быть указана в предложении GROUP BY
. Рассмотрим на примере:
SELECT name, MAX(age)
FROM tb_employees
GROUP BY name;
Каждый агрегатный результат вычисляется по строкам таблицы, соответствующим отдельной записи (в данном случае name). В этом примере мы получим возраст самого старшего сотрудника, по сравнению с его "однофамильцами", т.е. повторяющихся имен в результирующей таблицы не будет.
HAVING
используется в инструкции SELECT
с предложением GROUP BY
и предназначен для фильтрации результата запроса по заданным условиям.
SELECT name, MAX(age)
FROM tb_employees
GROUP BY name
HAVING MAX(age) > 23;
Предложение HAVING
подобно WHERE
, но применимо только к результату операции (результирующему набору), тогда как WHERE
применим к отдельным строчкам и выполняется до того, как будет получен результат операции. HAVING
и WHERE
можно использовать в одном запросе.
SEQUENCE
это объект базы данных, который генерирует целые числа в соответствии с правилами, установленными во время его создания, таким образом такая последовательность не зависит от какой-то конкретной таблицы, как в случае с IDENTITY
.
Для последовательности можно указывать как положительные, так и отрицательные целые числа.
Рассмотри на примере создания, изменения, удаления и вызов последовательности:
DROP SEQUENCE IF EXISTS serial;
CREATE SEQUENCE serial START 101;
ALTER SEQUENCE IF EXISTS serial INCREMENT BY 5
MAXVALUE 150;
SELECT nextval('serial');
Для более подробного ознакомления с синтаксисом SEQUENCE
рекомендуется обращаться к документации конкретной СУБД.
Merge - это DML операция, позволяющая вставлять обновлять данные одной таблицы - данными других таблиц. При слиянии таблиц проверяется условие, и если оно истинно, то выполняется UPDATE
, а если нет - INSERT
.
Ниже представлен пример данной команды в Oracle Database:
merge into TABLE_CAR car
using (select (select ID from TABLE_ENGINE where NAME = '8V') as ENGINEID,
'Heat engine' as TYPE
from DUAL) eng
on (car.TYPE = eng.TYPE)
when matched then
update set ENGINEID = eng.ENGINEID
when not matched then
insert (ENGINE) values (eng.ENGINEID);
Предложение USING
- определяет набор обновляемых или вставляемых данных. Может быть таблицей, представлением или подзапросом.
CASE
представляет собой общее условное выражение, напоминающее операторы if/else в других языках программирования.
SELECT name, age,
CASE
WHEN age > 30 THEN 'too old'
WHEN age < 20 THEN 'too young'
ELSE 'acceptable age'
END AS age_employee
FROM tb_employees;
В результирующей таблицы этого примера будет 3 столбца: name, age и age_employee, т.к. CASE
в данном случае является одним из элементов выборки, на равне с уже существующими таблицами name и age.
С помощью CHECK
можно указать, что значение данного столбца должно удовлетворять логическому выражению.
Например, можно ограничить цену товара положительным знаком или гарантировать, что цена со скидкой всегда будет меньше обычной:
CREATE TABLE tb_products (
id int,
varchar(255),
price int CONSTRAINT positive_price CHECK (price > 0)
);
CREATE TABLE tb_products (
id int,
varchar(255),
price int CHECK (price > 0),
discounted_price int CHECK (discounted_price > 0),
CHECK (price > discounted_price)
);
CONSTRAINT
и его идетификатор можно опустить, но он улучшает читаемость сообщения, в случае ошибки.
Стоит добавить, что CHECK
удовлетворяется, если выражение принимет значение TRUE или NULL. Что бы гарантировать, что столбец не содержит NULL, можно использовать ограничения NOT NULL
.
Ограничение NOT NULL
указывает, что столбцу нельзя присваивать значение NULL. По умолчанию NULL всегда разрешен.
CREATE TABLE tb_products (
id int NOT NULL,
name varchar(255) NOT NULL,
price int
);
Ограничение уникальности UNIQUE
гарантирует, что данные в определенном столбце или группе столбцов уникальны среди строк таблицы.
CREATE TABLE tb_products (
id int,
name varchar(255) CONSTRAINT unique_name UNIQUE,
price int
);
Как и в случае с CHECK
, оператор CONSTRAINT
можно опустить.
Что бы определить ограничение уникальности для групп столбцов, нужно записать их в виде ограничения таблицы, перечислив имена столбцов через запятую. Такое ограничение указывает, что сочетание значений перечисленных столбцов должно быть уникально во всех таблице, тогда как значение каждого столбца по отдельности уникальными не будут.
CREATE TABLE tb_employee (
id int,
name varchar(255),
age int,
UNIQUE (name, age)
);
Ограничение первичного ключа PRIMARY KEY
означает, что образующий столбец или группа столбцов может быть уникальным идентификатором строк в таблице. Значения должны быть уникальны и отличными от NULL. При добавлении первичного ключа в столбец, данные помечаются как NOT NULL.
CREATE TABLE tb_products (
id int PRIMARY KEY,
name varchar(255)
);
Первичные ключи могут включать несколько столбцов, синтаксис похож на запись ограничений уникальности UNIQUE
.
CREATE TABLE tb_products (
id int,
name varchar(255),
serial_num int,
PRIMARY KEY(id, serial_num)
);
Ограничение внешнего ключа FOREIGN KEY
указывает, что значение столбца (или группы столбцов) должны соответствовать значением некоторой строки другой таблицы.
Рассмотрим на примере: пусть у нас будет таблица продуктов и таблица заказами этих продуктами. Мы хотим, что бы в таблице заказов содержались только заказы действительно существущих продуктов. Поэтому определим в таблице с заказами ограничение внешнего ключа, ссылающегося на таблицу продуктов:
CREATE TABLE tb_products (
id int PRIMARY KEY,
name varchar(255),
price int
);
CREATE TABLE tb_orders (
order_id int PRIMARY KEY,
product_id int REFERENCES tb_products (id),
quantity int
);
С таким ограничением создать заказ со значением product_id, отсутствующим в таблице tb_products, будет невозможно. Если опустить списк столбцов (после указания таблицы), внешний ключ будет неявно связан с первичным ключем главной таблицы.
Но все же более удобным для чтения, является создания первичного и внешнего ключа с помощью CONSTRAINT
.
CREATE TABLE tb_orders (
id int,
name varchar(50),
product_id int,
CONSTRAINT pk_cst PRIMARY KEY (id),
CONSTRAINT pr_cst FOREIGN KEY (product_id) REFERENCES tb_products(id)
);
Так же существует ограничивающе и каскадное удаление, обновление. Об этом подробнее здесь и здесь.
Схема данных содержит таблицы, поля для каждой таблицы и ограничения целостности.
CREATE SCHEMA storage AUTHORIZATION admin;
DROP SCHEMA storage;
При создании объекта базы данных схему можно указать явно, в ином случае объект будет создан для схемы public.
CREATE TABLE storage.tb_product (
id int,
name varchar(50)
);
Если в данных присутствует значение NULL, логические операторы и операторы сравнения могут возвращать, кроме TRUE или
FALSE, также и третий результат - UNKNOWN. Это называется тройственная логика.
В таблице ниже содержаться примеры поведения логических операторов в разных ситуациях.
Язык SQL реализован во всех реляционных базах данных, но у некоторых СУБД есть расширение этого стандарта, собственный язык работы с данными, его называют диалектом SQL. Такой внутренний язык дает возможность получать системную информацию и упрощать SQL запросы.
Вот некоторые популярные диалекты языка SQL:
Transact-SQL (сокращенно T-SQL) – используется в Microsoft SQL Server;
PL/SQL (procedural language/structured query language) – используется в Oracle Database;
PL/pgSQL (procedural language/postgres structured query language) – используется в PostgreSQL.
Хранимые процедуры представляют собой набор инструкций, которые выполняются как единое целое. Тем самым хранимые процедуры позволяют упростить комплексные операции и вынести их в единый объект. Хранимые процедуры обычно выполняются быстрее, чем обычные SQL-инструкции. Так как код процедур компилируется один раз при первом ее запуске, а затем сохраняется в скомпилированной форме. Для более подробного ознакомления с работой процедур, рекомендуется обращаться к документации конкретной СУБД (PostreSQL).
Ниже представлен пример создания процедуры в СУБД PostgreSQL. Мы возвращаем результат функции в виде строки.
CREATE FUNCTION getProduct(int) returns tb_products AS $$
SELECT * FROM tb_products WHERE id = $1;
$$ LANGUAGE SQL;
SELECT * FROM getProduct(1);
Триггер - хранимая процедура особого типа, которая будет вызываться при добавлении, изменении или удалении данных в определенно таблице. Для более подробного ознакомления с работой триггера, рекомендуется обращаться к документации конкретной СУБД (PostgreSQL).
Ниже представлен пример создания триггера в СУБД PostreSQL, с помощью диалекта PL/pgSQL.
Создадим триггер, который при добавлении или изменении строки в таблице сохраняет в этой строке информацию о текущем пользователи и отметку времени. Кроме того, он требует, что бы для столбца price значение было не меньше 0.
CREATE TABLE tb_car (
id int,
name varchar(255),
price int,
last_date timestamp,
last_user text
);
CREATE FUNCTION price_check() RETURNS trigger AS $price_check$
BEGIN
-- Проверить, что установливаемая цена не ниже 0
IF NEW.price < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.name;
END IF;
-- Записать, кто и когда вносил запись
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$price_check$ LANGUAGE plpgsql;
CREATE TRIGGER price_check BEFORE INSERT OR UPDATE ON tb_car
FOR EACH ROW EXECUTE PROCEDURE price_check();
Далее создадим два запроса на добавление данных:
INSERT INTO tb_car (id, name, price) VALUES (1, 'Mazda', 500);
INSERT INTO tb_car (id, name, price) VALUES (1, 'Mazda', -1);
В первом случае данные успешно запишутся и строка будет содержать время выполнения операции, и имя пользователя. Во втором случае при попытке выполнения будет выведена ошибка "Mazda cannot have a negative salary".
Представление - это таблица, которая содержит запрос из других таблиц. Таблицы-представления не содержат собственных данных, это фактически запрос, который выполнется каждый раз, когда запрашивается вывод представления.
Синтаксис создания представления:
CREATE VIEW all_author AS
SELECT * FROM author;
Вывод представлений осуществляется так же, как и вывод базовых таблиц:
SELECT * FROM all_author;
Таблицы-представления могут быть модифицированы DML-командами, но модификация будет воздействовать на базовую таблицу.
Удаление представления:
DROP VIEW all_author;
Еще один пример создания представления - у нас есть таблицы author
и book
, которые связаны М:М с помощью кросс-таблицы author_book
. Создадим из этих таблицы представление, в котором будет выводиться только имя, фамилия и книга автора:
CREATE VIEW author_books AS
SELECT a.first_name, a.last_name, b.title
FROM author_book ab
JOIN author a ON ab.author_id = a.id
JOIN book b ON ab.book_id = b.id;
-- выводим результат
SELECT * FROM author_books
Партицирование (секционирование) - это разделение объектов БД (таблиц, индексов, представлений) по разным файловым группам, которые могут быть физически распределены. Используется в целях повышения производительности и доступности данных БД.
Для начала нужно определить ключи партицирования - один или несколько столбцов, относительно которых будет делаться разделение таблицы на партиции.
Существует несколько методов партицирования:
-
range()
- по диапазону ключа; -
list()
- по списку ключа; -
hash()
- по хэшу ключа.
Метод range()
чаще всего используется при партицирования по дипазону дат или значений ключа, например:
-- партицирование по значению (по зарпалте)
create table EMPLOYEES (
ID int not null,
NAME varchar(30),
HIRED date,
SALARY NUMBER
)
partition by range (SALARY) (
partition p0 values less than (5000),
partition p1 values less than (10000),
partition p3 values less than maxvalue
) enable row movement;
-- партицирование по дате (по году)
create table EMPLOYEES (
ID int not null,
NAME varchar(30),
HIRED date
)
partition by range (year(HIRED)) (
partition p0 values less than (1991),
partition p1 values less than (1996),
partition p2 values less than (2001),
partition p3 values less than (maxvalue)
);
Значение maxvalue
позволяет записать значение в последнюю партицию при превышении значения диапозона предпоследней партиции. Тем самым "перекрыть" всю временную ось.
Команда enable row movement
позволяет "переходить" строкам таблицы из одной партиции в другую. Это может происходить автоматически при обновлении ключа партиции. Эту команду можно выполнить для уже существующей таблицы - alter table TABLE_NAME enable row movement
.
Команда interval
позволяет автоматически создавать партиции по заданному интервалу (шагу). Такой подход называется интервальное партицирование.
Например:
partition by range (ID) interval (1) (
partition FIRST_PARTITION values less than (2)
);
В данном случае будет создано столько партиций, сколько будет уникальных идентификаторов.
Одна партиция (для id < 2) создана по умолчанию.
При партицировании по методу hash()
данные таблицы будут "равномерно распределяться" между партициями. При удалении какой-либо хэш-партиции вызывается перезапись всех данных в другую партицию.
Пример:
create table EMPLOYEES (
ID int not null,
NAME varchar(30),
HIRED date
)
partition by hash(id) partitions 10;
В данном примере таблица будет разделена на десять хэш-партиций.
При партицировании большой таблицы сами партиции могут оказаться достаточно крупными.
Такие партиции также могут партицироваться, причем использовать при этом и другие методы партицирования. Такое двухуровневое партицирование называется композитным. Допускается только два уровня партицирования - партиция и субпартиция.
Пример:
create table EMPLOYEES (
ID number(10,0),
SUBID number(2,0),
BIRTHDAY date,
BIO varchar (100 byte)
)
partition by rande(BIRTHDAY)
subpartition by hash(SUBID) subpartitions 2
(
partition FIRST_PART values less then ('01-apr-2000'),
partition SECOND_PART values less then ('01-apr-2020'),
partition THIRD_PART values less then (MAXVALUE)
);
В данном случае у нас есть 3 партиции (разделенных по дате), каждая из которых "делится" на две портиции по хэшу (рассчитаном на основе поля SUBID).
В случае использования субпартиций - партиции носят логический (объединительный) харакетр, как и сама таблица. Возникает несколько уровней вложенности - таблица, партиция, субпартиция.
При создании суб/партиций без указания имени, оно генирируется автоматически в формате SYS_######.
Для отображения партиций и субпартицей можно обратиться к ним по наименованию:
select * from TABLE_NAME partition (PARTITION_NAME);
select * from TABLE_NAME subpartition (SUBPARTITION_NAME);
Переименование партиции:
alter table TABLE_NAME rename partition PARTITION_NAME1 to PARTITION_NAME2;
Изменение метода партицирования:
alter table TABLE_NAME modify partition by range (ID) interval (1) (
partition FIRST_PART values less than (2)
);
Удаление партиций:
alter table TABLE_NAME drop partition PARTITION_NAME;
Источники:
Секционирование таблиц в Oracle на практике
Источники:
Представления