Як співвідносити два ряди в одній таблиці


11

У мене є таблиця, де рядки можуть бути пов'язані один з одним, і логічно, що відносини йдуть обома напрямками (в основному, без напрямків) між двома рядками. (І якщо вам цікаво, так, це справді повинна бути одна таблиця. Це дві речі точно такої ж логічної сутності / типу.) Я можу придумати кілька способів представити це:

  1. Зберігайте відносини та його зворотні
  2. Збережіть взаємозв'язок в один бік, обмежте базу даних не зберігаючи її іншим способом і майте два індекси з протилежними порядками для ФК (один індекс - індекс PK)
  3. Зберігайте взаємозв'язок в один бік з двома індексами і дозволяйте ввести все-таки другий (звучить на кшталт приємно, але ей, повнота)
  4. Створіть якусь таблицю групування і додайте ФК до неї в оригінальній таблиці. (Виникає багато питань. Таблиця групування матиме лише число; чому взагалі мати таблицю? Зробити FK NULLable або мати групи з одним рядком?)

Які основні плюси і мінуси цих способів, і, звичайно, є якийсь спосіб, про який я не думав?

Ось SQLFiddle з якою грати: http://sqlfiddle.com/#!12/7ee1a/1/0 . (Буває PostgreSQL, оскільки саме це я використовую, але я не думаю, що це питання є дуже специфічним для PostgreSQL.) В даний час він зберігає як відносини, так і їх зворотні лише як приклад.


Чи може дане значення пов'язане з більш ніж одним? Чи дане значення завжди пов'язане з іншим? Вони діляться однаковими іншими загальними даними?
Philᵀᴹ

Так, вони можуть бути пов’язані з більш ніж одним іншим рядом. Ні, вони не обов'язково завжди пов'язані з іншим рядом. Вони не обов'язково мають загальні дані. Дякую.
jpmc26

На жаль Забув @Phil. Також відредаговано, щоб додати потенційну структуру, яка мені щойно виникла.
jpmc26

Відповіді:


9

Те, що ви сконструювали, добре. Що потрібно додати, це обмеження, щоб зробити відносини безрезультатними. Отже, ви не можете мати (1,5)рядок без (5,1)додавання рядка.

Це може бути досягнуто * з обмеженням власного посилання на містку таблицю.

*: це може бути виконано в Postgres, Oracle, DB2 та всіх СУБД, які реалізують обмеження зовнішніх ключів, як описано у стандарті SQL (відкладено, наприклад, перевірено в кінці транзакції.) Відкладена перевірка все-таки дійсно не потрібна, як у SQL- Сервер, який перевіряє їх у кінці оператора, і ця конструкція все ще працює. Ви не можете цього зробити в MySQL, оскільки "InnoDB перевіряє UNIQUE та FOREIGN KEY обмеження рядком за рядком" .

Отже, у Postgres наступне відповідатиме вашим вимогам:

CREATE TABLE x
(
  x_id SERIAL NOT NULL PRIMARY KEY,
  data VARCHAR(10) NOT NULL
);

CREATE TABLE bridge_x
(
  x_id1 INTEGER NOT NULL REFERENCES x (x_id),
  x_id2 INTEGER NOT NULL REFERENCES x (x_id),
  PRIMARY KEY(x_id1, x_id2),
  CONSTRAINT x_x_directionless
    FOREIGN KEY (x_id2, x_id1)
    REFERENCES bridge_x (x_id1, x_id2)
);

Тестується за адресою: SQL-Fiddle

Якщо ви спробуєте додати рядок (1,5):

INSERT INTO bridge_x VALUES
(1,5) ;

Не вдається:

ПОМИЛКА: вставка або оновлення в таблиці "bridge_x" порушує обмеження зовнішнього ключа "x_x_directionless"
Детальніше: Ключ (x_id2, x_id1) = (5, 1) відсутній у таблиці "bridge_x" .:
ВСТАВИТИСЯ В МІСТО_БІРУВАННЯ ВІДКЛЮЧЕННЯ (1,5)

Крім того, ви можете додати CHECKобмеження, якщо хочете заборонити (y,y)рядки:

ALTER TABLE bridge_x
  ADD CONSTRAINT x_x_self_referencing_items_not_allowed
    CHECK (x_id1 <> x_id2) ;

Як ви вже згадували, існують інші способи реалізації цього способу, наприклад, зберігання лише одного напрямку відносин (в одному ряду, а не двох) шляхом примушування нижнього ідентифікатора до x_id1та вищого ідентифікатора у x_id2стовпці. Це виглядає простіше у виконанні, але зазвичай призводить до складніших запитів пізніше:

CREATE TABLE bridge_x
(
  x_id1 INTEGER NOT NULL REFERENCES x (x_id),
  x_id2 INTEGER NOT NULL REFERENCES x (x_id),
  PRIMARY KEY(x_id1, x_id2),
  CONSTRAINT x_x_directionless
    CHECK (x_id1 <= x_id2)                       -- or "<" to forbid `(y,y)` rows
);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.