Использование PostgreSQL v9.1. У меня есть следующие таблицы:
CREATE TABLE foo
(
id BIGSERIAL NOT NULL UNIQUE PRIMARY KEY,
type VARCHAR(60) NOT NULL UNIQUE
);
CREATE TABLE bar
(
id BIGSERIAL NOT NULL UNIQUE PRIMARY KEY,
description VARCHAR(40) NOT NULL UNIQUE,
foo_id BIGINT NOT NULL REFERENCES foo ON DELETE RESTRICT
);
Скажем, первая таблица foo
заполнена так:
INSERT INTO foo (type) VALUES
( 'red' ),
( 'green' ),
( 'blue' );
Есть ли способ bar
легко вставить строки , ссылаясь на foo
таблицу? Или я должен сделать это в два этапа, сначала найдя foo
нужный мне тип, а затем вставив новую строку в bar
?
Вот пример псевдокода, показывающий то, что я надеялся сделать:
INSERT INTO bar (description, foo_id) VALUES
( 'testing', SELECT id from foo WHERE type='blue' ),
( 'another row', SELECT id from foo WHERE type='red' );
postgresql
foreign-key
postgresql-9.1
insert
Stéphane
источник
источник
Обычная вставка
Использование
LEFT [OUTER] JOIN
вместо[INNER] JOIN
означает, что строки изval
не удаляются, когда не найдено совпаденийfoo
. Вместо этогоNULL
вводится дляfoo_id
.VALUES
Выражение подзапроса делает то же самое , как @ ypercube в КТР. Стандартные табличные выражения предлагают дополнительные функции и их легче читать в больших запросах, но они также представляют собой барьеры для оптимизации. Таким образом, подзапросы обычно немного быстрее, когда ничего из вышеперечисленного не требуется.id
поскольку имя столбца является широко распространенным анти-паттерном. Должно бытьfoo_id
и /bar_id
или что-нибудь описательное. При объединении нескольких таблиц вы получаете несколько столбцов с именамиid
...Считайте простым
text
илиvarchar
вместоvarchar(n)
. Если вам действительно нужно наложить ограничение по длине, добавьтеCHECK
ограничение:Возможно, вам придется добавить явные приведения типов. Поскольку
VALUES
выражение напрямую не связано с таблицей (как вINSERT ... VALUES ...
), типы не могут быть получены, и типы данных по умолчанию используются без явного объявления типа, которое может работать не во всех случаях. Достаточно сделать это в первом ряду, остальные встанут в очередь.Вставить пропущенные строки FK одновременно
Если вы хотите создать несуществующие записи
foo
на лету, в одном операторе SQL CTE являются полезными:Обратите внимание на две новые фиктивные строки для вставки. Оба пурпурные , которых пока не существует
foo
. Две строки, чтобы проиллюстрировать необходимостьDISTINCT
в первомINSERT
утверждении.Пошаговое объяснение
1-й CTE
sel
предоставляет несколько строк входных данных. Подзапросval
сVALUES
выражением может быть заменен таблицей или подзапросом в качестве источника. СразуLEFT JOIN
чтобыfoo
добавитьfoo_id
для уже существующихtype
строк. Все остальные ряды получаютfoo_id IS NULL
этот путь.2-й CTE
ins
вставляет различные новые типы (foo_id IS NULL
) вfoo
и возвращает вновь сгенерированныйfoo_id
- вместе сtype
присоединяемым обратно для вставки строк.Последний внешний
INSERT
элемент теперь может вставлять foo.id для каждой строки: либо ранее существовавший тип, либо он был вставлен на шаге 2.Строго говоря, обе вставки происходят «параллельно», но, поскольку это один оператор,
FOREIGN KEY
ограничения по умолчанию не будут жаловаться. Ссылочная целостность применяется в конце оператора по умолчанию.SQL Fiddle для Postgres 9.3. (Работает так же в 9.1.)
Если вы выполняете несколько запросов одновременно, возникает крошечное условие гонки . Читайте больше в связанных вопросах здесь и здесь и здесь . На самом деле происходит только при большой параллельной нагрузке, если вообще когда-либо. По сравнению с решениями для кеширования, такими как рекламируемые в другом ответе, вероятность очень мала .
Функция для многократного использования
Для повторного использования я бы создал функцию SQL, которая принимает массив записей в качестве параметра и использует
unnest(param)
вместоVALUES
выражения.Или, если синтаксис для массивов записей слишком запутан для вас, используйте разделенную запятыми строку в качестве параметра
_param
. Например, формы:Затем используйте это, чтобы заменить
VALUES
выражение в приведенном выше утверждении:Функция с UPSERT в Postgres 9,5
Создайте пользовательский тип строки для передачи параметров. Мы могли бы обойтись без этого, но это проще:
Функция:
Вызов:
Быстрый и надежный для сред с параллельными транзакциями.
В дополнение к запросам выше, это ...
... применяется
SELECT
илиINSERT
наfoo
: Любой ,type
который не существует в таблице FK, но, вставляется. Предполагая, что большинство типов уже существуют. Чтобы быть абсолютно уверенными и исключить условия гонки, существующие строки, которые нам нужны, заблокированы (чтобы параллельные транзакции не могли вмешиваться). Если это слишком параноидально для вашего случая, вы можете заменить:с участием
... применяется
INSERT
илиUPDATE
(истинно "UPSERT") наbar
: еслиdescription
уже существует,type
оно обновляется:Но только если
type
действительно изменится:... передает значения как известные типы строк с
VARIADIC
параметром. Обратите внимание, что по умолчанию максимум 100 параметров! Для сравнения:Есть много других способов передать несколько строк ...
Связанный:
источник
INSERT missing FK rows at the same time
примере, поместит ли это в транзакцию снижение риска состязаний в SQL Server?SELECT
внутриWITH
предложения). Источник: документация MS.INSERT ... RETURNING \gset
in,psql
затем использовать возвращаемые значения как psql:'variables'
, но это работает только для вставок в одну строку.Уважать. Вам в основном нужны идентификаторы foo, чтобы вставить их в панель.
Не специфично для postgres, кстати. (и вы не пометили это так) - так обычно работает SQL. Здесь нет ярлыков.
Тем не менее, в приложениях может быть кэш объектов foo в памяти. Мои таблицы часто имеют до 3 уникальных полей:
Пример:
Очевидно, что когда вы хотите связать что-то с учетной записью - сначала вы должны, технически, получить Id - но, учитывая, что и Идентификатор, и Код никогда не меняются, когда они есть, положительный кэш в памяти может остановить большинство поисков от попадания в базу данных.
источник