powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Триггер DELETE в случае группового удаления данных
42 сообщений из 42, показаны все 2 страниц
Триггер DELETE в случае группового удаления данных
    #38884718
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Имеется триггерная функция. Она не только производит удаление, но и еще манипулирует данными таблицы. При удалении одной записи - все работает прекрасно. Как только приходит запрос на удаление набора записей начинается непонятное. Я так понял, что при поступлении на удаление набора строк, Postgres, вначале, выполняет для каждой строки всего набора секцию before. Затем, снова поочередно для каждой строки из набора, удаление. Ну и в конце, аналогично, секцию after.
Первый вопрос - верно ли мое предположение?
Второй, если "да", нет ли способа определения триггера (триггерной функции) на удаления, при котором обрабатывается каждая запись из набора на удаления поочередно? (то есть берется строка... к ней применяется before, удаление, after... берется следующая строка...)
Ну и третий вопрос, в догонку... Чем и как отлаживать триггерные функции? На форуме перечитал много подобных веток. Но, во первых, все они старые. Во вторых, нашел только про отладчик под виндовс... (сервер Postgres я запускаю на Убунте в ВиртуалБоксе. Соединяюсь с сервером при помощи pgAdmin3 из OS X) Буду очень благодарен за дельный совет по отладке.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38884733
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38884742
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
vyegorov,

Извиняюсь, сразу не указал сей важный момент. Триггер объявлен именно FOR EACH ROW. И когда был один AFTER. И затем, когда я сделал два, разбил функцию на два триггера - BEFORE и AFTER (собственно, так я понял, что могу предположить вышеописанную проблему при групповом удалении. часть багов это решило, но один остался, поэтому и пишу здесь)
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38884888
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
EvgIqПри удалении одной записи - все работает прекрасно. Как только приходит запрос на удаление набора записей начинается непонятное.
Все же, в чем собственно проблема?

И приведите определения таблиц, триггеров и, по возможности, функций.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38884914
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
vyegorov,
Тут получится много, но раз уж начал :)
В общем пытаюсь реализовать систему хранения структуры "дерево", почитать о чем речь можно здесь: http://habrahabr.ru/post/166699/
Я являюсь автором этой статьи.
На данный момент пытаюсь реализовать работу с деревом на сервере. Все вроде работает кроме удаления (и я подозреваю, что такая же история будет и при групповом перемещении... но это маловероятная операция)

При удалении записи идет проверка на "дырки", которые могут быть вызваны удалением. Их пытаюсь "заделать" перемещением крайне правого элемента на место "дырки"
Когда удаляешь одну запись работает прекрасно. Когда удаляешь группу с
Код: plaintext
count_childs=0
После разбиение триггера и функции AFTER на 2-е - BEFORE и AFTER удалось побороть часть проблем. Но одна осталась. А именно если "дырки" получаются подряд на одном уровне в нескольких местах. То есть дерево "сворачивается" некорректно.

Вот код, может кого и заинтересует :) А может кто-то и подскажет что я делаю неправильно (не судите строго, это типо мое хобби, в коде много корявостей, и всяких казалось бы лишних вещей, связанных с тем что правлю его еще походу)

Код: 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.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.
291.
292.
293.
294.
295.
296.
297.
298.
299.
300.
301.
302.
303.
304.
305.
306.
307.
308.
309.
310.
311.
312.
313.
314.
315.
316.
317.
318.
319.
320.
321.
322.
323.
324.
325.
326.
327.
328.
329.
330.
331.
332.
333.
334.
335.
336.
337.
338.
339.
340.
341.
342.
343.
344.
345.
346.
347.
348.
349.
350.
351.
352.
353.
354.
355.
356.
357.
358.
359.
360.
361.
362.
363.
364.
365.
366.
367.
368.
369.
370.
371.
CREATE OR REPLACE FUNCTION const_ch()
  RETURNS integer AS
$BODY$BEGIN
	-- устанавливаем допустимое количество Детей у элемента в дереве
	RETURN 3;
END$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION const_ch()
  OWNER TO postgres;


CREATE OR REPLACE FUNCTION const_lv()
  RETURNS integer AS
$BODY$BEGIN
	-- устанавливаем допустимое количество Уровней в дереве
	RETURN 4;
END$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION const_lv()
  OWNER TO postgres;


CREATE OR REPLACE FUNCTION fill_emp(
    old_p_id bigint,
    old_p_lv integer,
    old_p_ch integer,
    id_dep bigint)
  RETURNS integer AS
$BODY$DECLARE	
	lv		integer;	-- количество уровней в дереве
	ch		integer;	-- допустимое количество детей	
	max_id_for_parent	bigint; -- для крайнего правого ребенка
	limit_right	bigint;
	delta_id	bigint;
	
BEGIN
	-- У каждого элемента могут быть дети
	-- последовательность детей формируется непрерывной,
	-- если удалить (или перенести) крайне правого ребенка то нет проблем
	-- Если же "выдернуть" ребенка из начала или середины последовательности,
	-- то остается "дырка".
	-- Надо "заделать" ее переносом крайне правого ребенка.
	
	ch:=const_ch();
	lv:=const_lv();

	--RAISE EXCEPTION '% + (((%+1)^(%-%-1))*%)',old_p_id, ch, lv, old_p_lv, old_p_ch;
	
	-- Найдем максимально правого существующего ребенка у родителя "дырки"
	max_id_for_parent:=old_p_id+(power((ch+1),(lv-old_p_lv-1))*old_p_ch)::bigint;
	---RAISE EXCEPTION '%',max_id_for_parent;

	
	IF id_dep < max_id_for_parent THEN -- если изъятый элемент был не последний
		-- имеем "дырку"

		-- найдем правую границу для возможной подгруппы Элемента с max_id_for_parent
		--limit_right:=(max_id_for_parent-1+ power((ch+1),(lv-old_p_lv-1))::bigint)::bigint;
		limit_right:=max_id_for_parent-1+ ((ch+1)^(lv-old_p_lv-1))::bigint;
		--RAISE EXCEPTION 'id_dep=%, max_id_for_parent=%, limit_right=%', id_dep, max_id_for_parent, limit_right;

		-- делаем апдейт всех детей max_id_for_parent
		-- перемещение в пределах уровня - просто дельта между max_id_for_parent и id_dep    
		-- id = (id - max_id_for_parent + id_dep)::bigint,
		delta_id:=max_id_for_parent-id_dep;
		UPDATE board_group 
		SET 
			id=id-delta_id,
			parent_id=parent_id-delta_id
		WHERE board_group.id BETWEEN max_id_for_parent+1 AND limit_right;

		-- не забудем про сам элемент
		-- второй апдейт нужен т.к. родитель у перемещаемого Элемента не изменяется
		UPDATE board_group 
		SET 
			id=id_dep
		WHERE board_group.id=max_id_for_parent;
	END IF;
	RETURN 0;

END;$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION fill_emp(bigint, integer, integer, bigint)
  OWNER TO postgres;


CREATE TABLE board_group
(
  id bigint NOT NULL,
  lvl integer NOT NULL,
  count_childs integer NOT NULL,
  nm character varying(150) NOT NULL,
  parent_id bigint NOT NULL,
  CONSTRAINT board_group_pkey PRIMARY KEY (id),
  CONSTRAINT board_group_parent_id_19050eec0369f2c9_fk_board_group_id FOREIGN KEY (parent_id)
      REFERENCES board_group (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED
)
WITH (
  OIDS=FALSE
);
ALTER TABLE board_group
  OWNER TO postgres;


CREATE INDEX board_group_6be37982
  ON board_group
  USING btree
  (parent_id);


CREATE TRIGGER after_del
  AFTER DELETE
  ON board_group
  FOR EACH ROW
  EXECUTE PROCEDURE del_id_board_after();


CREATE TRIGGER after_upd_parent_id
  AFTER UPDATE
  ON board_group
  FOR EACH ROW
  EXECUTE PROCEDURE upd_move_board();


CREATE TRIGGER before_del
  BEFORE DELETE
  ON board_group
  FOR EACH ROW
  EXECUTE PROCEDURE del_id_board_before();


CREATE TRIGGER before_new_id
  BEFORE INSERT
  ON board_group
  FOR EACH ROW
  EXECUTE PROCEDURE new_id_board();



CREATE OR REPLACE FUNCTION del_id_board_after()
  RETURNS trigger AS
$BODY$DECLARE

	old_p_id	bigint;
	old_p_lv	integer;
	old_p_ch	integer;
	i		integer;

BEGIN
	
	-- возьмем Родителя удаленного Элемента
	SELECT id, lvl, count_childs 
		INTO old_p_id, old_p_lv, old_p_ch 
		FROM board_group 
	WHERE id=OLD.parent_id;	

	

	IF (old_p_ch > 0) THEN
		--- RAISE EXCEPTION 'old_p_id=%, old_p_lv=%, old_p_ch=%, OLD.id=%',old_p_id, old_p_lv, old_p_ch, OLD.id;
		i := fill_emp(old_p_id, old_p_lv, old_p_ch+1, OLD.id);
	END IF;
	
	RETURN NULL;
END;$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION del_id_board_after()
  OWNER TO postgres;


CREATE OR REPLACE FUNCTION del_id_board_before()
  RETURNS trigger AS
$BODY$DECLARE

	old_p_id	bigint;
	old_p_lv	integer;
	old_p_ch	integer;
	i		integer;

BEGIN
	IF OLD.count_childs > 0 THEN
		RAISE EXCEPTION 'У данного элемента имеются дети в количестве % шт. Удалите сначала их.', OLD.count_childs;
	END IF;

	-- возьмем Родителя удаленного Элемента
	SELECT id, lvl, count_childs 
		INTO old_p_id, old_p_lv, old_p_ch 
		FROM board_group 
	WHERE id=OLD.parent_id;	

	-- уменьшим количество детей у родителя
	UPDATE board_group SET count_childs = count_childs-1 WHERE id = OLD.parent_id;
	
	RETURN OLD;
END;$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION del_id_board_before()
  OWNER TO postgres;


CREATE OR REPLACE FUNCTION new_id_board()
  RETURNS trigger AS
$BODY$DECLARE
	lv		integer;	-- количество уровней в дереве
	ch		integer;	-- допустимое количество детей
	id_new		bigint;		-- вычесляемый id нового элемента
	id_p		bigint;		-- id родителя 
	lv_p		integer;	-- уровень родителя
	count_childs_p	integer;	-- количество детей родителя
BEGIN
	-- для создания нового элемента достаточно заполнить  у него поле parent
	id_p := NEW.parent_id;

	ch := const_ch();
	lv := const_lv();

	-- найдем родителя
	SELECT lvl, count_childs INTO lv_p, count_childs_p FROM board_group WHERE board_group.id=id_p;	
	

	-- Проверка 1: может ли родитель еше иметь детей
	IF count_childs_p >= ch THEN
		RAISE EXCEPTION 'Родителю c id=% более нельзя иметь детей (их количество уже %)', id_p, ch;
	END IF;

	-- Проверка 2: может ли родитель иметь детей
	IF lv_p = lv THEN
		RAISE EXCEPTION 'Элементу с id=% нельзя иметь детей (его уровень % максимально возможный)', id_p, lv;
	END IF;
	
	NEW.id := ((power((ch+1),(lv-lv_p-1)))*(count_childs_p+1))::bigint+id_p;
	NEW.lvl := lv_p + 1;
	NEW.count_childs := 0;

	NEW.nm := NEW.id; -- ЭТО ТОЛЬКО ДЛЯ ТЕСТОВ, затем убрать
	
	UPDATE board_group SET count_childs = count_childs_p+1 WHERE board_group.id = id_p;
	
	return NEW;
END;$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION new_id_board()
  OWNER TO postgres;



CREATE OR REPLACE FUNCTION upd_move_board()
  RETURNS trigger AS
$BODY$DECLARE
	lv		integer;	
	ch		integer;	
	new_p_id	bigint;
	new_p_lv	integer;
	new_p_ch	integer;
	old_p_id	bigint;
	old_p_lv	integer;
	old_p_ch	integer;
	delta_lv	integer;
	dlv		integer;
	limit_right	bigint;
	max_lv		integer;
	i		integer;
	id_dest		bigint;
	id_dep		bigint;
	x		bigint;

BEGIN

	IF (NEW.id = OLD.id) AND (NEW.parent_id != OLD.parent_id) THEN

		IF NEW.id = NEW.parent_id THEN
			RAISE EXCEPTION 'Перенос id=% к самому себе невозможен', NEW.id;
		END IF;
		
		ch := const_ch();
		lv := const_lv();
		
		-- Имеем перенос к другому родителю
		-- найдем этого другого родителя
		new_p_id := NEW.parent_id;
		SELECT lvl, count_childs INTO new_p_lv, new_p_ch FROM board_group WHERE board_group.id=new_p_id;	
		
		-- Проверка 1: может ли родитель еше иметь детей
		IF new_p_ch >= ch THEN
			RAISE EXCEPTION 'Родителю c id=% более нельзя иметь детей (их количество уже %)', new_p_id, ch;
		END IF;

		-- Проверка 2: может ли родитель иметь детей
		IF new_p_lv = lv THEN
			RAISE EXCEPTION 'Элементу с id=% нельзя иметь детей (его уровень % максимально возможный)', new_p_id, lv;
		END IF;

		old_p_id := OLD.parent_id;
		SELECT lvl, count_childs INTO old_p_lv, old_p_ch FROM board_group WHERE board_group.id=old_p_id;	
		
		delta_lv := old_p_lv - new_p_lv;

		
		dlv:=@delta_lv;-- модуль
	
		-- Так как переносим элемент и всех его детей 
		-- то найдем правую границу диапазона всех этих элементов
		id_dep := OLD.id;
		limit_right := id_dep-1 + ((ch+1)^(lv-old_p_lv-1))::bigint;

		-- Найдем максимальное значения поля lvl для продгруппы переноса
		SELECT max(lvl) INTO max_lv FROM board_group WHERE board_group.id BETWEEN (id_dep+1) AND limit_right;
		
		
		IF (max_lv - delta_lv) > lv THEN
			RAISE EXCEPTION 'Перенос невозможен из-за выхода переносимых элементов за нижний уровень';
		END IF;


		-- найдем новый id для головного элемента, он понадобится в апдейте переносимой группы
		id_dest :=new_p_id+(((ch+1)^(lv-new_p_lv-1))*(new_p_ch+1))::bigint;

		UPDATE board_group 
		SET 
			id = id_dest,
			lvl = lvl-delta_lv,
			parent_id = new_p_id
		WHERE id=id_dep;
		
		x := ((ch+1)^dlv)::bigint;
		--RAISE EXCEPTION 'x=%',x;

		IF delta_lv < 0 THEN -- вниз
			UPDATE board_group 
			SET 
				id = ((id-id_dep)/x) + id_dest,
				lvl = lvl-delta_lv,
				parent_id = ((parent_id-id_dep)/x) + id_dest
			WHERE id BETWEEN (id_dep+1) AND limit_right;
		
		ELSE -- вверх 
			UPDATE board_group 
			SET 
				id = ((id-id_dep)*x) + id_dest,
				lvl = lvl-delta_lv,
				parent_id = ((parent_id-id_dep)*x) + id_dest
			WHERE id BETWEEN (id_dep+1) AND limit_right;
		
		END IF;	

		
		-- Увеличиваем количество детей у нового родителя
		UPDATE board_group SET count_childs = count_childs+1 WHERE id = new_p_id;

		-- Уменьшаем количество детей у старого родителя
		UPDATE board_group SET count_childs = count_childs-1 WHERE id = old_p_id;

		-- проверим на дырки
		IF (old_p_ch > 1) THEN
			i := fill_emp(old_p_id, old_p_lv, old_p_ch, id_dep);
		END IF;	

	END IF;
	RETURN NEW;
END;$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION upd_move_board()
  OWNER TO postgres;
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38884920
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
в посте выше есть недосказанность:
Когда удаляешь одну запись работает прекрасно. Когда удаляешь группу с count_childs=0 происходит непонятное поведение.
извиняюсь...
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885031
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
EvgIqв посте выше есть недосказанность:
Когда удаляешь одну запись работает прекрасно. Когда удаляешь группу с count_childs=0 происходит непонятное поведение.
извиняюсь...

Скажите словами в чем проявляется “непонятное поведение”?
Вокруг да около ходите, а в чем проблема не говорите.

И пример приведите для корректного случая и для "непонятного", будет проще.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885099
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
vyegorov,
На словах довольно трудно объяснить. При удалении элемента, если он не последний у родителя, в последовательности детей получается "дырка". Ее наличие недопустимо. Поэтому на ее место перемещаю крайне правого ребенка у того же родителя. Соответсвенно происходит пересчет этого ребенка и его поддерева согласно нового места (на место "дырки").

Одиночное перемещение работает.
То есть имеем, например, последовательность детей у одного родителя (X1,X2,Y1..Yn), где X - Элементы без детей, с count_childs=0, (их можно удалить), а Y элементы с детьми, которые, в свою очередь содержат подобную же последовательность детей. Если удалить один Х из любого места, Yn нормально "встанет", со всем своим поддеревом, на место удаленного Х, произойдет пересчет id как самого Yn (он станет равным Х), так и всех его детей.

Но если удалять одним запросом все элементы Х (с count_childs=0) на всех уровнях (а это допустимо при такой структуре дерева, и ингода может быть удобно), то нормального пересчета (сворачивания дерева) не происходит, и id у потомков, перемещаемого на место "дырки" элемента, получаются некорректные. Точнее там запускается серия перемещений, так как "дырок" в этом случае образуется много.

Сегодня вечером сделаю подробный пример со скринами :)
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885152
alex564657498765453
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
EvgIqв посте выше есть недосказанность:
Когда удаляешь одну запись работает прекрасно. Когда удаляешь группу с count_childs=0 происходит непонятное поведение.
извиняюсь...

мне думается, что пост выше надо пересказать в двух словах, а то проматывать колёсиком лень, не то что ещё прочитать и вдуматься и разобрать на запчасти эту длинную мысль.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885170
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
alex564657498765453, а зачем тогда Вы здесь? остроумием меряться? так я не школьник уже, но ответить смогу не сумлевайтесь
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885211
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
EvgIq,

У меня есть ощущение, что проблема не в триггерах, а в алгоритме. Нужно вникнуть.

Предоставьте пожалуйста:
1. Структуру таблиц
2. Триггера и функции (уже есть)
3. Очень небольшие тестовые данные
4. Для этих данных, покажите что конкретно не работает и как это воспроизвести.
5. Покажите, как бы вы хотели, чтобы это работало.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885308
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
vyegorov,
- Таблица в листинге есть
- В функции fill_emp поменял несколько строк, начиная с 21-й:

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
-- Найдем максимально правого существующего ребенка у родителя "дырки"
	-- max_id_for_parent:=old_p_id+(power((ch+1),(lv-old_p_lv-1))*old_p_ch)::bigint;
	-- Найдем максимально правого ребенка
	SELECT max(id) 
		INTO max_id_for_parent 
		FROM board_group 
	WHERE parent_id = old_p_id;

	--RAISE EXCEPTION '%',max_id_for_parent;



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

Теперь по остальным Вашим, совершенно справедливым, замечаниям:

1. Для теста, в нижепреведенных функциях RETURN'ы должны быть такие (размерность дерева):
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
CREATE OR REPLACE FUNCTION const_ch()
  RETURNS integer AS
$BODY$BEGIN
	-- устанавливаем допустимое количество Детей у элемента в дереве
	RETURN 3;
END$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION const_ch()
  OWNER TO postgres;

CREATE OR REPLACE FUNCTION const_lv()
  RETURNS integer AS
$BODY$BEGIN
	-- устанавливаем допустимое количество Уровней в дереве
	RETURN 4;
END$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION const_lv()
  OWNER TO postgres;



2. Руками вносим первый элемент, корень дерева:
id = -128
lvl = 0
count_childs =0
nm - само заполнится значением id при создании
parent_id = -128

3. Заполняем дерево, каждый раз выбирая родителя с максимальным id на предыдущем уровне, делаем это 3 раза (создаем 3 уровня, для демонстрации бага хватит)

4. Имеем исходное дерево http://itmag.es/6fq7Y
оно же в админе Django http://itmag.es/6xOz5

5. Сначала пример нормальной работы.
Удаляем один элемент -64
Результат - http://itmag.es/34b7r
в админке - http://itmag.es/9UyH
Это пример нормальной работы. Дети/уровни/коды - все как надо. Первоначальный элемент (-64) удалился, получилась "дырка. На его место переместился элемент бывший ранее (64) со всеми его детьми.

6. Снова вернемся к исходному дереву, но уже удалим все элемент которые не имеют детей:
http://itmag.es/6KB12
http://itmag.es/1GnmO

7. Получилось такое безобразие:
http://itmag.es/6aOqz
http://itmag.es/3jzQ7

А должно быть так:
http://itmag.es/5itRR

То есть, элемент 64 как и положено стал (-64), но вот его единственный ребенок должен быть с номером (-48) а не (-16). Если добавить уровней и элементов, ситуация становится еще запутаннее.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885353
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
EvgIq,

Несколько замечаний:
1. Функции `const_ch` и `const_lv` лучше сделать как `LANGUAGE sql`, они тогда будут "прозрачны" для планировщика;
2. Также их стоит определить как `IMMUTABLE`;
3. Правильно не `childs`, а `children` (это так, придраться).

По существу. Создал новую базу. Создал схему (все, что было заявлено). Ничего не могу вставить в таблицу, получаю:
Код: sql
1.
2.
3.
4.
insert into board_group(id,lvl,count_childs,parent_id) values (-128,0,0,-128);

ERROR:  null value in column "id" violates not-null constraint
DETAIL:  Failing row contains (null, null, 0, null, -128).



Что я делаю не так.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885411
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
vyegorov, вбивайте в id цифру ноль (оно потом станет как положено), а в parent_id уже только существующего родителя, на основании его вычислится id.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885425
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
EvgIq,

Я пробовал:
Код: sql
1.
2.
3.
insert into board_group(id,lvl,count_childs,parent_id) values (0,0,0,0);
insert into board_group(id,lvl,count_childs) values (0,0,0);
insert into board_group(id) values (0);


Та же ошибка. Что-то `new_id_board()` не фурычит у вас.

Вы могли бы не писать "вбивайте", а привести скрипт, который на пустой базе позволит воспроизвести вашу проблему.
Пока я только отладкой занят.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885432
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
vyegorov, я в pgAdmin вбиваю, или в админке Django, все работает
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885446
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Вы parent_id ставьте существующий.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885458
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Да, перед первой вставкой, (-128,0,0,0,-128) триггеры отключите. Из-за этого может быть ошибка.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885512
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Вобщем тут при групповом удалении возникают коллизии. Подгруппа уже перенесена, а со старого места пытаюсь удалить. Хотя смущает, что количество оставшихся элементов в "свернувшемся" дереве правильное, и связанность дерево не потеряло...

С помощью райсов выяснил, что как ни указывай порядок удаляемых элементов в запросе DELETE FROM xxx WHERE id IN (5, 1, 3, 6...), удалятся они будут всё равно по возрастанию - (1, 3, 5, 6...). Может на это влияет то, что id - первичный ключ.

Вероятно всё бы заработало как надо, если бы можно было указать порядок удаления от Большего к Меньшему. Тогда дерево должно сворачиваться корректно.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885517
Ы
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Ы
Гость
EvgIq,

Так заверните DELETE в цикл, и будет вам удаление в правильном порядке.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885559
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Ы,

Если Вы про то, что удалять по одной строке с клиентов, все верно, так работать будет, я написал это в самом первом сообщении.
Но хотелось бы, конечно, решение на сервере.
Если не найду способа, при групповом удалении, выбирать данные в обратном порядке, то переверну само дерево :)
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885647
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
EvgIqПри удалении элемента, если он не последний у родителя, в последовательности детей получается "дырка". Ее наличие недопустимо.
В морг. Откажитесь от этого бессмысленного требования, именно оно делает Вам проблемы.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885739
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Dimitry Sibiryakov,
Сама структура дерева такая. Без "дырок" никак не получится.
Отказываться я не буду уже, так как всё, кроме группового удаления id (и, возможно группового переноса, но это требуется очень редко, то есть можно тупо запретить переносить по нескольку узлов за раз), работает прекрасно.
Решить же баг с групповым удалением можно:
1. Как то заставить Postgresql при групповом удалении брать элементы в обратном порядке
если нельзя, то:
2. PRIMARY KEY DESC (в обратной сортировке)
если нельзя, то
3. Отказаться от PRIMARY KEY и сделать уникальный индекс DESC (но это ркшкние мне не нравится, хотя может кого-то и устроит, в принципе все так же будет работать)

И, наконец, что сработает 100%% - "Перевернуть" дерево. То есть id вычислять в убывающем порядке. Тогда сворачивание дерева будет происходить корректно. И это решение самое простое и интересное, ведь id это всего лишь цифры :)

Зато в дереве, с такой структурой, я могу: за один запрос выбирать всех Родителей, за один запрос выбирать всех детей (вообще всех или просто на определенном уровне/уровнях). Получился этакий "Materialized Path" но в типе int со всеми вытекающими :)
Да, есть ограничения по размерности дерева. Но, допустим, мне 8х255 хватит :)
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885750
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
EvgIq,
Да, хочу уточнить, что решения 2 и 3 призваны побороть проблему группового удаления id в прямой сортировке. Но не факт что помогут. Это просто мое предположение, что групповое удаление происходит в порядке индекса поля.

Можно так же, при удалении/перемещении, если получается "дырка", то не уменьшать количество детей у Родителя. Тогда вычисления нового id на этом уровне будет происходить корректно, структура дерева не порушится. Но... будет попусту расходоваться id - они же "пропадут". А в условиях ограничения размерности дерева это ценный ресурс. Конечно, можно предусмотреть возможность "возвращать" неиспользуемые id для новых элементов. Тут подумать надо, это тоже интересно :)
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885849
Dimitry SibiryakovEvgIqПри удалении элемента, если он не последний у родителя, в последовательности детей получается "дырка". Ее наличие недопустимо.
В морг. Откажитесь от этого бессмысленного требования, именно оно делает Вам проблемы.

да он по ходу упоротый
ранняя весна, обострение
т.ч. не мешайте, а помогите советом

2ТС
г-н евгений, женя (надеюсь, не палыч), зря вы в версионнике записи кучками двигаете, особо в таком, как postgresl, у вас всё от этого опухнет и отвалится. и индесы, индексы не в одни ворота не пройдут.

вам бы блокировочник типа масдай-скл, там есть триггера на стейтменты, где все ваши удаленные записи можно сортировать в deleted.* . а главное -- ничто не пухнет при вашем подходе. т.е. ваш подход -- вот именно для блокировочников подойдёт -- им уже хуже всё равно не будет -- куда уж хуже, когда ты блокировочник. тут и женя, надеюсь не павлович, и тот ничего не испортит.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885869
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
сизиф и мартышки,

Я ничего и никому не порчу. Я не павлович. Нечего сказать по существу - проходите мимо.
Я смотрю тут много подобных умников-философоф трется. А казалось бы технический форум.
И да, пока на дворе еще только поздняя зима, г-н сифиз и мартышки. Посмотрите в календарь, в окно, или куда там вас выпустят посмотреть. Опыт по обострениям видать у вас большой.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885903
EvgIq,

ну право слово, не стоит дуться
ну вот, случилось вам идея ощастливить мир -- щасливьте
я только за; и даже вот помогаю
а что не понимаете, что вам помогают именно технически -- тоже не беда

ещё раз:
если вам знаком MSSQL -- там в триггере на удаление (на весь стейтмент, а не на каждый рядок) -- вам проще будет высчитать "коллективные эффекты" (раз они вам мешают жить)
а в качестве бонуса -- вы не породите массу dead rows [в версионнике], которые постгресу, в норме, ни к чему, и если есть способы обсчистывать дерево без таких подарков от жениев не павловичей [а они есть], то люди будут пользоваться ими, а не вашим щасьем.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885912
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
сизиф и мартышки,

Глобальные идеи для всемирного счастия я оставляю вам. И дабы не провоцировать очередной кризис у поциентов, уже впавших в раннее весеннее обострение, сообщаю - я буду складывать "дырки" в отдельную таблицу, и брать их оттуда когда нормальные id на уровне кончатся.
Свои же, неактуальные сейчас для меня, бестолковые, советы по выбору бд, оставьте, для коллег по палате. Для них же приберегите свои размышления о групповых операциях над ключем, особенно когда попытаетесь помочь реализовать, для всемирного опять же счастия, такие алгоритмы как "Nested Sets" и "Materialized Paths".
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885956
EvgIq,

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

кодинг, это не гениальные идеи, а банальные рефлексы по их реализации
и, в т.ч. реализации даже далеко не жениальных идей

судя же по вашему коду -- вы нуб в бд вообще и в postgresql -- в частности
поэтому мсскл вам ничем не хуже.

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

Но к делу:
егоров правильно вам написал по мелочи:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
CREATE OR REPLACE FUNCTION const_ch()
  RETURNS integer AS
$BODY$	-- устанавливаем допустимое количество Детей у элемента в дереве
	SELECT 3;
$BODY$
LANGUAGE sql IMMUTABLE
COST 10;

CREATE OR REPLACE FUNCTION const_lv()
  RETURNS integer AS
$BODY$
	-- устанавливаем допустимое количество Уровней в дереве
	SELECT 4;
$BODY$
LANGUAGE sql IMMUTABLE
COST 10;



-- это конечно всё "не существенно", для изобретателей велосипедов, но когда база из за таких кодеров встаёт колом -- приходится доставать полено -- и заниматься промежушной педагогикой

Далее тот же егоров парвильно вам написал про вставку первого.
А то, что вы вместо правки своего жениального овнокода вдарились в рассуждения про джанго выявило в вас (не впервый раз) прожектёра.
правится это примерно так (не думая):

Код: 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.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
CREATE OR REPLACE FUNCTION new_id_board()
  RETURNS trigger AS
$BODY$DECLARE
	lv		integer;	-- количество уровней в дереве
	ch		integer;	-- допустимое количество детей
	id_new		bigint;		-- вычесляемый id нового элемента
	id_p		bigint;		-- id родителя 
	lv_p		integer;	-- уровень родителя
	count_childs_p	integer;	-- количество детей родителя
BEGIN
	-- для создания нового элемента достаточно заполнить  у него поле parent
	id_p := NEW.parent_id;

	ch := const_ch();
	lv := const_lv();

	-- найдем родителя
	SELECT lvl, count_childs INTO lv_p, count_childs_p FROM board_group WHERE board_group.id=id_p;	
	
	IF NOT FOUND THEN
		lv_p:=0; count_childs_p:=0;
		NEW.lvl := 0;
	ELSE
		-- Проверка 1: может ли родитель еше иметь детей
		IF count_childs_p >= ch THEN
			RAISE EXCEPTION 'Родителю c id=% более нельзя иметь детей (их количество уже %)', id_p, ch;
		END IF;

		-- Проверка 2: может ли родитель иметь детей
		IF lv_p = lv THEN
			RAISE EXCEPTION 'Элементу с id=% нельзя иметь детей (его уровень % максимально возможный)', id_p, lv;
		END IF;

		NEW.id := ((power((ch+1),(lv-lv_p-1)))*(count_childs_p+1))::bigint+id_p;
		NEW.lvl := lv_p + 1;		
	END IF;
	NEW.count_childs := 0;
	

	NEW.nm := NEW.id; -- ЭТО ТОЛЬКО ДЛЯ ТЕСТОВ, затем убрать
	RAISE NOTICE '	%', NEW;
	
	UPDATE board_group SET count_childs = count_childs_p+1 WHERE board_group.id = id_p;
	
	return NEW;
END;$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION new_id_board()
  OWNER TO postgres;



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


бесконечно лень вчитываться в вашу пену слов и кода, т.ч. предположу, что вас, возможно , спас бы перенос AFTER DELETE логики в заключение в BEFORE DELETE [могу врать, но есть подозрение, что это так]

т.е.
Код: 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.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
-- заглушка ----------
CREATE OR REPLACE FUNCTION del_id_board_after()
  RETURNS trigger AS
$BODY$DECLARE

	old_p_id	bigint;
	old_p_lv	integer;
	old_p_ch	integer;
	i		integer;

BEGIN
/*	
	-- возьмем Родителя удаленного Элемента
	SELECT id, lvl, count_childs 
		INTO old_p_id, old_p_lv, old_p_ch 
		FROM board_group 
	WHERE id=OLD.parent_id;	

	

	IF (old_p_ch > 0) THEN
		--- RAISE EXCEPTION 'old_p_id=%, old_p_lv=%, old_p_ch=%, OLD.id=%',old_p_id, old_p_lv, old_p_ch, OLD.id;
		i := fill_emp(old_p_id, old_p_lv, old_p_ch+1, OLD.id);
	END IF;
*/	
	RETURN NULL;
END;$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION del_id_board_after()
  OWNER TO postgres;
--------------------------------------------------------
CREATE OR REPLACE FUNCTION del_id_board_before()
  RETURNS trigger AS
$BODY$DECLARE

	old_p_id	bigint;
	old_p_lv	integer;
	old_p_ch	integer;
	i		integer;

BEGIN
	IF OLD.count_childs > 0 THEN
		RAISE EXCEPTION 'У данного элемента имеются дети в количестве % шт. Удалите сначала их.', OLD.count_childs;
	END IF;

	-- возьмем Родителя удаленного Элемента
	SELECT id, lvl, count_childs 
		INTO old_p_id, old_p_lv, old_p_ch 
		FROM board_group 
	WHERE id=OLD.parent_id;	

	-- уменьшим количество детей у родителя
	UPDATE board_group SET count_childs = count_childs-1 WHERE id = OLD.parent_id;
	-----------------	-----------------
		IF (old_p_ch > 0) THEN
			--- RAISE EXCEPTION 'old_p_id=%, old_p_lv=%, old_p_ch=%, OLD.id=%',old_p_id, old_p_lv, old_p_ch, OLD.id;
			i := fill_emp(old_p_id, old_p_lv, old_p_ch+1, OLD.id);
		END IF;
	-----------------	-----------------	

	
	RETURN OLD;
END;$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION del_id_board_before()
  OWNER TO postgres;



-- если я вру -- поправьте, желательно на пальцах.
(там реально думать надо [о видимостях и т.п.], а это затратно, а повода нет)


теперь -- почему нет повода:
id дерева -- это, как правило, ссылка на сущность, на которую ссылаются другие объекты (а не только оно само -- уробороссом) . каскадный апдейт всей базы, это то, за что не увольняют, а закапывают на месте. даже в блокировочнике. в случае версионника -- это еще и дублирование занятого дискового, и последующая глобальная сборка мусора [в postgresql -- воркерами автовакуума]

повторяю, в этом случае вам не помогать нужно, а вязать вас санитарами, пхать в смирительную, и на процедурки

так что, чтобы повысить юзабельность вашего макетика (который у вас рассыпается) сделайте отдельно -- board_id суррогат, неизменый. -- На него будете ссылаться снаружи, без всех этих каскадов. Вместо id и parent_id я бы ввел key и parent_key -- для читаемости, но это вопрос предпочтений и наличного кода.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885982
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
сизиф и мартышки,

То, что вы сломя голову, не читая и не вникая бросились улучшать мир, давать советы, ставит под сомнение их ценность, а ваш тон и манеры - вашу адекватность.
Повторю для дурачков - я извинился за свой код (5-е сообщение в ветке). И вообще тема вопроса была другая. Но человек попросил, я запилил. Конечно все еще будет правиться/рефакториться/теститься и проч.
Про RETURN'ы - будет вызов констант из таблицы, так как в БД будет не одно дерево. Так что ваш совет очередной пук в лужу.
Про AFTER DELETE и BEFORE DELETE - так же писал уже, второй раз не вижу смысла так как см. первое предложение в этой мессаге.
и т.д. и т.п.
...вобщем для вас всё печально...
Но вы ведь здесь заняты тренировкой своего хилого больного остроумия, вместо обычного решения технических задач, что, несомненно, более пошло бы вам на пользу.
"Собака лает, караван идет", так вот, вы в этой ветке не караван. Не утруждайтесь далее, отдохните.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38885997
EvgIqсизиф и мартышки,

То, что вы сломя голову, не читая и не вникая бросились улучшать мир, давать советы, ставит под сомнение их ценность, а ваш тон и манеры - вашу адекватность.
Повторю для дурачков - я извинился за свой код (5-е сообщение в ветке). И вообще тема вопроса была другая. Но человек попросил, я запилил. Конечно все еще будет правиться/рефакториться/теститься и проч.
Про RETURN'ы - будет вызов констант из таблицы, так как в БД будет не одно дерево. Так что ваш совет очередной пук в лужу.
Про AFTER DELETE и BEFORE DELETE - так же писал уже, второй раз не вижу смысла так как см. первое предложение в этой мессаге.
<>чотаржу.

select-ы вполне выполняются из таблиц
[это на предмет газификации луж return-ами]
смысл же изложен егоровым [выбор процедурного языка, прозрачного планировщику, а не синтаксиса]
повторяться не буду -- rtfm , и воздастся.

первое ваше сообщение (единственное о вашем предпочтении в выборе before -- after логики) я тоже бегло просмотрел -- т.ч. не обнаружил за вами внятного понимания, что вы делаете before, а что -- after. настаивать на прояснением этого не буду -- очевидно это непосильный для вас труд.


если караван до сих пор так и ходит -- только под себя , -- то это проблема корована.
и да, простите за издевательство -- старая зобава -- грабить корованы
как вижу -- "корован" -- так и тянет
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38886002
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
сизиф и мартышки,
сабака грабит караваны?, что-то новенькое :) идите, работайте, повышайте тех уровень и недостающее воспитание, а то уволят ведь несмотря на высокие мотивы и каскадные операции.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38886014
EvgIqсизиф и мартышки,
сабака грабит караваны?, что-то новенькое :) идите, работайте, повышайте тех уровень и недостающее воспитание, а то уволят ведь несмотря на высокие мотивы и каскадные операции.
деточка, заплесневелые мемы можно было бы и узнавать без расшифровки

ну или если в гугле не зобанеле -- то проявить реакцыю (быстрость разумом невтонов, ага)

а то же -- ни реакции, ни способности к педантичному труду
-- одни жениальные потуги, не подтверждённые ничем, кроме готовности пусто, кхм, лаяться

иди, куй, мальчик. вычёркиваю(тм).
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38886019
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
сизиф и мартышки,
что за мем? неужели мартышки грабят караваны? гуголь так говорит? врет! :)
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38886266
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
EvgIq,

Я попытался еще раз:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
ALTER TABLE board_group DISABLE TRIGGER before_new_id;
insert into board_group(id,lvl,count_childs,nm,parent_id) values (-128,0,0,0,-128);
ALTER TABLE board_group ENABLE TRIGGER before_new_id;

SELECT * FROM board_group;
INSERT INTO board_group(parent_id) values (-128);

ERROR:  null value in column "id" violates not-null constraint
DETAIL:  Failing row contains (null, null, null, null, -128).


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

Также я в третий раз прошу — предоставьте набор SQL-команд, которые приведут систему к виду, показанному вами на скриншотах.
На данный момент я сомневаюсь, что это возможно без грубой доделки молотком и зубилом.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38886302
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
vyegorov,
команда
Код: plsql
1.
INSERT INTO board_group values (0,0,0,0,-128);


Создаст элемент (-64,1,0,'-64',128)
затем
Код: plsql
1.
INSERT INTO board_group values (0,0,0,0,-128);


Создаст элемент (0,1,0,'0',128)
затем
Код: plsql
1.
INSERT INTO board_group values (0,0,0,0,-128);


Создаст элемент (64,1,0,'64',128)
ну и далее по аналогии
Код: plsql
1.
2.
3.
4.
5.
6.
INSERT INTO board_group values (0,0,0,0,64);
INSERT INTO board_group values (0,0,0,0,64);
INSERT INTO board_group values (0,0,0,0,64);
INSERT INTO board_group values (0,0,0,0,112);
INSERT INTO board_group values (0,0,0,0,112);
INSERT INTO board_group values (0,0,0,0,112);



Получится дерево как на картинке.
Я не зря приводил скрины из админки Django, чтобы было понятно что проект рабочий.
Вчера я переделал алгоритм - теперь, если получается "дырка", "складываю" ее родителя и номер в отдельную таблицу, и "забираю" ее оттуда при создании нового ребенка или перемещение к Родителю. Первоначальный вопрос, который в теме ветки, стал неактуален.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38886315
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
В строках "создаст элемент" минус у родителя забыл указать... правильно так:
Создаст элемент (-64,1,0,'-64',-128)....Создаст элемент (0,1,0,'0',-128)....Создаст элемент (64,1,0,'64',-128)...
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38886768
vyegorov,

не мучайте дитё
оно в первый раз дорвалось до "одминки джанго"

и да, человечество придумало позиционную запись для экономии собственной памяти,
а оно возвращается от позиционной записи обратно к бесконечно длинным битовым словам
вместо записи слов длины M в алфавите N используя слова алфавита [01] длиной N^M
в общем -- алгоритмически там всё безобразно.
т.е. буквально всё, а не только подмеченное сибиряковым
т.ч. пусть себе играет в куличики, пока оно не лепит их у вас, в вашей команде
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38886793
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
сизиф и мартышки,
О, грабитель караванов, компы раздали? :) Вы же вроде как разобиделись и попрощались? Правильно, не обижайтесь, заходите почаще, я завсегда рад подбодрить больного человека.
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38888366
Мутуз
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
EvgIq,

первый вопрос - да, верно
Код: 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.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
create table tmp_test_del
(
  id integer,
  val integer
);



truncate table tmp_test_del;
insert into tmp_test_del
select i, i % 2  from generate_series(1, 10) as i;


--select * from tmp_test_del



CREATE OR REPLACE FUNCTION trf_tmp_test_del_bd()
  RETURNS trigger AS
$BODY$
BEGIN 
  raise notice 'BEFORE DELETE (id=%, rows=%)', OLD.id, (SELECT count(*) from tmp_test_del);
  RETURN OLD;  
END;
$BODY$
  LANGUAGE plpgsql VOLATILE SECURITY DEFINER  COST 100;
GRANT EXECUTE ON FUNCTION trf_tmp_test_del_bd() TO public;

CREATE TRIGGER tr_tmp_test_del_bd
  BEFORE DELETE
  ON tmp_test_del
  FOR EACH ROW
  EXECUTE PROCEDURE trf_tmp_test_del_bd();


CREATE OR REPLACE FUNCTION trf_tmp_test_del_ad()
  RETURNS trigger AS
$BODY$
BEGIN 
  raise notice 'AFTER DELETE (id=%, rows=%)', OLD.id, (SELECT count(*) from tmp_test_del);
  RETURN OLD;  
END;
$BODY$
  LANGUAGE plpgsql VOLATILE SECURITY DEFINER  COST 100;
GRANT EXECUTE ON FUNCTION trf_tmp_test_del_ad() TO public;

CREATE TRIGGER tr_tmp_test_del_ad
  AFTER DELETE
  ON tmp_test_del
  FOR EACH ROW
  EXECUTE PROCEDURE trf_tmp_test_del_ad();


--тест
delete from  tmp_test_del where val = 0


вывод:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
NOTICE:  BEFORE DELETE (id=2, rows=10)
NOTICE:  BEFORE DELETE (id=4, rows=9)
NOTICE:  BEFORE DELETE (id=6, rows=8)
NOTICE:  BEFORE DELETE (id=8, rows=7)
NOTICE:  BEFORE DELETE (id=10, rows=6)
NOTICE:  AFTER DELETE (id=2, rows=5)
NOTICE:  AFTER DELETE (id=4, rows=5)
NOTICE:  AFTER DELETE (id=6, rows=5)
NOTICE:  AFTER DELETE (id=8, rows=5)
NOTICE:  AFTER DELETE (id=10, rows=5)

Второй вопрос-
ну напишите свою функцию на удаление:
Код: sql
1.
2.
3.
4.
5.
6.
FUNCTION del(p_id integer)
$$
  -- do something
  delete from table where id = p_id;
  -- do something
$$



Третий вопрос-
www.pgadmin.org/docs/1.8/debugger.html
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38888372
Мутуз
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
подчищаем за собой

Код: sql
1.
2.
3.
4.
5.
DROP TRIGGER tr_tmp_test_del_ad ON tmp_test_del;
DROP TRIGGER tr_tmp_test_del_bd ON tmp_test_del;
DROP FUNCTION trf_tmp_test_del_bd();
DROP FUNCTION trf_tmp_test_del_ad();
DROP TABLE tmp_test_del;
...
Рейтинг: 0 / 0
Триггер DELETE в случае группового удаления данных
    #38888506
EvgIq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Мутуз,
Спасибо, все понял.
...
Рейтинг: 0 / 0
42 сообщений из 42, показаны все 2 страниц
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Триггер DELETE в случае группового удаления данных
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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