Триггер SQL: изменяемая строка уже была изменена - PullRequest
1 голос
/ 23 октября 2019

У меня есть таблица, в которой есть элементы с полями заказов, которые я использую для рисования их на дереве.

CREATE TABLE items (
    menuId INTEGER,
    itemId INTEGER,
    name VARCHAR, 
    order1 INTEGER,
    order2 INTEGER,
    order3 INTEGER,
    order4 INTEGER,
);

Первичный ключ - это 'menuId' и 'itemId'.

Foreignключ 'menuId' с таблицей 'menus'.

Для каждого меню у меня есть уникальная проверка, например, для menu1 я не могу иметь два элемента с одинаковыми порядками комбинации, но menu2 может.

Я хочу создать триггер, который срабатывает, когда я вставляю или обновляю элемент, поэтому, если добавляется новый элемент с порядком 2, тот с порядком 2 обновляется до 3, порядка с 3 по 4 и так далее. Пока мне все равно, есть ли пробелы между ордерами, это то, что я постараюсь исправить в другой момент.

Триггер такой:

CREATE TRIGGER trg_items_menu_bibu
  BEFORE INSERT OR UPDATE
  ON items
  FOR EACH ROW
  EXECUTE PROCEDURE update_menu_order();

CREATE OR REPLACE FUNCTION update_menu_order()
  RETURNS trigger AS
$BODY$
BEGIN
    IF EXISTS (SELECT name
                FROM items
                WHERE order1 = NEW.order1
                AND order2 = NEW.order2
                AND order3 = NEW.order3
                AND order4 = NEW.order4
                AND menuId = NEW.menuId
                AND itemId <> NEW.itemId) THEN
        -- If exists an item for that menu with the same orders
        IF (NEW.order4 <> 0) THEN
            -- If it's an item in the sublevel4
            UPDATE items SET order4 = order4 + 1
                WHERE order1 = NEW.order1
                AND order2 = NEW.order2
                AND order3 = NEW.order3
                AND order4 = NEW.order4
                AND menuId = NEW.menuId
                AND itemId <> NEW.itemId;
        ELSEIF (NEW.order3 <> 0) THEN
            -- If it's an item in the sublevel3
            UPDATE items SET order3 = order3 + 1
                WHERE order1 = NEW.order1
                AND order2 = NEW.order2
                AND order3 = NEW.order3
                AND order4 = 0
                AND menuId = NEW.menuId
                AND itemId <> NEW.itemId;
        ELSEIF (NEW.order2 <> 0) THEN
            -- If it's an item in the sublevel2
            UPDATE items SET order2 = order2 + 1
                WHERE order1 = NEW.order1
                AND order2 = NEW.order2
                AND order3 = 0
                AND order4 = 0
                AND menuId = NEW.menuId
                AND itemId <> NEW.itemId;
        ELSE
            -- If it's an item in the sublevel1
            UPDATE items SET order1 = order1 + 1
                WHERE order1 = NEW.order1
                AND order2 = 0
                AND order3 = 0
                AND order4 = 0
                AND menuId = NEW.menuId
                AND itemId <> NEW.itemId;
        END IF;
    END IF;

    RETURN NEW;
END;
$BODY$
  LANGUAGE plpgsql;

Если яесть следующие элементы:

menuId  itemId  name    order1  order2  order3  order4
1       1       menu1   1       0       0       0
1       2       menu2   1       1       0       0
1       3       menu3   1       2       0       0

И я пытаюсь вставить следующее:

menuId  itemId  name    order1  order2  order3  order4
1       4       menu4   1       1       0       0       

Триггер корректно обновляется до этого:

menuId  itemId  name    order1  order2  order3  order4
1       1       menu1   1       0       0       0
1       4       menu4   1       1       0       0       
1       2       menu2   1       2       0       0
1       3       menu3   1       3       0       0

Но если вместо этого япопытался обновить:

1       3       menu3   1       1       0       0

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

Я понимаю, что проблема в том, что триггер сделал menu2 update order2 до 3, который заставил menu3 обновить order2 до 4, но menu3 уже было изменено при первоначальном обновлении.

Что я делаюне знаю, как я могу решить эту проблему или если это вообще возможно.

1 Ответ

0 голосов
/ 23 октября 2019

Используйте double precision вместо integer для заказа.

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

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

Это будет прекрасно работать с ORDER BY;если по какой-либо причине вам необходимо отобразить интегральный порядок, сгенерируйте его в запросе с помощью оконной функции dense_rank.

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