Простая сортировка обычно даёт не тот результат, который ожидается пользователем.
ucs_basic (C) | en_US.utf8 (ISO-14651) | ru_RU.utf8 |
---|---|---|
_ель | ґвалт | джаз |
Есенин | джаз | ґвалт |
джаз | ель | ель |
ель | _ель | _ель |
жаркое | Есенин | Есенин |
ёжик | ёжик | ёжик |
ґвалт | жаркое | жаркое |
В разных языках разные преобразования в верхний/нижний регистр
SELECT
LOWER('WINDOWS' COLLATE "en_US.utf8"),
UPPER('linux' COLLATE "en_US.utf8")
lower | upper |
---|---|
windows | LINUX |
SELECT
LOWER('WINDOWS' COLLATE "tr_TR.utf8"),
UPPER('linux' COLLATE "tr_TR.utf8")
lower | upper |
---|---|
wındows | LİNUX |
В PostgreSQL поддержка COLLATION реализована на базе C-функции strcoll_l.
Это порождает ряд проблем:
С PostgreSQL 10 (окт 2019) появилась поддержка COLLATION на базе библиотеки ICU.
CREATE TABLE person (
id SERIAL PRIMARY KEY,
name TEXT COLLATE "ru_RU.utf8",
...
);
CREATE UNIQUE INDEX uniq_name ON person (LOWER(name));
SELECT * FROM person WHERE LOWER(name) = LOWER('Jack Sparrow');
Проблемы:
CREATE EXTENSION IF NOT EXISTS citext;
CREATE TABLE person (
id SERIAL PRIMARY KEY,
name CITEXT COLLATE "ru_RU.utf8",
...
);
CREATE UNIQUE INDEX uniq_name ON person (name);
SELECT * FROM person WHERE name = 'Jack Sparrow';
[ 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 ] [...] ]
SELECT
col0,
(SELECT col1 FROM table1 WHERE table1.id = table0.id),
(SELECT col2 FROM table1 WHERE table1.id = table0.id)
FROM
table0;
SELECT *
FROM t1
WHERE column1 = (SELECT MAX(column2) FROM t2);
SELECT *
FROM t1 AS t
WHERE 2 = (SELECT COUNT(*) FROM t1 WHERE t1.id = t.id);
SELECT s1 FROM t1 WHERE s1 > ANY (SELECT s1 FROM t2);
SELECT s1 FROM t1 WHERE s1 = ANY (SELECT s1 FROM t2);
SELECT s1 FROM t1 WHERE s1 IN (SELECT s1 FROM t2);
SELECT s1 FROM t1 WHERE s1 > ALL (SELECT s1 FROM t2);
SELECT s1 FROM t1 WHERE s1 <> ALL (SELECT s1 FROM t2);
SELECT s1 FROM t1 WHERE s1 NOT IN (SELECT s1 FROM t2);
SELECT *
FROM t1
WHERE (col1, col2)
= (SELECT col3, col4 FROM t2 WHERE id = 10);
SELECT *
FROM t1
WHERE ROW (col1, col2)
= (SELECT col3, col4 FROM t2 WHERE id = 10);
SELECT column1, column2, column3
FROM t1
WHERE (column1, column2, column3)
IN (SELECT column1, column2, column3 FROM t2);
SELECT DISTINCT store_type
FROM stores
WHERE EXISTS (
SELECT *
FROM cities_stores
WHERE cities_stores.store_type = stores.store_type
);
SELECT DISTINCT store_type
FROM stores
WHERE NOT EXISTS (
SELECT *
FROM cities_stores
WHERE cities_stores.store_type = stores.store_type
);
SELECT ... FROM (subquery) [AS] name ...
SELECT AVG(sum_column1)
FROM (
SELECT SUM(column1) AS sum_column1
FROM t1
GROUP BY column1
) AS t1;
SELECT
ROW_NUMBER() OVER (ORDER BY age ASC) AS rownumber,
person_id, person_name, age
FROM person;
SELECT
ROW_NUMBER() OVER w AS rownumber,
RANK() OVER w AS ranking,
person_id, person_name, age
FROM person
WINDOW w AS (ORDER BY age ASC);
SELECT
count(*) AS unfiltered,
count(*) FILTER (WHERE i < 5) AS filtered
FROM generate_series(1,10) AS s(i);
Функция | Описание |
---|---|
row_number() | номер текущей строки в её разделе, начиная с 1 |
rank() | ранг текущей строки с пропусками; то же, что и row_number для первой родственной ей строки |
dense_rank() | ранг текущей строки без пропусков; эта функция считает группы родственных строк |
percent_rank() | относительный ранг текущей строки: (rank - 1) / (общее число строк - 1) |
first_value(any) | возвращает значение, вычисленное для первой строки в рамке окна |
last_value(any) | возвращает значение, вычисленное для последней строки в рамке окна |
|
возвращает значение, вычисленное в н-ой строке в рамке окна (считая с 1), или NULL, если такой строки нет. |
SELECT DISTINCT
department_id,
FIRST_VALUE(person_name) OVER w,
FIRST_VALUE(age) OVER w
FROM person
WINDOW w as (PARTITION BY department_id);
SELECT DISTINCT ON (department_id)
department_id,
person_name,
age
FROM person;
SELECT ...
UNION [ALL | DISTINCT]
SELECT ...
[UNION [ALL | DISTINCT]
SELECT ...]
person | amount |
---|---|
Joe | 1000 |
Alex | 2000 |
Bob | 5000 |
person | amount |
---|---|
Joe | 2000 |
Alex | 2000 |
Zach | 35000 |
SELECT * FROM sales2010
UNION
SELECT * FROM sales2011;
person | amount |
---|---|
Alex | 2000 |
Bob | 5000 |
Joe | 1000 |
Joe | 2000 |
Zach | 35000 |
SELECT * FROM sales2010
UNION ALL
SELECT * FROM sales2011;
person | amount |
---|---|
Joe | 1000 |
Alex | 2000 |
Bob | 5000 |
Joe | 2000 |
Alex | 2000 |
Zach | 35000 |
Employee | |
---|---|
LastName | DepartmentID |
Rafferty | 31 |
Jones | 33 |
Steinberg | 33 |
Robinson | 34 |
Smith | 34 |
John |
|
Department | |
---|---|
DepartmentID | DepartmentName |
31 | Sales |
33 | Engineering |
34 | Clerical |
35 | Marketing |
SELECT * FROM Employee CROSS JOIN Department;
Employee. LastName |
Employee. DepartmentID |
Department. DepartmentID |
Department. DepartmentName |
---|---|---|---|
Rafferty | 31 | 31 | Sales |
Jones | 33 | 31 | Sales |
Steinberg | 33 | 31 | Sales |
Robinson | 34 | 31 | Sales |
Smith | 34 | 31 | Sales |
John |
|
31 | Sales |
Rafferty | 31 | 33 | Engineering |
Jones | 33 | 33 | Engineering |
Steinberg | 33 | 33 | Engineering |
Robinson | 34 | 33 | Engineering |
Smith | 34 | 33 | Engineering |
John |
|
33 | Engineering |
Rafferty | 31 | 34 | Clerical |
Jones | 33 | 34 | Clerical |
Steinberg | 33 | 34 | Clerical |
Robinson | 34 | 34 | Clerical |
Smith | 34 | 34 | Clerical |
John |
|
34 | Clerical |
Rafferty | 31 | 35 | Marketing |
Jones | 33 | 35 | Marketing |
Steinberg | 33 | 35 | Marketing |
Robinson | 34 | 35 | Marketing |
Smith | 34 | 35 | Marketing |
John |
|
35 | Marketing |
SELECT * FROM Employee, Department;
Employee. LastName |
Employee. DepartmentID |
Department. DepartmentID |
Department. DepartmentName |
---|---|---|---|
Rafferty | 31 | 31 | Sales |
Jones | 33 | 31 | Sales |
Steinberg | 33 | 31 | Sales |
Robinson | 34 | 31 | Sales |
Smith | 34 | 31 | Sales |
John |
|
31 | Sales |
Rafferty | 31 | 33 | Engineering |
Jones | 33 | 33 | Engineering |
Steinberg | 33 | 33 | Engineering |
Robinson | 34 | 33 | Engineering |
Smith | 34 | 33 | Engineering |
John |
|
33 | Engineering |
Rafferty | 31 | 34 | Clerical |
Jones | 33 | 34 | Clerical |
Steinberg | 33 | 34 | Clerical |
Robinson | 34 | 34 | Clerical |
Smith | 34 | 34 | Clerical |
John |
|
34 | Clerical |
Rafferty | 31 | 35 | Marketing |
Jones | 33 | 35 | Marketing |
Steinberg | 33 | 35 | Marketing |
Robinson | 34 | 35 | Marketing |
Smith | 34 | 35 | Marketing |
John |
|
35 | Marketing |
SELECT *
FROM Employee
INNER JOIN Department
ON Employee.DepartmentID = Department.DepartmentID;
Employee. LastName |
Employee. DepartmentID |
Department. DepartmentID |
Department. DepartmentName |
---|---|---|---|
Rafferty | 31 | 31 | Sales |
Jones | 33 | 33 | Engineering |
Steinberg | 33 | 33 | Engineering |
Robinson | 34 | 34 | Clerical |
Smith | 34 | 34 | Clerical |
SELECT *
FROM Employee, Department
WHERE Employee.DepartmentID = Department.DepartmentID;
Employee. LastName |
Employee. DepartmentID |
Department. DepartmentID |
Department. DepartmentName |
---|---|---|---|
Rafferty | 31 | 31 | Sales |
Jones | 33 | 33 | Engineering |
Steinberg | 33 | 33 | Engineering |
Robinson | 34 | 34 | Clerical |
Smith | 34 | 34 | Clerical |
SELECT *
FROM Employee
NATURAL JOIN Department;
DepartmentID | Employee. LastName |
Department. DepartmentName |
---|---|---|
31 | Rafferty | Sales |
33 | Jones | Engineering |
33 | Steinberg | Engineering |
34 | Robinson | Clerical |
34 | Smith | Clerical |
SELECT *
FROM Employee
JOIN Department USING (DepartmentID);
DepartmentID | Employee. LastName |
Department. DepartmentName |
---|---|---|
31 | Rafferty | Sales |
33 | Jones | Engineering |
33 | Steinberg | Engineering |
34 | Robinson | Clerical |
34 | Smith | Clerical |
SELECT *
FROM Employee
LEFT OUTER JOIN Department
ON Employee.DepartmentID = Department.DepartmentID;
Employee. LastName |
Employee. DepartmentID |
Department. DepartmentID |
Department. DepartmentName |
---|---|---|---|
Rafferty | 31 | 31 | Sales |
Jones | 33 | 33 | Engineering |
Steinberg | 33 | 33 | Engineering |
John |
|
|
|
Robinson | 34 | 34 | Clerical |
Smith | 34 | 34 | Clerical |
SELECT Employee.*, Department.*
FROM Department
RIGHT OUTER JOIN Employee
ON Employee.DepartmentID = Department.DepartmentID;
Employee. LastName |
Employee. DepartmentID |
Department. DepartmentID |
Department. DepartmentName |
---|---|---|---|
Rafferty | 31 | 31 | Sales |
Jones | 33 | 33 | Engineering |
Steinberg | 33 | 33 | Engineering |
John |
|
|
|
Robinson | 34 | 34 | Clerical |
Smith | 34 | 34 | Clerical |
SELECT *
FROM Employee
FULL OUTER JOIN Department
ON Employee.DepartmentID = Department.DepartmentID;
Employee. LastName |
Employee. DepartmentID |
Department. DepartmentID |
Department. DepartmentName |
---|---|---|---|
Rafferty | 31 | 31 | Sales |
Jones | 33 | 33 | Engineering |
Steinberg | 33 | 33 | Engineering |
John |
|
|
|
Robinson | 34 | 34 | Clerical |
Smith | 34 | 34 | Clerical |
|
|
35 | Marketing |
SELECT *
FROM Employee
FULL OUTER JOIN Department
ON Employee.DepartmentID = Department.DepartmentID;
SELECT *
FROM Employee
LEFT OUTER JOIN Department
ON Employee.DepartmentID = Department.DepartmentID;
UNION ALL
SELECT *
FROM Employee
RIGHT OUTER JOIN Department
ON Employee.DepartmentID = Department.DepartmentID
WHERE Employee.DepartmentID IS NULL;
SELECT F.EmployeeID, F.LastName, S.EmployeeID, S.LastName, F.Country
FROM Employee F
INNER JOIN Employee S ON F.Country = S.Country
WHERE F.EmployeeID < S.EmployeeID
ORDER BY F.EmployeeID, S.EmployeeID;
EmployeeID | LastName | Country | DepartmentID |
---|---|---|---|
123 | Rafferty | 31 | Australia |
124 | Jones | 33 | Australia |
145 | Steinberg | 33 | Australia |
201 | Robinson | 34 | United States |
305 | Smith | 34 | Germany |
306 | John |
|
Germany |
EmployeeID | LastName | DepartmentID | Country |
---|---|---|---|
123 | Rafferty | 31 | Australia |
124 | Jones | 33 | Australia |
145 | Steinberg | 33 | Australia |
201 | Robinson | 34 | United States |
305 | Smith | 34 | Germany |
306 | John |
|
Germany |
F.EmployeeID | F.LastName | S.EmployeeID | S.LastName | F.Country |
---|---|---|---|---|
123 | Rafferty | 124 | Jones | Australia |
123 | Rafferty | 145 | Steinberg | Australia |
124 | Jones | 145 | Steinberg | Australia |
305 | Smith | 306 | John | Germany |
SELECT E.*
FROM Employee E WHERE EXISTS (
SELECT *
FROM Department D WHERE D.DepartmentID = E.DepartmentID
);
SELECT E.*
FROM Employee E WHERE E.DepartmentID IN (
SELECT DepartmentID
FROM Department D
);
SELECT E.*
FROM Employee E
JOIN Department D ON (E.DepartmentID = D.DepartmentID);
n |
---|
1 |
2 |
3 |
4 |
5 |
SELECT 1 AS n
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
UNION ALL
SELECT 5;
n |
---|
1 |
2 |
3 |
4 |
5 |
WITH RECURSIVE series(n) AS (
SELECT 1
UNION ALL
SELECT n + 1 FROM series WHERE n < 5
)
SELECT * FROM series ORDER BY n;
SELECT n FROM unnest(ARRAY[
1, 2, 3, 4, 5
]) n ORDER BY n;
SELECT n
FROM generate_series(1, 5) n;
a | b |
---|---|
1 | foo |
2 | bar |
3 | baz |
SELECT * FROM unnest(ARRAY[
1, 2, 3
], ARRAY[
'foo', 'bar', 'baz'
]) n (a, b) ORDER BY a;
SELECT * FROM (
VALUES (1, 'foo'), (2, 'bar'), (3, 'baz')
) n (a, b)
CREATE TABLE city (
id bigserial NOT NULL PRIMARY KEY,
region_id bigint NOT NULL,
name varchar(128) NOT NULL
);
SELECT nextval(pg_get_serial_sequence('city', 'id'));
SELECT nextval(pg_get_serial_sequence('city', 'id'))
FROM generate_series(1, 100);
SELECT col1 FROM tbl
UNION ALL
SELECT col2 FROM tbl;
SELECT unnest(array[col1, col2]) as col1 FROM tbl;
WITH RECURSIVE recursetree (id, path, name) AS (
SELECT id, array[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 | Вашингтон |
Надо всем аватарам, у которых сдан квест на "Кащея" отправить "Меч кладенец" по игровой почте.
CREATE TABLE avatar (
id SERIAL PRIMARY KEY,
name VARCHAR(64)
);
CREATE TABLE quest (
id BIGSERIAL PRIMARY KEY,
quest_rid INT,
avatar_id INT REFERENCES avatar (id) ON DELETE CASCADE
);
CREATE TABLE mail (
id SERIAL PRIMARY KEY,
recipient_id INT REFERENCES avatar (id) ON DELETE CASCADE,
mail_rid INT
);
CREATE TABLE item (
id BIGSERIAL PRIMARY KEY,
mail_id INT REFERENCES mail (id) ON DELETE CASCADE,
item_rid INT
);
Надо всем аватарам, у которых сдан квест на "Кащея" отправить "Меч кладенец" по игровой почте.
-- Получить всех, кто выполнил квест
SELECT * FROM quest q WHERE q.quest_rid = 42;
-- Создать письма
INSERT INTO mail (recipient_id, mail_rid)
SELECT q.avatar_id, 17
FROM quest q WHERE q.quest_rid = 42;
-- Создать предметы в письмах
INSERT INTO item (mail_id, item_rid)
SELECT mail_id, 7291
FROM mail m WHERE ???;
Надо всем аватарам, у которых сдан квест на "Кащея" отправить "Меч кладенец" по игровой почте.
--- Сохраняем номер последнего добавленного письма
CREATE TEMPORARY TABLE tmp_mail ON COMMIT DROP AS
SELECT GREATEST(MAX(id), 0) as last_id FROM mail;
-- Создать письма
INSERT INTO mail (recipient_id, mail_rid)
SELECT q.avatar_id, 17
FROM quest q WHERE q.quest_rid = 42;
-- Создать предметы в письмах
INSERT INTO item (mail_id, item_rid)
SELECT m.id, 7291
FROM mail m WHERE m.id > (SELECT last_id FROM tmp_mail);
Надо всем аватарам, у которых сдан квест на "Кащея" отправить "Меч кладенец" по игровой почте.
-- Создать письма и сохранить их идентификаторы
CREATE TEMPORARY TABLE tmp_mail ON COMMIT DROP AS
WITH new_mail AS (
INSERT INTO mail (recipient_id, mail_rid)
SELECT q.avatar_id, 17
FROM quest q WHERE q.quest_rid = 42
RETURNING id
)
SELECT * FROM new_mail;
-- Создать предметы в письмах
INSERT INTO item (mail_id, item_rid)
SELECT m.id, 7291
FROM mail m WHERE m.id > (SELECT last_id FROM tmp_mail);
Надо всем аватарам, у которых сдан квест на "Кащея" отправить "Меч кладенец" по игровой почте.
-- Создать письма и предметы в письмах
WITH new_mail AS (
INSERT INTO mail (recipient_id, mail_rid)
SELECT q.avatar_id, 17
FROM quest q WHERE q.quest_rid = 42
RETURNING id
)
INSERT INTO item (mail_id, item_rid)
SELECT m.id, 7291
FROM new_mail m;
Найти имена и классы, которые имеют друзей только в том же классе. Вернуть результат, отсортированный по классу, затем имени в классе.
CREATE TABLE highschooler (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(128),
grade INT
);
CREATE TABLE friend (
id1 INT REFERENCES highschooler (id) ON DELETE CASCADE,
id2 INT REFERENCES highschooler (id) ON DELETE CASCADE
);
Дружба взаимная, если есть запись (123, 456), то есть и (456, 123).
Найти имена и классы, которые имеют друзей только в том же классе. Вернуть результат, отсортированный по классу, затем имени в классе.
Найти имена и классы, которые имеют друзей только в том же классе. Вернуть результат, отсортированный по классу, затем имени в классе.
Есть в том же классе и нет в других классах
==
Есть друзья и нет в других классах
Найти имена и классы, которые имеют друзей только в том же классе. Вернуть результат, отсортированный по классу, затем имени в классе.
SELECT H.name, H.grade
FROM highschooler AS H
JOIN friend AS F ON (H.Id = F.Id1)
LEFT JOIN highschooler AS O ON (F.Id2 = O.Id AND H.Grade <> O.Grade)
SELECT H.name, H.grade
FROM highschooler AS H
JOIN friend AS F ON (H.Id = F.Id1)
LEFT JOIN highschooler AS O ON (F.Id2 = O.Id AND H.Grade <> O.Grade)
GROUP BY H.grade, H.name, H.id
HAVING COUNT(O.Id) = 0
ORDER BY H.grade, H.name
Найти разницу между средней оценкой фильмов выпущенных до 1980 года, и средней оценкой фильмов выпущенных после 1980 года.
CREATE TABLE movie (
mID SERIAL PRIMARY KEY,
title VARCHAR(128),
year INT,
director VARCHAR(128)
);
CREATE TABLE rating (
rID SERIAL PRIMARY KEY,
mID INT REFERENCES movie (mID) ON DELETE CASCADE,
stars INT,
ratingDate TIMESTAMP
);
Найти разницу между средней оценкой фильмов выпущенных до 1980 года, и средней оценкой фильмов выпущенных после 1980 года.
SELECT
AVG(CASE WHEN year > 1980 THEN stars ELSE NULL END) AS after,
AVG(CASE WHEN year < 1980 THEN stars ELSE NULL END) AS before
FROM (
SELECT M.mID, M.year, AVG(R.stars) as stars
FROM movie M
JOIN rating R USING (mID)
GROUP BY M.mID, M.year
) AS R
SELECT
AVG(CASE WHEN year > 1980 THEN stars ELSE NULL END) AS after,
AVG(CASE WHEN year < 1980 THEN stars ELSE NULL END) AS before
FROM (
SELECT M.mID, M.year, AVG(R.stars) as stars
FROM Movie M
JOIN Rating R USING (mID)
GROUP BY M.mID, M.year
) AS R
Найти разницу между средней оценкой фильмов выпущенных до 1980 года, и средней оценкой фильмов выпущенных после 1980 года.
SELECT
AVG(CASE WHEN year > 1980 THEN stars ELSE NULL END) -
AVG(CASE WHEN year < 1980 THEN stars ELSE NULL END) AS result
FROM (
SELECT M.mID, M.year, AVG(R.stars) as stars
FROM movie M
JOIN rating R USING (mID)
GROUP BY M.mID
) AS R
Найти разницу между средней оценкой фильмов выпущенных до 1980 года, и средней оценкой фильмов выпущенных после 1980 года.
SELECT AVG(R.s_after) - AVG(R.s_before) AS result
FROM (
SELECT
AVG(CASE WHEN year > 1980 THEN stars ELSE NULL END) AS s_after,
AVG(CASE WHEN year < 1980 THEN stars ELSE NULL END) AS s_before
FROM movie M
JOIN rating R USING (mID)
GROUP BY M.mID
) AS R
CREATE
[ OR REPLACE ]
[ TEMP | TEMPORARY ]
[ RECURSIVE ]
VIEW name [ ( column_name [, ...] ) ]
[ WITH ( view_option_name [= view_option_value] [, ... ] ) ]
AS query
[ WITH [ CASCADED | LOCAL ] CHECK OPTION ];
CREATE VIEW v AS
SELECT a.id, b.id FROM a, b;
-- ERROR: column "id" specified more than once
-- SQL-состояние: 42701
CREATE VIEW v (a_id, b_id) AS
SELECT a.id, b.id FROM a,b;
CREATE VIEW v AS
SELECT a.id a_id, b.id b_id FROM a,b;
CREATE TABLE films (
imdb varchar(16) PRIMARY KEY,
title varchar(40) NOT NULL,
kind varchar(10)
);
CREATE OR REPLACE VIEW comedies AS
SELECT *
FROM films
WHERE kind = 'Comedy'
WITH CASCADED CHECK OPTION;
INSERT INTO comedies (imdb, title, kind)
VALUES ('tt0114709', 'Toy Story 1', 'Animation');
-- ERROR: new row violates check option for view "comedies"
-- DETAIL: Failing row contains (tt0114709, Toy Story 1, Animation).
INSERT INTO comedies (imdb, title, kind)
VALUES ('tt1156398', 'Zombieland', 'Comedy');