ССЫЛКИ на две разные колонки в двух разных таблицах - PullRequest
0 голосов
/ 16 октября 2019

В базе данных Postgresql у нас есть следующая таблица:

=# SELECT * FROM toy_cars;
  serial_no |     name
------------+---------------
 199ER276FN | Snow Doctor
 8BE0F79A3R | Flatbed Truck
 D76185CE8G | Sand Speeder

=# SELECT * FROM toy_trains;
    serial_no    |    name
-----------------+-------------
 BMXH5R4T8K7KELD | Howler T140
 B1Q1JJDQW9LQN0G | Quakester
 8HO9240TO6RNNQ9 | Medusa 90

=# SELECT * FROM items_for_sale;
    serial_no    |   in_stock
-----------------+---------------
      199ER276FN | t
 BMXH5R4T8K7KELD | t
 B1Q1JJDQW9LQN0G | f
      8BE0F79A3R | f
 8HO9240TO6RNNQ9 | t
      D76185CE8G | f

Примечание:

  • Каждый столбец serial_no является первичным ключом этой таблицы иin_stock - логическое значение.

  • serial_no в таблице toy_cars имеет ограничение CHECK с регулярным выражением, разрешающее ввод только 10 символов.

  • serial_no в таблице toy_trains имеет ограничение регулярного выражения CHECK, допускающее использование только 15 символов.

  • serial_no в таблице items_for_sale - это серийный номерили игрушечные автомобили или поезда, и имеет ограничение CHECK регулярного выражения, позволяющее использовать только 10 или 15 символов.

  • Все столбцы serial_no имеют ограничение UNIQUE.

Мы хотим добавить проверку REFERENCES к serial_no в таблице items_for_sale, чтобы убедиться, что введенный серийный номер присутствует либо в таблице toy_cars, либо в таблице toy_trains.

Итак, если бы я попытался INSERT INTO items_for_sale VALUES('KYVGK0DBYXPMWW8','f');, это бы не получилось, потому что этот серийный номер отсутствует ни в toy_cars, ни в toy_trains.

Как это можно сделать? Мы предпочитаем использовать одну таблицу (как сейчас она структурирована).

1 Ответ

1 голос
/ 18 октября 2019

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

PostgreSQL не поддерживает ограничения CHECK, которые ссылаются на данные таблицы, отличные от новых или обновленныхстрока проверяется. (...) Если возможно, используйте ограничения UNIQUE, EXCLUDE или FOREIGN KEY, чтобы выразить ограничения между строками и таблицами.

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

См. этот очень связанный вопрос для различных решений.

Некоторые базы данных (MS SQL Server) позволяют использовать функцию в CHECK выражениях . Это было бы оптимально, но Postgres не допускает этот синтаксис.

Для PostgreSQL вам нужно создать TRIGGER, который будет выполняться, когда что-то будет вставлено в items_for_sale. Что, с другой стороны, разрешает разрешать функции или проверки между таблицами.

Это будет выглядеть примерно так:

CREATE TRIGGER check_serial_present
    BEFORE INSERT ON items_for_sale
    FOR EACH ROW
    EXECUTE FUNCTION assert_presence_of_serial(); -- implement this function

В другом связанном вопросе также упоминаетсядовольно элегантный способ достижения этого без триггеров:

Чистое решение без триггеров: добавьте лишние столбцы и включите их в ограничения FOREIGN KEY

Мне это очень нравится, так какконцептуально очень просто и легко понять. Я думаю, это будет просто включать следующие шаги:

  1. ALTER TABLE, чтобы добавить две строки: toy_car_serial и toy_trains_serial, оба NULLABLE, но с FOREIGN KEY ограничениями наупомянутые таблицы.
  2. Убедитесь, что любой INSERT вставит серийный номер в serial_no и toy_car_serial ИЛИ serial_no и toy_car_serial.
  3. Добавить CHECK( toy_car_serial = serial_no OR toy_trains_serial = serial_no).

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...