Обновление двух таблиц в соотношении многие-к-одному через представление в PostgreSQL - PullRequest
1 голос
/ 02 октября 2008

У меня есть две таблицы: foos и bars, и между ними существует отношение многие-к-одному: каждая foo может иметь много bars. У меня также есть представление foobars, которое объединяет эти две таблицы (его запрос похож на select foo.*, bar.id from foos, bars where bar.foo_id=foo.id).

РЕДАКТИРОВАТЬ: Вы бы не ошиблись, если бы сказали, что есть отношения многие ко многим между foo с и bar с. A bar, однако, является просто тегом (фактически, это размер) и состоит только из его имени. Таблица bars играет ту же роль, что и таблица ссылок.

У меня есть правило вставки в foobars, когда часть «foo» вставляется в foos как новая строка, а часть «bar», которая состоит из пары идентификаторов бара, разделенных запятыми, split, и для каждой такой части создается связь между ней и соответствующим foo (для этого я использую процедуру).

Это прекрасно работает для вставок. У меня проблема, однако, когда дело доходит до обновления всего этого. foo часть правила проста. Тем не менее, я не знаю, как справиться с множественной bar частью. Когда я пытаюсь сделать что-то вроде DELETE FROM bars WHERE foo_id=new.foo_id в правиле, я заканчиваю удаление всего из таблицы bars.

Что я делаю не так? Есть ли способ достичь того, что мне нужно? Наконец, мой подход ко всему разумному?

(Я делаю это слишком сложно с представлением, потому что данные, которые я получаю, имеют форму «foo и все их bar s», но пользователь должен видеть только foobars.)

Ответы [ 4 ]

2 голосов
/ 02 октября 2008

Rysiek, если я правильно понял, у вас есть текстовый столбец в таблице foos, который анализируется для извлечения внешних ключей, указывающих на таблицу bars. Такой подход к построению отношений может быть оправдан в некоторых случаях, однако почти каждое руководство / учебник по программированию базы данных будет препятствовать этому. Почему бы не использовать стандартный внешний ключ в bars, который указывает на foo в foos? Если не требуется, чтобы бары были назначены более чем одному foo. Если это так, это определяет ваше отношение «многие ко многим», а не «один ко многим». В любой ситуации использование стандартного решения на основе внешнего ключа кажется более естественным для базы данных.

Пример схемы БД для отношения один-ко-многим:

CREATE TABLE foos (
    id SERIAL PRIMARY KEY,
    ....
);
CREATE TABLE bars (
    id SERIAL PRIMARY KEY,
    foo_id INT REFERENCES bars (id) ON DELETE CASCADE,
    ...
);

И то же самое для отношения многие ко многим:

CREATE TABLE foos (
    id SERIAL PRIMARY KEY,
    ....
);
CREATE TABLE bars (
    id SERIAL PRIMARY KEY,
    ...
);
CREATE TABLE foostobars (
    foo_id INT REFERENCES foos (id) ON DELETE CASCADE,
    bar_id INT REFERENCES bars (id) ON DELETE CASCADE
);

Я бы также рекомендовал использовать INNER JOIN вместо умножения таблицы (SELECT FROM foos, bars).

CREATE VIEW foobars AS
SELECT
    foos.id AS foo_id, foos.something,
    bars.id AS bar_id, bars.somethingelse
FROM foos
INNER JOIN bars ON bars.foo_id = foo.id;

То же самое для ВНУТРЕННИХ СОЕДИНЕНИЙ "многие ко многим"

CREATE VIEW foobars AS
SELECT
    foos.id AS foo_id, foos.something,
    bars.id AS bar_id, bars.somethingelse
FROM foos
INNER JOIN foostobars AS ftb ON ftb.foo_id = foo.id
INNER JOIN bars ON bars.id = ftb.bar_id;
0 голосов
/ 04 октября 2008

Проблема удаления заключается в том, что вы удаляете предикат, который не основан на таблице, из которой вы удаляете. Вы должны удалить на основе предиката соединения. Это выглядело бы как-то строчка:

delete b
  from foo f
  join foobar fb
    on f.FooID = fb.FooID
  join bar b
    on b.BarId = fb.BarID
 where f.FooID = 123

Это позволяет вам составлять таблицу Foo, таблицу Bar и таблицу соединения, в которой записывается, что у Bar есть the Foo. Вам не нужно составлять списки и разбивать их на части. Это плохо, потому что оптимизатор запросов не может использовать индекс для идентификации соответствующих записей - на самом деле это нарушает правило 1NF «Нет повторяющихся групп». Правильная схема будет выглядеть примерно так:

Create table Foo (
     FooID int
    ,[Other Foo attributes]
)

Create table Bar (
     BarID int
    ,[Other Bar attributes]
)

Create table FooBar (
     FooID int
    ,BarID int
)

При наличии соответствующих индексов отношение M: M может храниться в FooBar, а СУБД может эффективно хранить и манипулировать этим в своих собственных структурах данных.

0 голосов
/ 02 октября 2008

Вот как я на самом деле справлялся: когда я получаю уникальное нарушение ограничения, вместо обновления я просто удаляю foo и позволяю каскаду позаботиться о bars. Тогда я просто пытаюсь вставить еще раз. Для этого мне нужно использовать более одного оператора SQL, но, похоже, он работает.

0 голосов
/ 02 октября 2008

Я не думаю, что new.foo_id является правильным в контексте удаления.

Разве это не должно быть УДАЛИТЬ из баров, ГДЕ foo_id = old.foo_id?

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