powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / CHECK CONSTRAINT иногда не срабатывает.
5 сообщений из 5, страница 1 из 1
CHECK CONSTRAINT иногда не срабатывает.
    #38923295
kalombo
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Есть таблица:
Код: 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
CHECK CONSTRAINT иногда не срабатывает.
    #38923305
Alexander A. Sak
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Параллельность, транзакции, их изоляция и типа все такое.

Код: 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
CHECK CONSTRAINT иногда не срабатывает.
    #38923310
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));



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

А вообще-то задача вызывает подозрение на ошибку в проектировании.
...
Рейтинг: 0 / 0
CHECK CONSTRAINT иногда не срабатывает.
    #38923317
kalombo
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
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
CHECK CONSTRAINT иногда не срабатывает.
    #38926435
LeXa NalBat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
5 сообщений из 5, страница 1 из 1
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / CHECK CONSTRAINT иногда не срабатывает.
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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