Как может реляционная база данных с ограничениями внешнего ключа принимать данные, которые могут быть в неправильном порядке? - PullRequest
2 голосов
/ 14 января 2020

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

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

Мы работаем над решением, которое создает строки-заполнители для указания внешнего ключа. Когда поступают реальные данные, заполнитель заменяется реальными значениями. Опять же, это добавляет сложности, но это лучшее решение, которое мы нашли на данный момент.

Как люди обычно решают эту проблему?

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

Допустим, у нас есть эти таблицы:

CREATE TABLE order (
  id INTEGER NOT NULL,
  order_number,
  PRIMARY KEY (id),
  UNIQUE (order_number)
);

CREATE TABLE line_item (
  id INTEGER NOT NULL,
  order_number INTEGER REFERENCES order(order_number),
  PRIMARY KEY (id)
);

Если я сначала вставлю ордер, не проблема! Но скажем, я пытаюсь:

INSERT INTO line_item (order_number) values (123) до того, как был вставлен заказ 123. Это, конечно, не выдержит ограничения fk. Но это может быть порядок, в котором я получаю данные, так как он читает из потока, который собирает эти данные из нескольких источников.

Кроме того, чтобы ответить на вопрос @ philpxy, я на самом деле мало что нашел по этому поводу. Одна вещь, которая была упомянута, была отложенные ограничения . Это механизм, который ожидает выполнения ограничений fk в конце транзакции. Однако я не думаю, что это возможно сделать в моем случае, поскольку эти операторы вставки будут выполняться в случайное время при получении данных.

Ответы [ 2 ]

1 голос
/ 14 января 2020

Вы можете выполнить sh это с помощью BEFORE INSERT триггера на line_item. В этом триггере вы запрашиваете order, если соответствующий элемент существует, а если нет, вы вставляете фиктивную строку.

Это позволит INSERT добиться успеха за счет некоторой производительности.

Чтобы вставить строки в order, используйте

INSERT INTO order ...
ON CONFLICT ON (order_number) DO UPDATE SET
   id = EXCLUDED.id;

Обновление первичного ключа проблематично c и может привести к конфликтам. Один из способов обойти это - использовать отрицательные id s для искусственно сгенерированных ордеров (при условии, что действительные id s положительные). Если у вас есть какие-либо ссылки на этот первичный ключ, вам нужно определить ограничение с помощью ON UPDATE CASCADE.

1 голос
/ 14 января 2020

У вас есть проблема бизнес-процесса, потому что позиции отдельных заказов поступают до того, как сами заказы поступят. Одним из обходных путей, возможно, не идеальным, будет создание триггера перед вставкой, который проверяет каждую входящую вставку в таблица line_item, существует ли этот порядок в таблице order. Если нет, то он сначала вставит запись заказа, прежде чем пытаться вставить на line_item.

CREATE OR REPLACE FUNCTION "public"."fn_insert_order" () RETURNS trigger AS $$
BEGIN
    INSERT INTO "order" (order_number)
    SELECT NEW.order_number
    WHERE NOT EXISTS (SELECT 1 FROM "order" WHERE order_number = NEW.order_number);
    RETURN NEW;
END
$$
LANGUAGE 'plpgsql'

# trigger 
CREATE TRIGGER "trigger_insert_order"
BEFORE INSERT ON line_item FOR EACH ROW
EXECUTE PROCEDURE fn_insert_order()

Примечание. Я предполагаю, что столбец id таблицы order фактически является авто increment, в этом случае Postgres будет автоматически присваивать ему значение при вставке, как указано выше. Скорее всего, это то, что вам нужно, поскольку наличие двух столбцов идентификаторов, которые необходимо назначить вручную, не имеет большого смысла.

...