СУБД. Лекция 2

СУБД
Навроцкий Артем
Лекция 2

Введение в проектирование. Введение в SQL.

Проектирования БД

Проектирование баз данных
Процесс создания схемы базы данных и определения необходимых ограничений целостности.

Основные задачи

Сложная система, спроектированная наспех, никогда не работает, и исправить ее, чтобы заставить работать, невозможно.
Законы Мерфи.
16-й закон систематики

Особенности концептуального проектирования

Трудно что-либо предвидеть, а уж особенно будущее.
Нильс Бор

Этапы проектирования

Внешнее представление (внешняя схема) данных
Совокупностью требований со стороны конкретной функции, выполняемой пользователем.
Концептуальная схема
Полная совокупностью всех требований к данным, полученной из пользовательских представлений о реальном мире.
Внутренняя схема
Сама база данных.

Инфологическое проектирование

Концептуальное (инфологическое) проектирование
Построение семантической модели предметной области, то есть информационной модели наиболее высокого уровня абстракции.

Логическое проектирование

Логическое (даталогическое) проектирование
Создание схемы базы данных на основе конкретной модели данных, например, реляционной модели данных.

Физическое проектирование

Физическое проектирование
Создание схемы базы данных для конкретной СУБД.


Почему проект БД может быть плохим?

Блюдо Вид Дата Продукт Калорийность Вес (г) Поставщик Город Страна Цена ($)
Лобио Закуска 01.09.2012 Фасоль 307 200 "Хуанхэ" Пекин Китай 0.37
Лобио Закуска 01.09.2012 Лук 45 40 "Наталка" Киев Украина 0.52
Лобио Закуска 01.09.2012 Масло 742 30 "Лайма" Рига Латвия 1.55
Лобио Закуска 01.09.2012 Зелень 18 10 "Даугава" Рига Латвия 0.99
Борщ Суп 01.09.2012 Мясо 166 80 "Наталка" Киев Украина 2.18
Борщ Суп 01.09.2012 Лук 45 30 "Наталка" Киев Украина 0.52
Борщ Суп 01.09.2012 Томаты 24 40 "Полесье" Киев Украина 0.45
Борщ Суп 01.09.2012 Рис 334 50 "Хуанхэ" Пекин Китай 0.44
Борщ Суп 01.09.2012 Масло 742 15 "Полесье" Киев Украина 1.62
Борщ Суп 01.09.2012 Зелень 18 15 "Наталка" Киев Украина 0.88
  1. Избыточность
  2. Потенциальная противоречивость
    (аномалии обновления)
  3. Аномалии включения
  4. Аномалии удаления

Концептуальное проектирование

Выявление сущностей и связей

Структура таблицы тем

Заголовок Дата Автор E-Mail Текст Оценка Ответы
Руслан и Людмила 01.01.1820 Пушкин А.С. sukin_syn@mail.ru 500 120
Война и Мир 01.03.1869 Толстой Л.Н. tolstoy@inbox.ru 100 345
Отцы и Дети 15.08.1862 Иван Тургенев nedobobov@mail.ru 256 18
Повести Белкина 12.06.1830 Пушкин А.С. sukin_syn@mail.ru 400 96
Муму 10.11.1852 Иван Тургенев nedobobov@mail.ru 312 46

Первичный ключ

Первичный ключ выбирается исходя из соображений удобства и сохранения уникальности. Если такого ключа нет, то имеет смысл добавить специальное поле.

TopicID Заголовок Дата Автор E-Mail Текст Оценка Ответы
5 Руслан и Людмила 01.01.1820 Пушкин А.С. sukin_syn@mail.ru 500 120
8 Война и Мир 01.03.1869 Толстой Л.Н. tolstoy@inbox.ru 100 345
12 Отцы и Дети 15.08.1862 Иван Тургенев nedobobov@mail.ru 256 18
43 Повести Белкина 12.06.1830 Пушкин А.С. sukin_syn@mail.ru 400 96
16 Муму 10.11.1852 Иван Тургенев nedobobov@mail.ru 312 46

Справочник пользователей

Нормальная форма
Свойство отношения в реляционной модели данных, характеризующее его с точки зрения избыточности, потенциально приводящей к логически ошибочным результатам выборки или изменения данных. Нормальная форма определяется как совокупность требований, которым должно удовлетворять отношение.
E-Mail Пароль Имя Дата Привилегии Разделы
tolstoy@mail.ru ***** Толстой Л.Н. 09.09.1828 Админ, Модератор Новости, Флуд
saltykov@inbox.ru ***** Николай Щедрин 27.01.1826 Модератор Флуд, Юмор
fmd@mail.ru ***** Федор Михайлович 11.11.1821 Игрок E-Mail
sukin_syn@mail.ru ***** Пушкин А.С. 06.06.1799 Админ Поэзия

1-ая нормальная форма

Переменная отношения находится в первой нормальной форме (1НФ) тогда и только тогда, когда в любом допустимом значении отношения каждый его кортеж содержит только одно значение для каждого из атрибутов.

E-Mail Пароль Имя Дата Привилегии Разделы
tolstoy@mail.ru ***** Толстой Л.Н. 09.09.1828 Админ, Модератор Новости, Флуд
saltykov@inbox.ru ***** Николай Щедрин 27.01.1826 Модератор Флуд, Юмор
fmd@mail.ru ***** Федор Михайлович 11.11.1821 Игрок E-Mail
sukin_syn@mail.ru ***** Пушкин А.С. 06.06.1799 Админ Поэзия

1-ая нормальная форма

Переменная отношения находится в первой нормальной форме (1НФ) тогда и только тогда, когда в любом допустимом значении отношения каждый его кортеж содержит только одно значение для каждого из атрибутов.

E-Mail Пароль Имя Дата Привилегии Разделы
tolstoy@mail.ru ***** Толстой Л.Н. 09.09.1828 Админ Новости
tolstoy@mail.ru ***** Толстой Л.Н. 09.09.1828 Админ Флуд
tolstoy@mail.ru ***** Толстой Л.Н. 09.09.1828 Модератор Новости
tolstoy@mail.ru ***** Толстой Л.Н. 09.09.1828 Модератор Флуд
saltykov@inbox.ru ***** Николай Щедрин 27.01.1826 Модератор Флуд
saltykov@inbox.ru ***** Николай Щедрин 27.01.1826 Модератор Юмор
fmd@mail.ru ***** Федор Михайлович 11.11.1821 Игрок E-Mail
sukin_syn@mail.ru ***** Пушкин А.С. 06.06.1799 Админ Поэзия

2-ая нормальная форма

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

E-Mail Пароль Имя Дата Привилегии Разделы
tolstoy@mail.ru ***** Толстой Л.Н. 09.09.1828 Админ Новости
tolstoy@mail.ru ***** Толстой Л.Н. 09.09.1828 Админ Флуд
tolstoy@mail.ru ***** Толстой Л.Н. 09.09.1828 Модератор Новости
tolstoy@mail.ru ***** Толстой Л.Н. 09.09.1828 Модератор Флуд
saltykov@inbox.ru ***** Николай Щедрин 27.01.1826 Модератор Флуд
saltykov@inbox.ru ***** Николай Щедрин 27.01.1826 Модератор Юмор
fmd@mail.ru ***** Федор Михайлович 11.11.1821 Игрок E-Mail
sukin_syn@mail.ru ***** Пушкин А.С. 06.06.1799 Админ Поэзия

2-ая нормальная форма

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

E-Mail Пароль Имя Дата
tolstoy@mail.ru ***** Толстой Л.Н. 09.09.1828
saltykov@inbox.ru ***** Николай Щедрин 27.01.1826
fmd@mail.ru ***** Федор Михайлович 11.11.1821
sukin_syn@mail.ru ***** Пушкин А.С. 06.06.1799
E-Mail Привилегии Разделы
tolstoy@mail.ru Админ Новости
tolstoy@mail.ru Админ Флуд
tolstoy@mail.ru Модератор Новости
tolstoy@mail.ru Модератор Флуд
saltykov@inbox.ru Модератор Флуд
saltykov@inbox.ru Модератор Юмор
fmd@mail.ru Игрок E-Mail
sukin_syn@mail.ru Админ Поэзия

3-я нормальная форма

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

TopicID Заголовок Дата Автор E-Mail Текст Оценка Ответы
5 Руслан и Людмила 01.01.1820 Пушкин А.С. sukin_syn@mail.ru 500 120
8 Война и Мир 01.03.1869 Толстой Л.Н. tolstoy@inbox.ru 100 345
12 Отцы и Дети 15.08.1862 Иван Тургенев nedobobov@mail.ru 256 18
43 Повести Белкина 12.06.1830 Пушкин А.С. sukin_syn@mail.ru 400 96
16 Муму 10.11.1852 Иван Тургенев nedobobov@mail.ru 312 46

3-я нормальная форма

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

E-Mail Автор
sukin_syn@mail.ru Пушкин А.С.
tolstoy@inbox.ru Толстой Л.Н.
nedobobov@mail.ru Иван Тургенев
TopicID Заголовок Дата E-Mail Текст Оценка Ответы
5 Руслан и Людмила 01.01.1820 sukin_syn@mail.ru 500 120
8 Война и Мир 01.03.1869 tolstoy@inbox.ru 100 345
12 Отцы и Дети 15.08.1862 nedobobov@mail.ru 256 18
43 Повести Белкина 12.06.1830 sukin_syn@mail.ru 400 96
16 Муму 10.11.1852 nedobobov@mail.ru 312 46

4-ая нормальная форма

Переменная отношения находится в четвёртой нормальной форме, если она находится в третьей нормальной форме и не содержит нетривиальных многозначных зависимостей.

E-Mail Привилегии Разделы
tolstoy@mail.ru Админ Новости
tolstoy@mail.ru Админ Флуд
tolstoy@mail.ru Модератор Новости
tolstoy@mail.ru Модератор Флуд
saltykov@inbox.ru Модератор Флуд
saltykov@inbox.ru Модератор Юмор
fmd@mail.ru Игрок E-Mail
sukin_syn@mail.ru Админ Поэзия

4-ая нормальная форма

Переменная отношения находится в четвёртой нормальной форме, если она находится в третьей нормальной форме и не содержит нетривиальных многозначных зависимостей.

E-Mail Привилегии
tolstoy@mail.ru Админ
tolstoy@mail.ru Модератор
saltykov@inbox.ru Модератор
fmd@mail.ru Игрок
sukin_syn@mail.ru Админ
E-Mail Разделы
tolstoy@mail.ru Новости
tolstoy@mail.ru Флуд
saltykov@inbox.ru Флуд
saltykov@inbox.ru Юмор
fmd@mail.ru E-Mail
sukin_syn@mail.ru Поэзия

Реляционная часть

CREATE TABLE

CREATE [ { TEMPORARY } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name ( [
  { column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ]
    | table_constraint
    | LIKE source_table [ like_option ... ] }
    [, ... ]
] )
[ INHERITS ( parent_table [, ... ] ) ]
[ WITH ( storage_parameter [= value] [, ... ] ) | WITH OIDS | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
[ TABLESPACE tablespace_name ]

Or:

CREATE [ { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name
    OF type_name [ (
  { column_name WITH OPTIONS [ column_constraint [ ... ] ]
    | table_constraint }
    [, ... ]
) ]
[ WITH ( storage_parameter [= value] [, ... ] ) | WITH OIDS | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
[ TABLESPACE tablespace_name ]

CREATE TABLE

column_constraint:

[ CONSTRAINT constraint_name ]
{ NOT NULL |
  NULL |
  DEFAULT default_expr |

  CHECK ( expression ) [ NO INHERIT ] |
  UNIQUE index_parameters |
  PRIMARY KEY index_parameters |
  REFERENCES reftable [ ( refcolumn ) ]
    [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
    [ ON DELETE action ] [ ON UPDATE action ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]

CREATE TABLE

table_constraint:

[ CONSTRAINT constraint_name ]
{ CHECK ( expression ) [ NO INHERIT ] |
  UNIQUE ( column_name [, ... ] ) index_parameters |
  PRIMARY KEY ( column_name [, ... ] ) index_parameters |
  FOREIGN KEY ( column_name [, ... ] )
    REFERENCES reftable [ ( refcolumn [, ... ] ) ]
    [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
    [ ON DELETE action ] [ ON UPDATE action ] }
  EXCLUDE [ USING index_method ] ( exclude_element WITH operator [, ... ] )
    index_parameters [ WHERE ( predicate ) ] |
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]

Обновление кортежа в родительском отношении (action)

NO ACTION
Не разрешать изменение
RESTRICT
То же, что и NO ACTION, но всегда IMMEDIATE
CASCADE
Изменить каскадно
SET NULL
Установить в NULL
SET DEFAULT
Установить значение по умолчанию

CREATE TABLE (пример)

CREATE TABLE films (
    code        char(5) CONSTRAINT firstkey PRIMARY KEY,
    title       varchar(40) NOT NULL,
    date_prod   date,
    kind        varchar(10),
    len         interval hour to minute,
    imdb        varchar(20) NULL,
    CONSTRAINT uniq_imdb UNIQUE(imdb)
);

CREATE TABLE distributors (
     did    integer PRIMARY KEY DEFAULT nextval('serial'),
     name   varchar(40) NOT NULL CHECK (name <> '')
);

ALTER TABLE

ALTER TABLE [ IF EXISTS ] [ ONLY ] name [ * ]
    action [, ... ]
ALTER TABLE [ IF EXISTS ] [ ONLY ] name [ * ]
    RENAME [ COLUMN ] column_name TO new_column_name
ALTER TABLE [ IF EXISTS ] [ ONLY ] name [ * ]
    RENAME CONSTRAINT constraint_name TO new_constraint_name
ALTER TABLE [ IF EXISTS ] name
    RENAME TO new_name

action:

ADD [ COLUMN ] [ IF NOT EXISTS ] column_name data_type [ ... ]
DROP [ COLUMN ] [ IF EXISTS ] column_name [ RESTRICT | CASCADE ]
ALTER [ COLUMN ] column_name [ SET DATA ] TYPE data_type [ USING expression ]
ALTER [ COLUMN ] column_name SET DEFAULT expression
ALTER [ COLUMN ] column_name DROP DEFAULT
ADD table_constraint [ NOT VALID ]
DROP CONSTRAINT [ IF EXISTS ]  constraint_name [ RESTRICT | CASCADE ]
...

ALTER TABLE (пример)

ALTER TABLE distributors
    ADD COLUMN address varchar(30),
    DROP COLUMN address RESTRICT,
    ALTER COLUMN address TYPE varchar(80),
    ALTER COLUMN name TYPE varchar(100);

ALTER TABLE foo
    ALTER COLUMN foo_timestamp DROP DEFAULT,
    ALTER COLUMN foo_timestamp TYPE timestamp with time zone
    USING
        timestamp with time zone 'epoch'
        + foo_timestamp * interval '1 second',
    ALTER COLUMN foo_timestamp SET DEFAULT now();

information_schema

information_schema.tables

information_schema.columns

SELECT

[ WITH [ RECURSIVE ] with_query [, ...] ]
SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ]
    [ * | expression [ [ AS ] output_name ] [, ...] ]
    [ FROM from_item [, ...] ]
    [ WHERE condition ]
    [ GROUP BY grouping_element [, ...] ]
    [ HAVING condition [, ...] ]
    [ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] select ]
    [ ORDER BY expression [ ASC | DESC | USING operator ] [, ...] ]
    [ LIMIT { count | ALL } ]
    [ OFFSET start [ ROW | ROWS ] ]
    [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ]
    [ FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE }
        [ OF table_name [, ...] ] [ NOWAIT | SKIP LOCKED ] [...] ]

FROM. Типы таблиц

WHERE. Фильтрация

SELECT *
FROM persons
WHERE department_id IN (
    SELECT id FROM departments WHERE company_id = 42
)

JOIN-ы

CASE

CASE WHEN condition THEN result
     [WHEN ...]
     [ELSE result]
END
NULLIF(value1, value2)
GREATEST(value [, ...])
LEAST(value [, ...])

SELECT a,
   CASE WHEN a=1 THEN 'one'
        WHEN a=2 THEN 'two'
        ELSE 'other'
   END
FROM test;

GROUP BY. Формирование групп

GROUP BY. Формирование групп

SELECT * FROM employee;
id name department grade
1 John HR A
2 Mark HR A
3 Smit IT A
4 Lili IT B
5 Alex PR B
6 Walton HR B
7 Kim PR B
8 Marry HR A
9 Paul PR C
10 Christ HR A
11 Jiji HR B
12 Bob PR B

GROUP BY. Формирование групп

SELECT grade, department, COUNT(*)
FROM employee
GROUP BY grade, department
ORDER BY grade, department;
grade department COUNT(*)
A HR 4
A IT 1
B HR 2
B IT 1
B PR 3
C PR 1

Агрегаторы

aggregate_name (
    [ ALL | DISTINCT ] expression [ , ... ]
    [ order_by_clause ]
) [ FILTER ( WHERE filter_clause ) ]

SELECT
  string_agg(name, ',' ORDER BY name),
  avg(age),
  count(*) FILTER (WHERE age BETWEEN 13 AND 19) AS teens
FROM persons;

Агрегаторы

Функция Описание
avg(expression) Среднее арифмитическое
max(expression) Максимальное значение
min(expression) Минимальное значение
sum(expression) Сумма
count(*) Кол-во строк
count(expression) Кол-во не NULL-значений

Агрегаторы

Функция Описание
bit_and(expression) Битовое "И"
bit_or(expression) Битовое "ИЛИ"
bool_and(expression), every(expression) Логическое "И"
bool_or(expression) Логическое "ИЛИ"
stddev(expression) Стандартное отклонение
variance(expression) Дисперсия (квадрат отклонения)

Агрегаторы

Функция Описание
array_agg(expression) Сформировать массив значений
json_agg(expression) Сформировать JSON-массив
jsonb_agg(expression) Сформировать JSONB-массив
json_object_agg(key, value) Сформировать JSON-объект
jsonb_object_agg(key, value) Сформировать JSONB-объект
string_agg(expression, delimiter) Конкатенация строк
xmlagg(expression) Конкатенация XML-элементов

HAVING. Фильтрация после группировки

SELECT grade, department, COUNT(*)
FROM employee
GROUP BY grade, department
HAVING COUNT(*) > 3;

SELECT * FROM (
    SELECT grade, department, COUNT(*) as employee_cnt
    FROM employee
    GROUP BY grade, department
) a
WHERE employee_cnt > 3;

ORDER BY. Сортировка

SELECT select_list
FROM table_expression
ORDER BY sort_expression1 [ASC | DESC] [NULLS { FIRST | LAST }]
      [, sort_expression2 [ASC | DESC] [NULLS { FIRST | LAST }] ...]

SELECT a, b FROM table1 ORDER BY a + b, c;

SELECT a + b AS sum, c FROM table1 ORDER BY sum;
SELECT a, max(b) FROM table1 GROUP BY a ORDER BY 1;

# SELECT a + b AS sum, c FROM table1 ORDER BY sum + c;
ERROR:  column "sum" does not exist
LINE 1: SELECT a + b AS sum, c FROM table1 ORDER BY sum + c;
                                                    ^

LIMIT. Разбивка на страницы

ISO SQL:2003 - Window Functions:

SELECT * FROM (
  SELECT
    ROW_NUMBER() OVER (ORDER BY age ASC) AS rownumber,
    person_id, person_name, age
  FROM person
) AS foo
WHERE rownumber <= 10;

SELECT * FROM (
  SELECT
    RANK() OVER (ORDER BY age ASC) AS ranking,
    person_id, person_name, age
  FROM person
) AS foo
WHERE ranking <= 10;

LIMIT. Разбивка на страницы

Синтаксис MySQL:

SELECT
  person_id, person_name, age
FROM person
LIMIT 10
OFFSET 20;

Синтаксис DB2:

SELECT
  person_id, person_name, age
FROM person
FETCH FIRST 10 ROWS ONLY;

WITH

WITH regional_sales AS (
        SELECT region, SUM(amount) AS total_sales
        FROM orders
        GROUP BY region
     ),
     top_regions AS (
        SELECT region
        FROM regional_sales
        WHERE total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales)
     )
SELECT region,
       product,
       SUM(quantity) AS product_units,
       SUM(amount) AS product_sales
FROM orders
WHERE region IN (SELECT region FROM top_regions)
GROUP BY region, product;

WITH RECURSIVE

WITH RECURSIVE recursetree (id, parent_id, level, name) AS (
    SELECT id, parent_id, 0, name FROM tree
    WHERE parent_id = 0
  UNION ALL
    SELECT t.id, t.parent_id, level+1, t.name
    FROM tree t
    JOIN recursetree rt ON rt.id = t.parent_id
  )
SELECT * FROM recursetree;

id parent level name
1 0 0 Россия
2 0 0 США
3 1 1 Москва
4 2 1 Нью Йорк
5 2 1 Вашингтон
6 3 2 Бутово

WITH RECURSIVE

WITH RECURSIVE recursetree (id, path, name) AS (
    SELECT id, array_append('{}'::int[], id), name FROM tree
    WHERE parent_id = 0
  UNION ALL
    SELECT t.id, array_append(path, t.id), t.name
    FROM tree t
    JOIN recursetree rt ON rt.id = t.parent_id
  )
SELECT id, array_to_string(path, '.') as path, name FROM recursetree
ORDER BY path;
id path name
1 1 Россия
3 1.3 Москва
6 1.3.6 Бутово
2 2 США
4 2.4 Нью Йорк
5 2.5 Вашингтон

INSERT

[ WITH [ RECURSIVE ] with_query [, ...] ]
INSERT INTO table_name [ AS alias ] [ ( column_name [, ...] ) ]
    { DEFAULT VALUES | VALUES ( { expression | DEFAULT } [, ...] ) [, ...] | query }
    [ ON CONFLICT [ conflict_target ] conflict_action ]
    [ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ]

conflict_target:

( { index_column_name | ( index_expression ) } ... [, ...] )
[ WHERE index_predicate ]
ON CONSTRAINT constraint_name

conflict_target:

DO NOTHING
DO UPDATE SET { column_name = { expression | DEFAULT } |
                ( column_name [, ...] ) = ( { expression | DEFAULT } [, ...] ) |
                ( column_name [, ...] ) = ( sub-SELECT )
              } [, ...]
          [ WHERE condition ]

INSERT

INSERT INTO films (code, title, did, date_prod, kind) VALUES
('B6717', 'Tampopo', 110, '1985-02-10', 'Comedy'),
('HG120', 'The Dinner Game', 140, DEFAULT, 'Comedy')
RETURNING *;

INSERT INTO films
SELECT * FROM tmp_films WHERE date_prod < '2004-05-07';

INSERT INTO distributors (id, dname)
VALUES (5, 'Gizmo Transglobal'), (6, 'Associated Computing, Inc')
ON CONFLICT (id) DO UPDATE SET dname = EXCLUDED.dname;

INSERT INTO distributors (id, dname) VALUES (7, 'Redline GmbH')
ON CONFLICT (id) DO NOTHING;

UPDATE

[ WITH [ RECURSIVE ] with_query [, ...] ]
UPDATE [ ONLY ] table_name [ * ] [ [ AS ] alias ]
    SET { column_name = { expression | DEFAULT } |
          ( column_name [, ...] ) = ( { expression | DEFAULT } [, ...] ) |
          ( column_name [, ...] ) = ( sub-SELECT )
        } [, ...]
    [ FROM from_list ]
    [ WHERE condition | WHERE CURRENT OF cursor_name ]
    [ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ]

UPDATE

UPDATE table1
SET
    col_name1 = col_name2,
    col_name2 = col_name1
WHERE id = 42;

UPDATE weather
  SET temp_lo = temp_lo+1, temp_hi = temp_lo+15, prcp = DEFAULT
  WHERE city = 'San Francisco' AND date = '2003-07-03'
  RETURNING temp_lo, temp_hi, prcp;

UPDATE weather
  SET (temp_lo, temp_hi, prcp) = (temp_lo+1, temp_lo+15, DEFAULT)
  WHERE city = 'San Francisco' AND date = '2003-07-03'
  RETURNING temp_lo, temp_hi, prcp;

UPDATE


UPDATE accounts SET
  contact_first_name = (
    SELECT first_name FROM salesmen WHERE salesmen.id = accounts.sales_id
  ),
  contact_last_name = (
    SELECT last_name  FROM salesmen WHERE salesmen.id = accounts.sales_id
  );

UPDATE accounts SET (contact_first_name, contact_last_name) =
    (SELECT first_name, last_name FROM salesmen
     WHERE salesmen.id = accounts.sales_id);

UPDATE accounts SET contact_first_name = first_name,
                    contact_last_name = last_name
  FROM salesmen WHERE salesmen.id = accounts.sales_id;

DELETE

[ WITH [ RECURSIVE ] with_query [, ...] ]
DELETE FROM [ ONLY ] table_name [ * ] [ [ AS ] alias ]
    [ USING using_list ]
    [ WHERE condition | WHERE CURRENT OF cursor_name ]
    [ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ]

DELETE FROM films;

TRUNCATE [ TABLE ] [ ONLY ] name [ * ] [, ... ]
    [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]

TRUNCATE TABLE films;

DELETE

DELETE FROM films WHERE kind <> 'Musical';

DELETE FROM tasks WHERE status = 'DONE' RETURNING *;

DELETE FROM films
  WHERE producer_id IN (SELECT id FROM producers WHERE name = 'foo');

DELETE FROM films USING producers
  WHERE producer_id = producers.id AND producers.name = 'foo';

Список смежных вершин (Adjacency List)

Список смежных вершин (Adjacency List)

Главный недостаток такого подхода — необходимо достоверно знать количество уровней вложенности в вашей иерархии, кроме того, чем больше иерархия, тем больше JOIN'ов — тем ниже производительность.

Тем не менее, данный способ обладает и существенными достоинствами — в дерево легко вносить изменения, менять местами и удалять узлы.

Данный алгоритм хорошо применим, если вы оперируете с небольшими древовидными структурами, которые часто поддаются изменениям.

С другой стороны, этот алгоритм также довольно уверенно себя чувствует и с большими деревьями, если считывать их порциями вида «знаю родителя — прочитать всех наследников». Хороший пример такого случая — динамически подгружаемые деревья.

Однако он плохо применим, когда нужно вычитывать какие-либо иные куски дерева, находить пути, предыдущие и следующие узлы при обходе и вычитывать ветки дерева целиком (на всю глубину).

Вложенное множество (Nested Set)

Вложенное множество (Nested Set)

Nested Set действительно хорош, когда нам необходимо считывать структуру деревьев из БД. При этом он одинаково хорош для деревьев любого объема.

Тем не менее, для иерархических структур, которые подвергаются частому изменению он, очевидно, не будет являться оптимальным выбором.

Материализованный путь (Materialized Path)

Материализованный путь (Materialized Path)

Во-первых, по сравнению с Nested Set, он более поддается изменениям. В то же время остается достаточно удобным для выборки деревьев целиком и их частей. Но, и он не идеален. Особенно по части поиска предков ветки.

Использование именно этого алгоритма может быть заметно удобнее, для деревьев, над которыми часто выполняются как операции чтения, так и изменения.

Алгоритм довольно уверенно себя чувствует на достаточно больших объемах данных.

Наиболее неприятной в данном алгоритме будет операция вставки узла в середину уже существующей структуры и перенос одной ветки в другую.

А вот удаление, добавление в конец или изменение узла — это операции довольно простые, и, как правило, не вызывают сложностей в данной модели.

Комбинированный подход

По сути вопроса следует отметить, что скомбинировать приведенные методы можно лишь в двух направлениях:

Комбинировать же Nested Set и Materialized Path особого смысла не имеет, т.к. существенного выигрыша ни в чтении, ни в записи вы не получите.

Adjacency List + Materialized Path

Для AL при использовании с MP:

Для MP при использовании с AL:

Adjacency List + Nested Set

Для связки AL+NS взаимовыгодность не столь очевидна.

В первую очередь это объясняется тем, что недостатки от проблем изменения узлов дерева в модели NS напрочь убивают в этой сфере все достоинства AL.

Это значит, что такую связку следует рассматривать лишь как качественное улучшение поиска родителей и наследников заданного узла в алгоритме NS, а также как повышение надежности самого алгоритма (ключи можно всегда перестроить в случае порчи — информацию о связях хранит AL).

Но ведь и это качественное улучшение, хотя и не такое очевидное.

Навроцкий Артем
E-mail: bozaro@yandex.ru
Спасибо за внимание!