Сложная система, спроектированная наспех, никогда не работает, и исправить ее, чтобы заставить работать, невозможно.
Трудно что-либо предвидеть, а уж особенно будущее.
| Блюдо | Вид | Дата | Продукт | Калорийность | Вес (г) | Поставщик | Город | Страна | Цена ($) |
|---|---|---|---|---|---|---|---|---|---|
| Лобио | Закуска | 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 |
Компания N занимается разработкой полного цикла и поддержкой веб-приложений.
Стоимость часа специалиста зависит от его должности.
Стоимость работ по проекту зависит от того сколько специалистов и по сколько часов работали над данным проектом
Необходимо спроектировать базу, которая позволит удобно вести учет работ по проектам и выставлять счета клиентам
| ProjName | EmpName | JobType | ChargePerHour | HoursBilled | TotalCharge |
|---|---|---|---|---|---|
| Meta Cortex | Thomas A. Anderson | Senior Software Engineer | $10 | 25.4 | $254.00 |
| Michelle McGahey | UI/UX Designer | $5 | 15.0 | $75.00 | |
| John Anderson | Senior DBA | $8 | 10.5 | $84.00 | |
| Subtotal | $413.00 | ||||
| ENCOM | Kevin Flynn | Senior Software Engineer | $10 | 32.5 | $325.00 |
| Sam Flynn | Junior Software Engineer | $5 | 20.0 | $100.00 | |
| John Anderson | Software Engineer | $8 | 30.0 | $240.00 | |
| Alan Bradley | Junior DBA | $6 | 8.0 | $48.00 | |
| Subtotal | $713.00 | ||||
| Total | $1126.00 | ||||
Переменная отношения находится в первой нормальной форме (1НФ) тогда и только тогда, когда в любом допустимом значении отношения каждый его кортеж содержит только одно значение для каждого из атрибутов.
| ProjName | EmpName | JobType | ChargePerHour | HoursBilled | TotalCharge |
|---|---|---|---|---|---|
| Meta Cortex | Thomas A. Anderson | Senior Software Engineer | $10 | 25.4 | $254.00 |
| Meta Cortex | Michelle McGahey | UI/UX Designer | $5 | 15.0 | $75.00 |
| Meta Cortex | John Anderson | Senior DBA | $8 | 10.5 | $84.00 |
| ENCOM | Kevin Flynn | Senior Software Engineer | $10 | 32.5 | $325.00 |
| ENCOM | Sam Flynn | Junior Software Engineer | $5 | 20.0 | $100.00 |
| ENCOM | John Anderson | Software Engineer | $8 | 30.0 | $240.00 |
| ENCOM | Alan Bradley | Junior DBA | $6 | 8.0 | $48.00 |
Ключ — атрибут или набор атрибутов, который однозначно идентифицирует кортеж в таблице в базе данных.
Первичный ключ — используется для идентификации объекта.
Потенциальный ключ — неиспользуемый, как первичный.
Составной ключ — использует несколько атрибутов.
Естественный ключ — набор атрибутов описываемой записью сущности, уникально её идентифицирующий.
Суррогатный ключ — искусственно сгенерированное поле, никак не связанное с информационным содержанием записи.
Первичный ключ выбирается исходя из соображений удобства и сохранения уникальности. Если такого ключа нет, то имеет смысл добавить специальное поле.
| ProjNum | ProjName | EmpNum | EmpName | JobType | ChargePerHour | HoursBilled | TotalCharge |
|---|---|---|---|---|---|---|---|
| 15 | Meta Cortex | 105 | Thomas A. Anderson | Senior Software Engineer | $10 | 25.4 | $254.00 |
| 15 | Meta Cortex | 102 | Michelle McGahey | UI/UX Designer | $5 | 15.0 | $75.00 |
| 15 | Meta Cortex | 103 | John Anderson | Senior DBA | $8 | 10.5 | $84.00 |
| 12 | ENCOM | 108 | Kevin Flynn | Senior Software Engineer | $10 | 32.5 | $325.00 |
| 12 | ENCOM | 113 | Sam Flynn | Junior Software Engineer | $5 | 20.0 | $100.00 |
| 12 | ENCOM | 109 | John Anderson | Software Engineer | $8 | 30.0 | $240.00 |
| 12 | ENCOM | 101 | Alan Bradley | Junior DBA | $6 | 8.0 | $48.00 |
Составной суррогатный первичный ключ: < ProjNum, EmpNum >
| ProjNum | ProjName | EmpNum | EmpName | JobType | ChargePerHour | HoursBilled | TotalCharge |
|---|---|---|---|---|---|---|---|
| 15 | Meta Cortex | 105 | Thomas A. Anderson | Senior Software Engineer | $10 | 25.4 | $254.00 |
| 15 | Meta Cortex | 102 | Michelle McGahey | UI/UX Designer | $5 | 15.0 | $75.00 |
| 15 | Meta Cortex | 103 | John Anderson | Senior DBA | $8 | 10.5 | $84.00 |
| 12 | ENCOM | 108 | Kevin Flynn | Senior Software Engineer | $10 | 32.5 | $325.00 |
| 12 | ENCOM | 113 | Sam Flynn | Junior Software Engineer | $5 | 20.0 | $100.00 |
| 12 | ENCOM | 109 | John Anderson | Software Engineer | $8 | 30.0 | $240.00 |
| 12 | ENCOM | 101 | Alan Bradley | Junior DBA | $6 | 8.0 | $48.00 |
A -> B.
Выделяем все функциональные зависимости:
| ProjNum | ProjName | EmpNum | EmpName | JobType | ChargePerHour | HoursBilled | TotalCharge |
|---|---|---|---|---|---|---|---|
| 15 | Meta Cortex | 105 | Thomas A. Anderson | Senior Software Engineer | $10 | 25.4 | $254.00 |
| 12 | ENCOM | 113 | Sam Flynn | Junior Software Engineer | $5 | 20.0 | $100.00 |
ProjNum, EmpNum -> ProjName, EmpName, JobType, ChargePerHour, HoursBilled,
TotalChargeProjNum -> ProjNameEmpNum -> EmpName, JobType, ChargePerHourJobType -> ChargePerHourПеременная отношения находится во второй нормальной форме тогда и только тогда, когда она находится в первой нормальной форме, и каждый неключевой атрибут неприводимо (функционально полно) зависит от ее потенциального ключа.
Простыми словами: не должно быть зависимости от части ключа.
ProjNum, EmpNum -> ProjName, EmpName, JobType, ChargePerHour, HoursBilled, TotalChargeProjNum -> ProjNameEmpNum -> EmpName, JobType, ChargePerHourПростыми словами: не должно быть зависимости от части ключа.
Чтобы этого добиться разбиваем отношение с частичными зависимостями на несколько отдельных:
Project (ProjNum, ProjName)Employee (EmpNum, EmpName, JobType, ChargePerHour)Assignment (ProjNum, EmpNum, HoursBilled)| ProjNum | ProjName |
|---|---|
| 15 | Meta Cortex |
| 12 | ENCOM |
| ProjNum | EmpNum | HoursBilled |
|---|---|---|
| 15 | 105 | 25.4 |
| 12 | 113 | 20.0 |
| EmpNum | EmpName | JobType | ChargePerHour |
|---|---|---|---|
| 105 | Thomas A. Anderson | Senior Software Engineer | $10 |
| 113 | Sam Flynn | Junior Software Engineer | $5 |
Переменная отношения находится в третьей нормальной форме, когда она находится во второй нормальной форме, и отсутствуют транзитивные функциональные зависимости неключевых атрибутов от ключевых.
Простыми словами: любой столбец таблицы должен зависеть только от ключевого столбца.
EmpNum -> EmpName, JobType, ChargePerHourJobType -> ChargePerHourДетерминант функциональной зависимости - набор атрибутов, который находится слева от стрелки в записи функциональной зависимости.
Тривиальная зависимость - зависимость, в которой правая часть является подмножеством детерминанта.
Функциональная зависимость называется неприводимой слева, если из детерминанта невозможно убрать ни один атрибут, сохранив функциональную зависимость.
Функциональная зависимость A -> B является транзитивной, если существует такое
множество атрибутов C, что для него справедливы нетривиальные
функциональные зависимости A -> C и C -> B, при этом отсутствует
функциональная зависимость C -> A.
В реляционной алгебре транзитивность отличается от транзитивности в других разделах.
Обязательность отсутствия зависимости C -> A не присуща другим разделам.
Другими простыми словами: не должно быть функциональных зависимостей среди неключевых атрибутов.
Чтобы этого добиться выделяем транзитивные зависимости в отдельные отношения
Project (ProjNum, ProjName)Employee (EmpNum, EmpName, JobType)Assignment (ProjNum, EmpNum, HoursBilled)Job (JobType, ChargePerHour)| ProjNum | ProjName |
|---|---|
| 15 | Meta Cortex |
| 12 | ENCOM |
| ProjNum | EmpNum | HoursBilled |
|---|---|---|
| 15 | 105 | 25.4 |
| 12 | 113 | 20.0 |
| EmpNum | EmpName | JobType |
|---|---|---|
| 105 | Thomas A. Anderson | Senior Software Engineer |
| 113 | Sam Flynn | Junior Software Engineer |
| JobType | ChargePerHour |
|---|---|
| Senior Software Engineer | $10 |
| Junior Software Engineer | $5 |
Для отношений, имеющих один потенциальный ключ (первичный), НФБК является 3НФ. Отношение находится в НФБК тогда и только тогда, когда она находится в третьей нормальной форме, и при этом не только любой неключевой атрибут полностью функционально зависит от любого ключа, но и любой ключевой атрибут должен полностью функционально зависеть от любого ключа.
Таким образом, требование о фактической зависимости неключевых атрибутов от всего ключа целиком и Jни от чего другого, кроме как от ключа, распространяется и на ключевые атрибуты.
| Номер поставщика PNUM | Наименование поставщика PNAME | Номер детали DNUM | Поставляемое количество VOLUME |
|---|---|---|---|
| 1 | Фирма 1 | 1 | 100 |
| 1 | Фирма 1 | 2 | 200 |
| 1 | Фирма 1 | 3 | 300 |
| 2 | Фирма 2 | 1 | 150 |
| 2 | Фирма 2 | 2 | 250 |
| 3 | Фирма 3 | 3 | 1000 |
Отношение имеет два потенциальных ключа - {PNUM, DNUM} и {PNAME, DNUM}.
PNUM -> PNAME - наименование поставщика зависит от номера поставщика. PNAME -> PNUM - номер поставщика зависит от наименования поставщика. {PNUM, DNUM} -> VOLUME - поставляемое количество зависит от первого ключа. {PNUM, DNUM} -> PNAME - наименование поставщика зависит от первого ключа. {PNAME, DNUM} -> VOLUME - поставляемое количество зависит от второго ключа. {PNAME, DNUM} -> PNUM - номер поставщика зависит от второго ключ.| Номер поставщика PNUM | Наименование поставщика PNAME |
|---|---|
| 1 | Фирма 1 |
| 2 | Фирма 2 |
| 3 | Фирма 3 |
| Номер поставщика PNUM | Номер детали DNUM | Поставляемое количество VOLUME |
|---|---|---|
| 1 | 1 | 100 |
| 1 | 2 | 200 |
| 1 | 3 | 300 |
| 2 | 1 | 150 |
| 2 | 2 | 250 |
| 3 | 3 | 1000 |
| Нормальная форма | Определение |
|---|---|
| 1 НФ | Все значения атрибутов атомарны |
| 2 НФ | 1 НФ и нет частичных функциональных зависимостей |
| 3 НФ | 2 НФ и нет транзитивных зависимостей |
| НФ Бойса-Кодда | 3 НФ и все детерминанты являются потенциальными ключами |
| 4 НФ | НФБД и не содержит нетривиальных многозначных зависимостей |
| 5 НФ | 4 НФ и каждая нетривиальная зависимость соединения в ней определяется потенциальным ключом (ключами) этого отношения |
| Доменно-ключевая НФ | 5 НФ и каждое наложенное на неё ограничение является логическим следствием ограничений доменов и ограничений ключей, наложенных на данную переменную отношения |
| 6 НФ | 5 НФ и удовлетворяет всем нетривиальным зависимостям соединения, то есть недекомпозируема далее без потерь |
Каждый атрибут атомарен (1НФ) и зависит...
... от ключа (2НФ)
... только от ключа (3НФ)
... и ни от чего кроме ключа (НФБК).
SELECT 1 = NULL, 1 <> NULL, 1 < NULL, 1 > NULL;
| 1 = NULL | 1 <> NULL | 1 < NULL | 1 > NULL |
|---|---|---|---|
|
|
|
|
|
SELECT 1 IS NULL, 1 IS NOT NULL, NULL IS NULL, NULL IS NOT NULL;
| 1 IS NULL | 1 IS NOT NULL | NULL IS NULL | NULL IS NOT NULL |
|---|---|---|---|
| 0 | 1 | 1 | 0 |
| Тип столбца, поля | Объём занимаемой памяти | Диапазон допустимых значений |
|---|---|---|
| SMALLINT, INT2 | 2 байта | От -32768 до 32767 |
| INTEGER, INT, INT4 | 4 байта | От -2147683648 до 2147683648 |
| BIGINT, INT8 | 8 байт | От -263 до 263 -1 |
| DECIMAL [(M[,D])], NUMERIC [(M[,D])] |
~2 байта на 4 цифры | Повышенная точность более 10000 цифр |
| REAL | 4 байта | -3.402823466E+38 до -1.175494351E-38 1.175494351E-38 до 3.402823466E+38 |
| DOUBLE PRECISION | 8 байт | -1.7976931348623157E+308 до -2.2250738585072014E-308 2.2250738585072014E-308 до 1.7976931348623157E+308 |
| MONEY | 8 байт | От -92233720368547758.08 до +92233720368547758.07 |
SMALLSERIAL, SERIAL, BIGSERIAL - то же, но с автоинкрементом.
INT1 и беззнаковые есть в расширении https://github.com/petere/pguint
| Тип столбца, поля | Объём занимаемой памяти | Диапазон допустимых значений |
|---|---|---|
| BIT(N) | (N + 7) / 8 байт | Битовая строка с фиксированной длиной |
| BIT VARYING(N), VARBIT(N) | Битовая строка с переменной длиной | |
| BOOLEAN | 1 байт | 0 или 1 |
| Тип столбца, поля | Описание |
|---|---|
| CHARACTER(N), CHAR(N) | Строка фиксированной длины |
| CHARACTER VARYING(N), VARCHAR(M) | Строка переменной длины |
| TEXT | Строковые данные без ограничения длины |
| BYTEA | Двоичный данные без ограничения длины |
| Тип столбца, поля | Размер |
|---|---|
| TIMESTAMP [ (P) ] [ WITHOUT TIME ZONE ] | 8 байт |
| TIMESTAMP [ (P) ] WITH TIME ZONE | 8 байт |
| DATE | 4 байта |
| TIME [ (P) ] [ WITHOUT TIME ZONE ] | 8 байт |
| TIME [ (P) ] WITH TIME ZONE | 12 байт |
| INTERVAL [ FIELDS ] [ (P) ] | 16 байт |
Время хранится с точностью до микросекунд.
P - кол-во цифр для хранения дробной части секунд.
WITH TIMEZONE - внутри базы хранится UTC.
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 ]
[ 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 ]
[ 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 ]
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 [ 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
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 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();
[ 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 ] [, ...] ]
( { index_column_name | ( index_expression ) } ... [, ...] )
[ WHERE index_predicate ]
ON CONSTRAINT constraint_name
DO NOTHING
DO UPDATE SET { column_name = { expression | DEFAULT } |
( column_name [, ...] ) = ( { expression | DEFAULT } [, ...] ) |
( column_name [, ...] ) = ( sub-SELECT )
} [, ...]
[ WHERE condition ]
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;
[ 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 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 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;
[ 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 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';