Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / CHECK CONSTRAINT иногда не срабатывает. / 5 сообщений из 5, страница 1 из 1
01.04.2015, 07:45
    #38923295
kalombo
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
CHECK CONSTRAINT иногда не срабатывает.
Есть таблица:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
CREATE TABLE link
(
  id serial NOT NULL,
  "group" character varying(250),
  portid1 integer NOT NULL,
  portid2 integer NOT NULL,
  active boolean NOT NULL DEFAULT true,
  CONSTRAINT link_pkey PRIMARY KEY (id),
  CONSTRAINT unique_portid1 CHECK (check_link(portid1, id)),
  CONSTRAINT unique_portid2 CHECK (check_link(portid2, id))
)

CREATE OR REPLACE FUNCTION check_link(portid integer, linkid integer)
  RETURNS boolean AS
$BODY$
DECLARE
  i int;
BEGIN
  IF EXISTS (SELECT * FROM link 
  WHERE (portid1 = portid or portid2=portid) and id!=linkid) THEN
    RETURN false;
  END IF;

  RETURN true;  
END



Смысл этого CHECK CONSTRAINT в том, что в таблице значения в колноках portid1 и portid2 должны быть различны, то есть если в колонке portid1 уже присутствует какое-то значение, то оно уже не может появится ни в колонке portid1, ни в колонке portid2, аналогично и для portid2.

Я написал данный constraint, проверил его с помощью простых INSERT и UPDATE - работает. Однако, сегодня обнаружил в таблице вот такой набор записей:

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
select * from link where portid1 = 74581 or portid2=74581;
  id   | group | portid1 | portid2 | active
-------+-------+---------+---------+--------
 76628 |       |  113945 |   74581 | t
 76629 |       |  113945 |   74581 | t
 76630 |       |  113945 |   74581 | t
 76632 |       |  113945 |   74581 | t
 76633 |       |  113945 |   74581 | t
 76634 |       |   74581 |  113945 | t
 76645 |       |  113945 |   74581 | t
(7 rows)



Пробовал сделать Insert с этими же portid, constraint отрабатывает, не получается вставить запись. Попробовал удалить constraint, повесить заново - не получилось, то есть никто не мог удалить constraint, вставить записи и снова повесить. Как оказалось такая ситуация не единичная.
Подскажите, как такое могло получится? Потому что я использую SELECT в constraint? И как сделать правильно?
...
Рейтинг: 0 / 0
01.04.2015, 08:13
    #38923305
Alexander A. Sak
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
CHECK CONSTRAINT иногда не срабатывает.
Параллельность, транзакции, их изоляция и типа все такое.

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
postgres=# insert into link (portid1, portid2) values (1,2);
INSERT 0 1

postgres=# insert into link (portid1, portid2) values (2,3);
ОШИБКА:  новая строка в отношении "link" нарушает ограничение-проверку "unique_portid1"
ПОДРОБНОСТИ:  Ошибочная строка содержит (2, null, 2, 3, t).

postgres=# begin transaction;
BEGIN

postgres=# insert into link (portid1, portid2) values (3, 4);
INSERT 0 1

--
-- ПОСЛЕ ЭТОГО В ДРУГОМ КОННЕКТЕ ВЫПОЛНИЛИ ТОТ ЖЕ САМЫЙ INSERT С (3, 4)
--

postgres=# commit;
COMMIT

postgres=# select * from link;
 id | group | portid1 | portid2 | active 
----+-------+---------+---------+--------
  1 |       |       1 |       2 | t
  3 |       |       3 |       4 | t
  4 |       |       3 |       4 | t
...
Рейтинг: 0 / 0
01.04.2015, 08:29
    #38923310
Alexander A. Sak
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
CHECK CONSTRAINT иногда не срабатывает.
Как сделать правильно точно не скажу. Придумал вариант -- два уникальных индекса по выражению:

Код: sql
1.
2.
3.
create unique index ui_1 on link ((case when portid1<portid2 then portid1 else portid2 end));

create unique index ui_2 on link ((case when portid1>portid2 then portid1 else portid2 end));



Приведенный выше пример с дублированием уже не проходит. Вторая транзакция начинает ждать окончания первой, и в зависимости от нее либо завершаться нормально, либо выдавать ошибку. Дублей не появляется.

А вообще-то задача вызывает подозрение на ошибку в проектировании.
...
Рейтинг: 0 / 0
01.04.2015, 08:43
    #38923317
kalombo
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
CHECK CONSTRAINT иногда не срабатывает.
Alexander A. SakКак сделать правильно точно не скажу. Придумал вариант -- два уникальных индекса по выражению:

Код: sql
1.
2.
3.
create unique index ui_1 on link ((case when portid1<portid2 then portid1 else portid2 end));

create unique index ui_2 on link ((case when portid1>portid2 then portid1 else portid2 end));




Спасибо, мне тоже похожее решение подсказали:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
CREATE TABLE link
(
  id serial NOT NULL,
  "group" character varying(250),
  portid1 integer NOT NULL,
  portid2 integer NOT NULL,
  active boolean NOT NULL DEFAULT true,
  CONSTRAINT link_pkey PRIMARY KEY (id),
  CONSTRAINT snmp_ports_fkey1 FOREIGN KEY (portid1)
      REFERENCES snmp_ports (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE CASCADE,
  CONSTRAINT snmp_ports_fkey2 FOREIGN KEY (portid2)
      REFERENCES snmp_ports (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE CASCADE,
  CONSTRAINT portid1_unique UNIQUE (portid1),
  CONSTRAINT portid2_unique UNIQUE (portid2),
  CONSTRAINT portids_check CHECK (portid1 <> portid2)
)

CREATE UNIQUE INDEX portids_unique
  ON link
  USING btree
  ((LEAST(portid1, portid2)), (GREATEST(portid1, portid2)));


Только еще check приходится использовать.


Alexander A. SakПриведенный выше пример с дублированием уже не проходит. Вторая транзакция начинает ждать окончания первой, и в зависимости от нее либо завершаться нормально, либо выдавать ошибку. Дублей не появляется.

А вообще-то задача вызывает подозрение на ошибку в проектировании.

Да, похоже на ошибку в проектировании, я специально показал еще FOREIGN KEY, чтобы было понятно, что из себя таблица представляет. То есть это порты оборудования, которые могут быть соединены между собой, естественно, один порт дважды соединен быть не может. Я так понимаю правильней было бы не создавать таблицу link, а просто добавить поле link_to в табличке snmp_ports?
...
Рейтинг: 0 / 0
03.04.2015, 15:42
    #38926435
LeXa NalBat
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
CHECK CONSTRAINT иногда не срабатывает.
...
Рейтинг: 0 / 0
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / CHECK CONSTRAINT иногда не срабатывает. / 5 сообщений из 5, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]