您的位置:首页 > 数据库

SQL tree data struct(3): nested set (1,12) include (2,3) & (4, 11) ...

2008-03-02 11:49 447 查看
-- Credit to JOE CELKO
-- http://archives.postgresql.org/pgsql-sql/2001-11/msg00004.php
-- CREATE TABLE Personnel
-- (emp CHAR(10) NOT NULL PRIMARY KEY,
-- lft INTEGER NOT NULL UNIQUE CHECK (lft > 0),
-- rgt INTEGER NOT NULL UNIQUE CHECK (rgt > 1),
-- CONSTRAINT order_okay CHECK (lft < rgt) );

/*
The organizational chart would look like this as a directed graph:

Albert (1,12)
/ /
/ /
Bert (2,3) Chuck (4,11)
/ | /
/ | /
/ | /
/ | /
Donna (5,6) Eddie (7,8) Fred (9,10)
*/
if object_id('personnel') is not null
drop table personnel
go
create table personnel (emp varchar(30) not null primary key, lft int, rgt int)
go
if object_id('salaries') is not null
drop table salaries
go
create table salaries (emp varchar(30), salary int)
go
go

insert personnel
select 'Albert', 1, 12
union all select 'Bert', 2, 3
union all select 'Chuck', 4, 11
union all select 'Donna', 5, 6
union all select 'Eddie', 7, 8
union all select 'Fred', 9, 10
go

insert salaries
select 'Albert', 100000
union all select 'Bert', 70000
union all select 'Chuck', 90000
union all select 'Donna', 60000
union all select 'Eddie', 60000
union all select 'Fred', 50000
go
--==============================================================================
-- 1. An employee and all their Supervisors, no matter how deep the tree.
if object_id('func_get_supervisors') is not null
drop function func_get_supervisors
go
create function func_get_supervisors(@staff varchar(30))
returns table
as
return
select supervisor.*
from personnel as subordinate, personnel as supervisor
where subordinate.lft between supervisor.lft and supervisor.rgt and subordinate.emp = @staff
go
--==============================================================================
-- 2. The employee and all subordinates. There is a nice symmetry here.
if object_id('func_get_subordinates') is not null
drop function func_get_subordinates
go
create function func_get_subordinates(@staff varchar(30))
returns table
as
return
select subordinate.*
from personnel as subordinate, personnel as supervisor
where subordinate.lft between supervisor.lft and supervisor.rgt and supervisor.emp = @staff
go
--==============================================================================
-- 3. Add a GROUP BY and aggregate functions to these basic queries and you have hierarchical reports.
-- For example, the total salaries which each employee controls:
if object_id('func_dept_salaries') is not null
drop function func_dept_salaries
go
create function func_dept_salaries(@staff varchar(30))
returns int
as
begin
return
(select sum(sal.salary)
from personnel as subordinate, personnel as supervisor, salaries as sal
where subordinate.lft between supervisor.lft and supervisor.rgt and subordinate.emp = sal.emp
group by supervisor.emp
having supervisor.emp = @staff)
end
go
--==============================================================================
-- 4. To find the level of each node, so you can print the tree as an indented listing.
if object_id('func_get_levels') is not null
drop function func_get_levels
go
create function func_get_levels()
returns table
as
return
select indentation=count(supervisor.emp), subordinate.emp
from personnel as subordinate, personnel as supervisor
where subordinate.lft between supervisor.lft and supervisor.rgt
group by subordinate.emp
go
--==============================================================================
-- 5. The nested set model has an implied ordering of siblings which the adjacency list model does not.
if object_id('proc_add_staff') is not null
drop procedure proc_add_staff
go
create procedure proc_add_staff @new_staff varchar(30), @boss varchar(30)
as
if not exists(select * from personnel where emp = @boss)
return

declare @right_most_sibling int
set @right_most_sibling = (select rgt from personnel where emp = @boss)
update personnel set
lft = case when lft > @right_most_sibling then lft + 2 else lft end,
rgt = case when rgt >= @right_most_sibling then rgt + 2 else rgt end
where rgt >= @right_most_sibling
insert personnel select @new_staff, @right_most_sibling, @right_most_sibling+1
go

--==============================================================================
-- delete a node and put all the subordinates under the big boss ???
if object_id('proc_del_staff') is not null
drop procedure proc_del_staff
go
create procedure proc_del_staff @staff varchar(30)
as
declare @lft int, @rgt int
select @lft = lft, @rgt = rgt from personnel where emp = @staff
-- shrink subordinate group
update personnel set lft = lft - 1, rgt = rgt -1 where lft between @lft and @rgt
-- shrink supervisor group
update personnel set
lft = case when lft > @rgt then lft - 2 else lft end,
rgt = case when rgt > @rgt then rgt -2 else rgt end
where rgt > @rgt
delete from personnel where emp = @staff
go

--==============================================================================
-- promotion (1 level) ??
if object_id('proc_promote_staff') is not null
drop procedure proc_promote_staff
go
create procedure proc_promote_staff @staff varchar(30)
as
declare @current_boss varchar(30), @curr_boss_right int, @lft int, @rgt int, @sibling_spread int, @self_spread int

select @lft = lft, @rgt = rgt from personnel where emp = @staff
select top 1 @current_boss = emp, @curr_boss_right = rgt from personnel where lft < @lft and @lft < rgt order by lft desc
-- set expand spread
select @sibling_spread = @rgt - @lft + 1, @self_spread = @curr_boss_right - @rgt

-- memorize all @subordinates
declare @subordinates table(emp varchar(30) collate database_default)
insert @subordinates select emp from personnel where lft between @lft and @rgt
-- update all right sibling
update personnel set lft = lft - @sibling_spread, rgt = rgt - @sibling_spread where lft between @rgt and @curr_boss_right
-- update current_boss right bound
update personnel set rgt = rgt - @sibling_spread where emp = @current_boss
-- promote entire group
update personnel set lft = lft + @self_spread, rgt = rgt + @self_spread from personnel where emp in (select emp from @subordinates)
go
--==============================================================================
-- change post
if object_id('proc_change_post') is not null
drop procedure proc_change_post
go
create procedure proc_change_post @staff varchar(30), @new_boss varchar(30)
as
exec proc_del_staff @staff
exec proc_add_staff @staff, @new_boss
go
--==============================================================================
if object_id('proc_show_dept') is not null
drop procedure proc_show_dept
go
create procedure proc_show_dept @boss varchar(30), @debug bit = 0
as
declare @s nvarchar(1000)
set @s =
'select emp,
boss=case
when emp = ''' + @boss + ''' then NULL
else
(select top 1 emp from personnel super where sub.lft between super.lft and super.rgt
and sub.emp <> super.emp order by super.lft desc)
end
from personnel sub
where sub.lft <> 1 /*exclude big boss, so boss field doesn''t contain nulls*/'

exec proc_print_tree @s, @debug
go
--==============================================================================
if object_id('proc_show_all') is not null
drop procedure proc_show_all
go
create procedure proc_show_all @debug bit = 0
as
exec proc_print_tree
'select emp,
boss=(select top 1 emp from personnel super where sub.lft between super.lft and super.rgt
and sub.emp <> super.emp order by super.lft desc)
from personnel sub', @debug
go
--==============================================================================
-- select * from func_get_supervisors('Eddie')
-- select * from func_get_subordinates('Chuck')
-- select dbo.func_dept_salaries('Chuck')
-- select emp, dbo.func_dept_salaries(emp) from personnel
exec proc_add_staff 'Mark', 'Eddie'
exec proc_promote_staff 'Eddie'
exec proc_add_staff 'Gen', 'Eddie'
exec proc_del_staff 'Chuck'
exec proc_show_all
print '=============================================================================='
exec proc_del_staff 'Mark'
exec proc_change_post 'Mark', 'Donna'
exec proc_show_all

--##############################################################################
drop table personnel, salaries
drop function func_get_supervisors, func_get_subordinates, func_dept_salaries, func_get_levels
drop procedure proc_add_staff, proc_del_staff, proc_promote_staff, proc_change_post, proc_show_all, proc_show_dept
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐