· 6 years ago · May 12, 2019, 05:04 PM
1DROP TABLE IF EXISTS Personnel CASCADE;
2CREATE TABLE Personnel
3( emp_nbr INTEGER -- код работника
4 DEFAULT 0 NOT NULL PRIMARY KEY,
5 emp_name VARCHAR( 10 ) -- Ð¸Ð¼Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ð½Ð¸ÐºÐ°
6 DEFAULT '{ {vacant} }' NOT NULL,
7 address VARCHAR( 35 ) NOT NULL, -- Ð°Ð´Ñ€ÐµÑ Ñ€Ð°Ð±Ð¾Ñ‚Ð½Ð¸ÐºÐ°
8 birth_date DATE NOT NULL -- день Ñ€Ð¾Ð¶Ð´ÐµÐ½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ð½Ð¸ÐºÐ°
9);
1075
11-- Произведем первоначальное заполнение таблицы.
12INSERT INTO Personnel VALUES
13( 0, 'ваканÑиÑ', '', '2014-05-19' ),
14( 1, 'Иван', 'ул. Любителей Ñзыка C', '1962-12-01' ),
15( 2, 'Петр', 'ул. UNIX гуру', '1965-10-21' ),
16( 3, 'Ðнтон', 'ул. ÐÑÑемблернаÑ', '1964-04-17' ),
17( 4, 'Захар', 'ул. им. СУБД PostgreSQL', '1963-09-27' ),
18( 5, 'Ирина', 'проÑп. ПрограммиÑтов', '1968-05-12' ),
19( 6, 'Ðнна', 'пер. Перловый', '1969-03-20' ),
20( 7, 'Ðндрей', 'пл. Баз данных', '1945-11-07' ),
21( 8, 'Ðиколай', 'наб. ОС Linux', '1944-12-01' );
22-- -----------------------------------------------------------
23-- Таблица "ÐžÑ€Ð³Ð°Ð½Ð¸Ð·Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð°Ñ Ñтруктура"
24-- -----------------------------------------------------------
25DROP TABLE IF EXISTS Org_chart CASCADE;
26CREATE TABLE Org_chart
27( job_title VARCHAR( 30 ) -- наименование должноÑти
28 NOT NULL PRIMARY KEY,
29 emp_nbr INTEGER -- код работника
30 DEFAULT 0 NOT NULL -- 0 означает вакантную должноÑть
31 REFERENCES Personnel( emp_nbr ) -- внешний ключ
32 ON DELETE SET DEFAULT
33 ON UPDATE CASCADE
34 -- Ðто ограничение будет отключатьÑÑ Ð¿Ñ€Ð¸ выполнении
35 -- одной из хранимых процедур, поÑтому DEFERRABLE.
36 DEFERRABLE,
37 boss_emp_nbr INTEGER -- код начальника данного работника
38 DEFAULT 0
39 -- ПоÑкольку null означает корень иерархии, то ограничение
40 -- NOT NULL вводить не будем.
41 REFERENCES Personnel( emp_nbr ) -- внешний ключ
42 ON DELETE SET DEFAULT
43 ON UPDATE CASCADE
44 -- Ðто ограничение будет отключатьÑÑ Ð¿Ñ€Ð¸ выполнении
45 -- одной из хранимых процедур, поÑтому DEFERRABLE.
46 DEFERRABLE,
47 salary DECIMAL( 12, 4 ) -- зарплата работника, занимающего
48 -- Ñту должноÑть
49 NOT NULL CHECK ( salary >= 0.00 ),
50 -- Работник не может быть Ñам Ñебе начальником,
51 -- Ñ‚. е. код работника не должен Ñовпадать Ñ ÐºÐ¾Ð´Ð¾Ð¼
52 -- начальника);
53 -- еÑли должноÑть не занÑта, то код работника и код
54 -- начальника равны 0.
55 CHECK ( ( boss_emp_nbr <> emp_nbr ) OR
56 ( boss_emp_nbr = 0 AND emp_nbr = 0 )
57 ),
58 -- Без Ñтого внешнего ключа возможна ÑитуациÑ, когда
5976
60 -- удалÑетÑÑ Ð·Ð°Ð¿Ð¸ÑÑŒ, значение Ð¿Ð¾Ð»Ñ emp_nbr которой
61 -- иÑпользуетÑÑ Ð² качеÑтве Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»Ñ boss_emp_nbr
62 -- в других запиÑÑÑ…. Другими Ñловами, работник, которого
63 -- нет в орг. Ñтруктуре, ÑвлÑетÑÑ Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð¸ÐºÐ¾Ð¼ других
64 -- работников, приÑутÑтвующих в орг. Ñтруктуре).
65 FOREIGN KEY ( boss_emp_nbr )
66 REFERENCES Org_chart ( emp_nbr )
67 ON DELETE SET DEFAULT
68 ON UPDATE CASCADE
69 DEFERRABLE,
70 -- ПришлоÑÑŒ добавить и Ñто ограничение, иначе
71 -- внешний ключ FOREIGN KEY ( boss_emp_nbr ) Ñоздать
72 -- невозможно.
73 UNIQUE ( emp_nbr )
74);
75-- Произведем первоначальное заполнение таблицы.
76-- Обратите внимание, что у главы компании нет начальника,
77-- поÑтому значение NULL.
78INSERT INTO Org_chart VALUES
79( 'Президент', 1, NULL, 1000.00 ),
80( 'Вице-президент 1', 2, 1, 900.00 ),
81( 'Вице-президент 2', 3, 1, 800.00 ),
82( 'Ðрхитектор', 4, 3, 700.00 ),
83( 'Ведущий программиÑÑ‚', 5, 3, 600.00 ),
84( 'ПрограммиÑÑ‚ C', 6, 3, 500.00 ),
85( 'ПрограммиÑÑ‚ Perl', 7, 5, 450.00 ),
86( 'Оператор', 8, 5, 400.00 );
87
88CREATE OR REPLACE FUNCTION check_org_chart() RETURNS trigger
89AS
90$$
91BEGIN
92 -- Ðта Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ не позволÑет Ñделать две и более
93-- запиÑи Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ‚Ð¾Ñ€Ð¾Ð¼ боÑÑа, равным null, но против
94 -- зацикливаний она беÑÑильна.
95 -- Ðто уÑловие не позволÑет удалить из таблицы поÑледнюю
96 -- запиÑÑŒ (главный боÑÑ Ð´Ð¾Ð»Ð¶ÐµÐ½ быть вÑегда -- Ñто так и
97 -- нужно?).
98 -- ПРИМЕЧÐÐИЕ. УÑловие COUNT( boss_emp_nbr ) подÑчитывает
99 -- только те запиÑи, у которых значение Ð¿Ð¾Ð»Ñ boss_emp_nbr
10077
101 -- не равно NULL. УÑловие COUNT( * ) подÑчитывает вÑе запиÑи.
102 IF ( SELECT COUNT( * ) FROM Org_chart ) - 1 <>
103 ( SELECT COUNT( boss_emp_nbr ) FROM Org_chart )
104THEN
105 -- Прервем выполнение функции и выведем Ñообщение.
106 RAISE EXCEPTION 'Bad orgchart structure';
107 ELSE
108 -- Ð’ завиÑимоÑти от вида операции (вÑÑ‚Ñ€Ð¾ÐµÐ½Ð½Ð°Ñ Ð¿ÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ
109 -- TG_OP) Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†ÐµÐ¹ возвратим либо Ñтарую (OLD), либо
110 -- новую (NEW) верÑии Ñтроки таблицы.
111 IF ( TG_OP = 'DELETE' ) THEN
112 RETURN OLD;
113 ELSIF ( TG_OP = 'UPDATE' ) THEN
114 RETURN NEW;
115 ELSIF ( TG_OP = 'INSERT' ) THEN
116 RETURN NEW;
117 END IF;
118 RETURN NULL;
119 END IF;
120END;
121$$
122LANGUAGE plpgsql;
123
124DROP TRIGGER IF EXISTS check_org_chart ON Org_chart;
125
126CREATE TRIGGER check_org_chart
127AFTER INSERT OR UPDATE OR DELETE ON Org_chart
128 FOR EACH ROW EXECUTE PROCEDURE check_org_chart();
129
130-- -----------------------------------------------------------
131-- Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐºÐ¸ Ñтруктуры дерева на предмет
132-- отÑутÑÑ‚Ð²Ð¸Ñ Ñ†Ð¸ÐºÐ»Ð¾Ð².
133-- -----------------------------------------------------------
134CREATE OR REPLACE FUNCTION tree_test() RETURNS CHAR( 6 ) AS
135$$
136BEGIN
137 -- Создадим временную таблицу на оÑнове иерархии
138 -- должноÑтей.
139 CREATE TEMP TABLE Tree ON COMMIT DROP AS
140 SELECT emp_nbr, boss_emp_nbr FROM Org_chart;
141 -- УдалÑем лиÑÑ‚ÑŒÑ Ð´ÐµÑ€ÐµÐ²Ð°. УÑловие означает Ñледующее:
142 -- SELECT COUNT( * ) -- общее фактичеÑкое чиÑло узлов
143 -- дерева, Ñ‚. к. ÐºÐ°Ð¶Ð´Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ в таблице ÑоответÑтвует
144 -- одному узлу;
145 -- SELECT COUNT( * ) - 1 -- теоретичеÑкое чиÑло ребер
146 -- дерева, которое должно быть на 1 меньше фактичеÑкого
147 -- чиÑла узлов;
148 -- SELECT COUNT( boss_emp_nbr ) -- чиÑло запиÑей,
149 -- Ð´Ð»Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… значение Ð¿Ð¾Ð»Ñ boss_emp_nbr не равно NULL
150 -- (Ñм. опиÑание функции COUNT()), должно быть ровно на 1
151 -- меньше, чем COUNT( * ), т. к. только у одного работника
152 -- значение Ð¿Ð¾Ð»Ñ boss_emp_nbr может быть равно NULL --
153 -- Ñто глава организации.
154 WHILE ( SELECT COUNT( * ) FROM Tree ) - 1
155 = ( SELECT COUNT( boss_emp_nbr ) FROM Tree )
156 LOOP
157 -- УдалÑем запиÑи (Ñтроки) о работниках, которые
158 -- не ÑвлÑÑŽÑ‚ÑÑ Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð¸ÐºÐ°Ð¼Ð¸ ни Ð´Ð»Ñ Ð¾Ð´Ð½Ð¾Ð³Ð¾ из других
159 -- работников (в подзапроÑе выбираютÑÑ Ð²Ñе начальники).
160 DELETE
161 FROM Tree
162 WHERE Tree.emp_nbr NOT IN ( SELECT T2.boss_emp_nbr
163 FROM Tree AS T2
164WHERE T2.boss_emp_nbr
165 IS NOT NULL );
166 END LOOP;
167 -- Ðта проверка должна выполнÑтьÑÑ ÑƒÐ¶Ðµ поÑле завершениÑ
168 -- ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ð¸Ñей из таблицы Tree. ЕÑли запиÑей
169 -- не оÑталоÑÑŒ, значит, дерево ÑвÑзанное.
170 IF NOT EXISTS ( SELECT * FROM Tree )
171 THEN
172 RETURN ( 'Tree' );
173 -- ЕÑли хоть одна запиÑÑŒ оÑталаÑÑŒ, значит,
174 -- в дереве еÑть циклы.
175 ELSE
176 RETURN ( 'Cycles' );
177 END IF;
178END;
179$$
180LANGUAGE plpgsql;
181
182-- -----------------------------------------------------------
183-- Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ð° дерева Ñнизу вверх, Ð½Ð°Ñ‡Ð¸Ð½Ð°Ñ Ñ ÐºÐ¾Ð½ÐºÑ€ÐµÑ‚Ð½Ð¾Ð³Ð¾
184-- узла.
185-- Ð’ÐРИÐÐТ 1.
186-- -----------------------------------------------------------
187CREATE OR REPLACE FUNCTION up_tree_traversal(
188 IN current_emp_nbr INTEGER )
189 RETURNS TABLE( emp_nbr INTEGER, boss_emp_nbr INTEGER ) AS
190$$
191BEGIN
192 -- Выбираем запиÑÑŒ Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ³Ð¾ работника. Ðа первой
193 -- итерации Ñто будет работник, Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð³Ð¾ начинаетÑÑ
194 -- обход дерева вверх.
195 WHILE EXISTS ( SELECT *
196 FROM Org_chart AS O
197WHERE O.emp_nbr = current_emp_nbr )
198 LOOP
199 -- ЕÑли нужно, то выполним какое-либо дейÑтвие
200 -- Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ³Ð¾ узла дерева (Ñ‚. е. Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ³Ð¾
201 -- работника). Ð”Ð»Ñ Ñтого нужно процедуру SomeProc
202 -- заменить на какую-то полезную процедуру.
203 -- CALL SomeProc (current_emp_nbr);
204 -- Добавим очередную пару (работник; начальник)
205 -- к формируемому множеÑтву таких пар.
206 -- ПРИМЕЧÐÐИЕ. Ðтот оператор RETURN не завершает
207 -- выполнение процедуры, а лишь ДОБÐВЛЯЕТ очередную
208 -- запиÑÑŒ (Ñтроку) к результирующей таблице.
209 RETURN QUERY SELECT O.emp_nbr, O.boss_emp_nbr
210 FROM Org_chart AS O
211WHERE O.emp_nbr = current_emp_nbr;
212 -- Идем вверх по дереву к корню. Теперь текущим
213 -- работником ÑтановитÑÑ Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð¸Ðº только что
214 -- обработанного работника, тем Ñамым мы перемещаемÑÑ
215 -- на один уровень вверх по дереву. Когда текущим
216 -- работником Ñтанет главный начальник, у которого
217 -- уже нет начальника, тогда результатом Ñтого запроÑа
218 -- будет current_emp_nbr = NULL, в результате чего
219 -- уÑловие цикла будет не выполнено, и цикл завершитÑÑ.
220 current_emp_nbr = ( SELECT O.boss_emp_nbr
221 FROM Org_chart AS O
222WHERE O.emp_nbr = current_emp_nbr );
223 END LOOP;
224END;
225$$
226LANGUAGE plpgsql;
227
228-- -----------------------------------------------------------
229-- Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ð´Ð»Ñ Ð¾Ð±Ñ…Ð¾Ð´Ð° дерева Ñнизу вверх, Ð½Ð°Ñ‡Ð¸Ð½Ð°Ñ Ñ ÐºÐ¾Ð½ÐºÑ€ÐµÑ‚Ð½Ð¾Ð³Ð¾
230-- узла.
231-- Ð’ÐРИÐÐТ 2.
232-- -----------------------------------------------------------
233CREATE OR REPLACE FUNCTION up_tree_traversal2(
234 IN current_emp_nbr INTEGER )
235 RETURNS SETOF RECORD AS
236$$
237DECLARE
238 -- Ð’ ÑоответÑтвии Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ð¼ типом результирующего значениÑ,
239 -- возвращаемого функцией, объÑвим переменную типа RECORD.
240 rec RECORD;
241BEGIN
242 -- Выбираем запиÑÑŒ Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ³Ð¾ работника. Ðа первой
243 -- итерации Ñто будет работник, Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð³Ð¾ начинаетÑÑ
244 -- обход дерева вверх.
245 WHILE EXISTS ( SELECT *
246 FROM Org_chart AS O
247WHERE O.emp_nbr = current_emp_nbr )
248 LOOP
249 -- ЕÑли нужно, то выполним какое-либо дейÑтвие
250 -- Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ³Ð¾ узла дерева (Ñ‚. е. Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ³Ð¾
251 -- работника). Ð”Ð»Ñ Ñтого нужно процедуру SomeProc
252 -- заменить на какую-то полезную процедуру.
253 -- CALL SomeProc (current_emp_nbr);
254 -- Добавим очередную пару (работник; начальник)
255 -- к формируемому множеÑтву таких пар.
256 SELECT O.emp_nbr, O.boss_emp_nbr
257 INTO rec
258 FROM Org_chart AS O
259 WHERE O.emp_nbr = current_emp_nbr;
260 -- ПРИМЕЧÐÐИЕ. Ðтот оператор RETURN не завершает
261 -- выполнение процедуры, а лишь ДОБÐВЛЯЕТ очередную
262 -- запиÑÑŒ к результирующему множеÑтву.
263 RETURN NEXT rec;
264
265 -- Идем вверх по дереву к корню. Теперь текущим
266 -- работником ÑтановитÑÑ Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð¸Ðº только что
267 -- обработанного работника, тем Ñамым мы перемещаемÑÑ
268 -- на один уровень вверх по дереву. Когда текущим
269 -- работником Ñтанет главный начальник, у которого
270 -- уже нет начальника, тогда результатом Ñтого запроÑа
271 -- будет current_emp_nbr = NULL, в результате чего
272 -- уÑловие цикла будет не выполнено, и цикл завершитÑÑ.
273 current_emp_nbr = ( SELECT O.boss_emp_nbr
274 FROM Org_chart AS O
275WHERE O.emp_nbr = current_emp_nbr );
276 END LOOP;
277 RETURN;
278END;
279$$
280LANGUAGE plpgsql;
281
282-- -----------------------------------------------------------
283-- Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€ÐµÐ²Ð°.
284-- -----------------------------------------------------------
285CREATE OR REPLACE FUNCTION delete_subtree(
286 IN dead_guy INTEGER ) RETURNS VOID AS
287$$
288-- Параметр dead_guy -- код работника, возглавлÑющего поддерево.
289BEGIN
290 -- Создадим врЕменную поÑледовательноÑть. Она нужна
291 -- Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, чтобы формировать отрицательные значениÑ
292 -- Ð´Ð»Ñ Ð¿Ð¾Ð»ÐµÐ¹ emp_nbr и boss_emp_nbr.
293 -- У J. Celko иÑпользовалоÑÑŒ значение -99999, которое
294 -- запиÑывалоÑÑŒ в Ð¿Ð¾Ð»Ñ emp_nbr и boss_emp_nbr удалÑемых
295 -- запиÑей. Ðто значение Ñлужило меткой удалÑемой запиÑи.
296 -- Ðо мы добавили ограничение UNIQUE ( emp_nbr )
297 -- в таблицу Org_chart. ПоÑтому теперь уже Ñтало
298 -- невозможно иметь более одной запиÑи Ñо значением полÑ
299 -- emp_nbr равным -99999. Мы вынуждены запиÑывать в Ñто поле
300 -- Ð ÐЗЛИЧÐЫЕ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð² разных запиÑÑÑ… таблицы Org_chart.
301 -- Ртакие Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ÑƒÐ´Ð¾Ð±Ð½Ð¾ формировать, иÑпользуÑ
302 -- поÑледовательноÑть.
303 CREATE TEMP SEQUENCE New_emp_nbr START WITH 1;
304 -- Создадим временную таблицу.
305 CREATE TEMP TABLE Working_table ( emp_nbr INTEGER NOT NULL )
306 ON COMMIT DROP;
307 -- Отложим проверку вÑех ограничений FOREIGN KEY
308 -- до конца транзакции, иначе СУБД не позволит нам
309 -- выполнÑть Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÐµÐ¹ emp_nbr и boss_emp_nbr:
310 -- мы будем запиÑывать в них отрицательные значениÑ,
311 -- а Ñтих значений нет в таблице Personnel, на которую
312 -- ÑÑылаетÑÑ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° Org_chart.
313 SET CONSTRAINTS org_chart_emp_nbr_fkey,
314 org_chart_boss_emp_nbr_fkey,
315org_chart_boss_emp_nbr_fkey1
316 DEFERRED;
317 -- Пометим корень удалÑемого поддерева и вÑех
318 -- непоÑредÑтвенных подчиненных путем запиÑи в поле
319 -- emp_nbr или в поле boss_emp_nbr отрицательного значениÑ,
320 -- формируемого Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ поÑледовательноÑти.
321 UPDATE Org_chart
322 SET emp_nbr = CASE WHEN emp_nbr = dead_guy
323 THEN nextval( 'New_emp_nbr' ) * -1
324 ELSE emp_nbr
325 END,
326 boss_emp_nbr = CASE WHEN boss_emp_nbr = dead_guy
327 THEN nextval( 'New_emp_nbr' ) * -1
328ELSE boss_emp_nbr
329 END
330 -- УÑловие WHERE означает, что выбираютÑÑ Ð»Ð¸ÑˆÑŒ запиÑи,
331 -- в которых либо в поле emp_nbr, либо в поле boss_emp_nbr
332 -- Ñтоит значение идентификатора "главы" удалÑемого поддерева.
333 WHERE dead_guy IN ( emp_nbr, boss_emp_nbr );
334 -- Помечаем лиÑÑ‚ÑŒÑ Ð´ÐµÑ€ÐµÐ²Ð°, Ñ‚. е. запиÑи Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ð½Ð¸ÐºÐ¾Ð²,
335 -- не ÑвлÑющихÑÑ Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð¸ÐºÐ°Ð¼Ð¸ Ð´Ð»Ñ Ð´Ñ€ÑƒÐ³Ð¸Ñ… работников.
336 WHILE EXISTS ( SELECT * FROM Org_chart
337 WHERE boss_emp_nbr < 0 AND emp_nbr >= 0 )
338 LOOP
339 -- Получим ÑпиÑок подчиненных Ñледующего уровнÑ.
340 -- Сначала удалим вÑе запиÑи из временной таблицы.
341 DELETE FROM Working_table;
342
343 -- Выбираем подчиненных.
344 INSERT INTO Working_table
345 SELECT emp_nbr FROM Org_chart
346 WHERE boss_emp_nbr < 0;
347 -- Пометим Ñледующий уровень подчиненных.
348 -- Получаем очередное чиÑло из поÑледовательноÑти
349 -- и умножаем его на -1, поÑкольку нам нужны
350 -- отрицательные коды работников, т. к. именно
351 -- отрицательные Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ ÑвлÑÑŽÑ‚ÑÑ Ð¼ÐµÑ‚ÐºÐ¾Ð¹ удалÑемой запиÑи.
352 UPDATE Org_chart
353 SET emp_nbr = nextval( 'New_emp_nbr' ) * -1
354 WHERE emp_nbr IN ( SELECT emp_nbr FROM Working_table );
355 -- Помечаем начальников Ñледующего уровнÑ
356 -- (при движении вниз по дереву).
357 UPDATE Org_chart
358 SET boss_emp_nbr = nextval( 'New_emp_nbr' ) * -1
359 WHERE boss_emp_nbr IN ( SELECT emp_nbr FROM Working_table
360);
361 END LOOP;
362 -- УдалÑем вÑе помеченные узлы.
363 DELETE FROM Org_chart WHERE emp_nbr < 0;
364 -- Снова активизируем вÑе ограничениÑ.
365 SET CONSTRAINTS ALL IMMEDIATE;
366 -- Удалим врЕменную поÑледовательноÑть.
367 DROP SEQUENCE New_emp_nbr;
368END;
369$$
370LANGUAGE plpgsql;
371
372-- -----------------------------------------------------------
373-- ПредÑтавление (VIEW) Ð´Ð»Ñ Ñ€ÐµÐºÐ¾Ð½ÑÑ‚Ñ€ÑƒÐ¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¾Ñ€Ð³Ð°Ð½Ð¸Ð·Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð¾Ð¹
374-- Ñтруктуры.
375-- -----------------------------------------------------------
376DROP VIEW IF EXISTS Personnel_org_chart CASCADE;
377CREATE VIEW Personnel_org_chart
378( emp_nbr, emp, boss_emp_nbr, boss ) AS
379 -- За оÑнову принимаетÑÑ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° Org_chart).
380 -- ПРИМЕЧÐÐИЕ. LEFT OUTER JOIN необходим,
381 -- Ñ‚. к. у Ñ€ÑƒÐºÐ¾Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»Ñ Ð¾Ñ€Ð³Ð°Ð½Ð¸Ð·Ð°Ñ†Ð¸Ð¸ нет начальника
382 -- и значение Ð¿Ð¾Ð»Ñ boss_emp_nbr у него NULL.
383 SELECT O1.emp_nbr, E1.emp_name, O1.boss_emp_nbr, B1.emp_name
384 FROM ( Org_chart AS O1 LEFT OUTER JOIN Personnel AS B1
385 ON O1.boss_emp_nbr = B1.emp_nbr ), Personnel AS E1
386 WHERE O1.emp_nbr = E1.emp_nbr;
387
388-- -----------------------------------------------------------
389-- ПоÑтроение вÑех путей Ñверху дерева вниз
390-- (только Ð´Ð»Ñ Ñ‡ÐµÑ‚Ñ‹Ñ€ÐµÑ… уровней иерархии)
391-- -----------------------------------------------------------
392DROP VIEW IF EXISTS Create_paths;
393CREATE VIEW Create_paths ( level1, level2, level3, level4 ) AS
394 SELECT O1.emp AS e1, O2.emp AS e2, O3.emp AS e3,
395 O4.emp AS e4
396 FROM Personnel_org_chart AS O1
397 LEFT OUTER JOIN Personnel_org_chart AS O2
398 ON O1.emp = O2.boss
399 LEFT OUTER JOIN Personnel_org_chart AS O3
400 ON O2.emp = O3.boss
401 LEFT OUTER JOIN Personnel_org_chart AS O4
402 ON O3.emp = O4.boss
403 -- ЕÑли закомментировать уÑловие WHERE, тогда будут
404 -- поÑтроены цепочки, начинающиеÑÑ Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ работника,
405 -- а не только Ñ Ð³Ð»Ð°Ð²Ð½Ð¾Ð³Ð¾ руководителÑ.
406 WHERE O1.emp = 'Иван';
407
408-- -----------------------------------------------------------
409-- Ð¤ÑƒÐ½ÐºÑ†Ð¸Ñ Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ñлемента иерархии и продвижениÑ
410-- дочерних Ñлементов на один уровень вверх (Ñ‚. е. к "бабушке").
411-- -----------------------------------------------------------
412CREATE OR REPLACE FUNCTION delete_and_promote_subtree(
413 IN dead_guy INTEGER ) RETURNS VOID AS
414$$
415-- Параметр dead_guy -- код работника, возглавлÑющего поддерево.
416BEGIN
417 -- Ðазначить нового начальника вÑем непоÑредÑтвенным
418 -- подчиненным удалÑемого работника.
419 UPDATE Org_chart
420 -- Получим код начальника Ð´Ð»Ñ ÑƒÐ´Ð°Ð»Ñемого работника.
421 SET boss_emp_nbr = ( SELECT boss_emp_nbr
422 FROM Org_chart
423WHERE emp_nbr = dead_guy
424 )
425 WHERE boss_emp_nbr = dead_guy;
426 -- Теперь удалÑем работника. Ð’Ñе его подчиненные уже
427 -- переподчинены вышеÑтоÑщему начальнику.
428 DELETE FROM Org_chart WHERE emp_nbr = dead_guy;
429END;
430$$
431LANGUAGE plpgsql;