powered by simpleCommunicator - 2.0.60     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / как проверить уникальность при невозмлжности использовать индекс
12 сообщений из 12, страница 1 из 1
как проверить уникальность при невозмлжности использовать индекс
    #35286542
Gold_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Всем доброго дня..

Есть необходимость проверить уникальность по некотрым полям, при некоторых условиях. Возможностей индекса (функционального. частичного) не хватает. Решил использовать триггеры. Написать триггеры 100% гарантирующих уникальность не сумел. Что хотел сделать продемонстрирую на простом примере: уникальность по одному полю.
Код: plaintext
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.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
CREATE TABLE t1
(
  c int4
) ;
CREATE OR REPLACE FUNCTION f_check_iniq_ins_upd_aft()
  RETURNS trigger AS
$BODY$

DECLARE
cnt	integer;
	
BEGIN

--если заремарнное ниже предложения могло работать, то триггер отрабатывал бы правильно
--SET TRANSACTION ISOLATION LEVEL  SERIALIZABLE;

SELECT  INTO cnt 
		COUNT( 1 )
		FROM 
			t1
		WHERE c = NEW.c;

--SET TRANSACTION ISOLATION LEVEL  READ COMMITTED;		
	IF cnt> 1  THEN
		RAISE EXCEPTION 'Ошибка!!!';
	END IF;


RETURN NEW;
END;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE;


  
CREATE TRIGGER tr_test
  AFTER INSERT OR UPDATE
  ON t1
  FOR EACH ROW
  EXECUTE PROCEDURE f_check_iniq_ins_upd_aft();


Код: plaintext
DROP TABLE t1;

Как правильно написать такой триггер
...
Рейтинг: 0 / 0
как проверить уникальность при невозмлжности использовать индекс
    #35287436
ЯЕХХ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Мнэ... Можно пример, почему встроенных возможностей не хватило?

Для БД попроще используется такая техника: строится вспомогательная таблица с нужными полями и обычным уникальным индексом. Эта табличка заполняется /триггером или из приложения/ при изменении основной таблицы, при выполнении заданных условий. Как бы эмуляция частичного индекса получается.
...
Рейтинг: 0 / 0
как проверить уникальность при невозмлжности использовать индекс
    #35287609
Gold_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
В настоящее время скорее из-за ошибки проектировки, уникальность зависисит от значений в связянной таблице (решение с доп. таблицей подойдет). Еще пример, проверка на не пересечение интервалов
...
Рейтинг: 0 / 0
как проверить уникальность при невозмлжности использовать индекс
    #35287776
4321
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Gold_В настоящее время скорее из-за ошибки проектировки, уникальность зависисит от значений в связянной таблице (решение с доп. таблицей подойдет). Еще пример, проверка на не пересечение интерваловмне кажется это решается написанием булевой ф-ции и использованием не триггера, но констрайнта CHECK (который сам где-то там, внутре, создаст констрайнт-триггер, но это нам вроде как знать и не обязательно).

Кстати сказать - и на связанную таблицу ведь тоже надо что-то вешать. Или она намертво неапдейтуемая?
...
Рейтинг: 0 / 0
как проверить уникальность при невозмлжности использовать индекс
    #35288043
Gold_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
4321 Gold_В настоящее время скорее из-за ошибки проектировки, уникальность зависисит от значений в связянной таблице (решение с доп. таблицей подойдет). Еще пример, проверка на не пересечение интерваловмне кажется это решается написанием булевой ф-ции и использованием не триггера, но констрайнта CHECK ...
Что должна проверять функция?
...
Рейтинг: 0 / 0
как проверить уникальность при невозмлжности использовать индекс
    #35288080
Фотография Ёш
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
4321мне кажется это решается написанием булевой ф-ции и использованием не триггера, но констрайнта CHECK (который сам где-то там, внутре, создаст констрайнт-триггер, но это нам вроде как знать и не обязательно).

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
create table ttt (id serial primary key, a int, b int, check(check_intersec(id, a,b)));

CREATE OR REPLACE FUNCTION check_intersec(pk int4, v1 int4, v2 int4)
  RETURNS bool AS
$BODY$
declare
   r bool;
   d int default current_setting('custom.test_sleep');
begin
   raise notice 'check: % % %', pk, v1, v2;
   r := not (select count(*) >  0  from ttt where id != pk and (v1 between a and b or v2 between a and b));
   raise notice '%, test sleep %', r, d;
   perform pg_sleep(d);
   return r;
end
$BODY$
  LANGUAGE 'plpgsql' VOLATILE;

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
 1 : begin;
 1 : set custom.test_sleep to  10 ;
   2 : begin;
   2 : set custom.test_sleep to  1 ;
 1 : insert into ttt values (default,  1 ,  3 ); -- спит 10 секунд
   2 : insert into ttt values (default,  1 ,  3 ); end; -- спит 1 секунду и комитится
 1 : end; -- успешно !
и всё ок :) из-за того что между проверкой условия и комитом есть промежуток времени + в этот промежуток времени соседнии инсерты не блокируются.

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

можно сделать deferred constraint триггер, но фича с задержкой (промежутком времени между проверкой и реальным коммитом) и у него будет...

имхо только через блокировки...
...
Рейтинг: 0 / 0
как проверить уникальность при невозмлжности использовать индекс
    #35288325
4321
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ёшспасибо. еще раз напомнили давнопроверенное, - что (межтабличная) уникальность через чек - это заблуждение. Кстати, никакого слипа внутри ф-ии чека и не нужно. Оно и так позволяет. (проверил в двух независимых окнах. Вспомнил, что уже обламывался с идеей.)

Ёшможно сделать deferred constraint триггересли я правильно понял, то честный deferred создается только для FKey-ев. Остальное - врукопашку, т.е. от лукавого. Но интересно было бы проверить. (однако потом, как я помню, врукопашку надо грохать constraint-триггера, поэтому без подготовки не взялся - без аккуратности тут скорее насодишь десяток constraint-триггеров и не будешь понимать, что вообще происходит. А с аккуратностью у меня нынче проблемы - сильнейший "простудифилис весенний").

т.е. ,насколько я вас неправильно понял, - вставлять рекомендуете только через хранимки, а в них делать что-то типа .... FOR UPDATE?
...
Рейтинг: 0 / 0
как проверить уникальность при невозмлжности использовать индекс
    #35289469
Фотография Ёш
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
4321 Ёшможно сделать deferred constraint триггересли я правильно понял, то честный deferred создается только для FKey-ев. Остальное - врукопашку, т.е. от лукавого. Но интересно было бы проверить.вот это вроде ещё "честный deferred", насколько я понимаю:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
Команда:   CREATE CONSTRAINT TRIGGER
Описание:  создать новый триггер для проверки целостности
Синтаксис:
CREATE CONSTRAINT TRIGGER name
    AFTER event [ OR ... ]
    ON table_name
    [ FROM referenced_table_name ]
    { NOT DEFERRABLE | [ DEFERRABLE ] { INITIALLY IMMEDIATE | INITIALLY DEFERRED } }
    FOR EACH ROW
    EXECUTE PROCEDURE funcname ( arguments )
4321т.е. ,насколько я вас неправильно понял, - вставлять рекомендуете только через хранимки, а в них делать что-то типа .... FOR UPDATE?незнаю :) я имел ввиду что можно заблокировать в тригере перед вызовом select count() ... таблицу целиком, что бы исключить конкурирующие insert'ы, а for update имхо не запрещает вроде ж insert...
...
Рейтинг: 0 / 0
как проверить уникальность при невозмлжности использовать индекс
    #35290312
Quadrix
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Если я правильно понял вопрос и требуется, чтобы с одинаковым индексом не создавались строки, то надо триггер сделать не "после", а "до" вставки-изменения

т.е. вместо
Код: plaintext
AFTER INSERT OR UPDATE

поставить
Код: plaintext
BEFORE INSERT OR UPDATE
...
Рейтинг: 0 / 0
как проверить уникальность при невозмлжности использовать индекс
    #35290430
Gold_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
QuadrixЕсли я правильно понял вопрос и требуется, чтобы с одинаковым индексом не создавались строки, то надо триггер сделать не "после", а "до" вставки-изменения

т.е. вместо
Код: plaintext
AFTER INSERT OR UPDATE

поставить
Код: plaintext
BEFORE INSERT OR UPDATE


Вы ошибаетесь. Триггер "до" только ухудшить положение (время между проверкой и потверждением транакции увеличится)

В моем триггере в комментариях тоже описка в строчке:
Код: plaintext
1.
--SET TRANSACTION ISOLATION LEVEL  SERIALIZABLE;
должно быть
Код: plaintext
1.
--SET TRANSACTION ISOLATION LEVEL  Read uncommitted;

Но ни такого уровня, ни возможности переключения в постгресе нету.
...
Рейтинг: 0 / 0
как проверить уникальность при невозмлжности использовать индекс
    #35291743
4321
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ёш вот это вроде ещё "честный deferred", насколько я понимаю:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
Команда:   CREATE CONSTRAINT TRIGGER
Описание:  создать новый триггер для проверки целостности
Синтаксис:
CREATE CONSTRAINT TRIGGER name
    AFTER event [ OR ... ]
    ON table_name
    [ FROM referenced_table_name ]
    { NOT DEFERRABLE | [ DEFERRABLE ] { INITIALLY IMMEDIATE | INITIALLY DEFERRED } }
    FOR EACH ROW
    EXECUTE PROCEDURE funcname ( arguments )
это то я помню. как впрочем и то, что так делать (не вникая особо в кухню) - не рекомендуется. Это так раньше Пк Фк и Чеки создавались. Ну и сейчас можно, но немного о внутренностях надо бы знать. Поскольку и name у триггера кажется будет не тот, что указан в CREATE CONSTRAINT TRIGGER name и дропать его надо будет с исподвыподвертом. (тут, на форуме процедурка бродила на дроп по "имени" создания. Я ее еще пользовал.). Т.ч. сейчас для меня "честный" КТ - это автосозданный постгресом по кляузе ПК, ФК, Чек, а все не покрываемое ими, и требующее ручного CREATE CONSTRAINT TRIGGER - это уже "бубен".


Ёшя имел ввиду что можно заблокировать в тригере перед вызовом select count() ... таблицу целиком, что бы исключить конкурирующие insert'ы, а for update имхо не запрещает вроде ж insert...да, я даже на всякий потестил. не выходит. вот если уникью лочит значение, то тут придется лочить всю таблицу. Т.ч. видимо можно поискать в сторону (межтабличного) "мат-вью" с уникью. Затраты могут окупиться (за счет снятия блокировки таблицы целиком).

Интересно ,в постгресе есть возможность слепить чисто "индексно"-хранимые таблички? (не может видимо быть. т.к. постгрессовский индекс кажется не держит данных о транзакции. А ведь для межтабличного индекса они были бы к месту.)
...
Рейтинг: 0 / 0
как проверить уникальность при невозмлжности использовать индекс
    #35292383
Фотография Ёш
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
4321 Ёш вот это вроде ещё "честный deferred", насколько я понимаю:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
Команда:   CREATE CONSTRAINT TRIGGER
Описание:  создать новый триггер для проверки целостности
Синтаксис:
CREATE CONSTRAINT TRIGGER name
    AFTER event [ OR ... ]
    ON table_name
    [ FROM referenced_table_name ]
    { NOT DEFERRABLE | [ DEFERRABLE ] { INITIALLY IMMEDIATE | INITIALLY DEFERRED } }
    FOR EACH ROW
    EXECUTE PROCEDURE funcname ( arguments )
это то я помню. как впрочем и то, что так делать (не вникая особо в кухню) - не рекомендуется. Это так раньше Пк Фк и Чеки создавались. Ну и сейчас можно, но немного о внутренностях надо бы знать. Поскольку и name у триггера кажется будет не тот, что указан в CREATE CONSTRAINT TRIGGER name и дропать его надо будет с исподвыподвертом. (тут, на форуме процедурка бродила на дроп по "имени" создания. Я ее еще пользовал.). Т.ч. сейчас для меня "честный" КТ - это автосозданный постгресом по кляузе ПК, ФК, Чек, а все не покрываемое ими, и требующее ручного CREATE CONSTRAINT TRIGGER - это уже "бубен".

да, я тоже читал краем уха что-то про это, что не советуют это использовать :) и в доках про constraint trigger name до 8.3 было написано: "The name of the constraint trigger. The actual name of the created trigger will be of the form RI_ConstraintTrigger_0000 (where 0000 is some number assigned by the server). Use this assigned name when dropping the trigger."

но в 8.3 уже: "The name of the constraint trigger. This is also the name to use when modifying the trigger's behavior using SET CONSTRAINTS. The name cannot be schema-qualified — the trigger inherits the schema of its table."

имхо было что-то докручено в плане удобства и разрешения использования этой фишки, но подробностей не читал.

4321 Ёшя имел ввиду что можно заблокировать в тригере перед вызовом select count() ... таблицу целиком, что бы исключить конкурирующие insert'ы, а for update имхо не запрещает вроде ж insert...да, я даже на всякий потестил. не выходит. вот если уникью лочит значение, то тут придется лочить всю таблицу. Т.ч. видимо можно поискать в сторону (межтабличного) "мат-вью" с уникью. Затраты могут окупиться (за счет снятия блокировки таблицы целиком).а можно сделать через unique проверку пересечения хотя бы в одной таблице ? мне почему-то кажется что это возможно, но как сделать в реале - не могу понять %)

допустим есть та же таблица ttt (id int, a int, b int) и нужно что бы в ней небыло пересекающихся промежутков [a,b] (для простоты - a нижняя граница и всегда меньше или равно b). A [a1, b1] персекается с B [a2, b2] если a2 входит в [a1,b1] или b2 входит в [a1,b1]
...
Рейтинг: 0 / 0
12 сообщений из 12, страница 1 из 1
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / как проверить уникальность при невозмлжности использовать индекс
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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