急求一條查詢SQL語句---父類和子類關系的


現有一個類別表A包含欄位如下:
ClassID,ClassName, Validate, ParentClassID

如果記錄里的SELECT * FROM A WHERE ClassID=@ClassID AND ISNULL(ParentClassID,'')='' 有值表示該ClassID為最外層的父類別
如果SELECT * FROM A WHERE ParentClassID=@ClassID 沒有查詢到結果表示該ClassID 為最底層的子類別。

現求一SQL實現下述功能:
  對於一個ClassID,它既不是最外層的父類別,也不是最底層的子類別,要求查詢出該ClassID下的所有最底層的子類別
  比如說類別001 的父類別是000,它下面有類別002,003,004,005,;004下面又有006,007。007下面又有008
  那么我要得到的001下面的所有最底層子類別應該為002,003,005,006,008

求高手指點呀,盡量不要使用游標。

11 个解决方案

#1




--參考查詢指定父節點下的所有子節點:
USE tempdb
GO

-- 建立演示環境
CREATE TABLE Dept(
 id int PRIMARY KEY, 
 parent_id int,
 name nvarchar(20))
INSERT Dept
SELECT 0, 0, N'<全部>' UNION ALL
SELECT 1, 0, N'財務部' UNION ALL
SELECT 2, 0, N'行政部' UNION ALL
SELECT 3, 0, N'業務部' UNION ALL
SELECT 4, 0, N'業務部' UNION ALL
SELECT 5, 4, N'銷售部' UNION ALL
SELECT 6, 4, N'MIS' UNION ALL
SELECT 7, 6, N'UI' UNION ALL
SELECT 8, 6, N'軟件開發' UNION ALL
SELECT 9, 8, N'內部開發'
GO

-- 查詢指定部門下面的所有部門
DECLARE @Dept_name nvarchar(20)
SET @Dept_name = N'MIS'
;WITH
DEPTS AS(
 -- 定位點成員
 SELECT * FROM Dept
 WHERE name = @Dept_name
 UNION ALL
 -- 遞歸成員, 通過引用CTE自身與Dept基表JOIN實現遞歸
 SELECT A.*
 FROM Dept A, DEPTS B
 WHERE A.parent_id = B.id
)
SELECT * FROM DEPTS
GO

-- 刪除演示環境
DROP TABLE Dept

----CTE的綜合應用

USE tempdb
GO

-- 建立演示環境
CREATE TABLE Dept(
 id int PRIMARY KEY, 
 parent_id int,
 name nvarchar(20))
INSERT Dept
SELECT 0, 0, N'<全部>' UNION ALL
SELECT 1, 0, N'財務部' UNION ALL
SELECT 2, 0, N'行政部' UNION ALL
SELECT 3, 0, N'業務部' UNION ALL
SELECT 4, 0, N'業務部' UNION ALL
SELECT 5, 4, N'銷售部' UNION ALL
SELECT 6, 4, N'MIS' UNION ALL
SELECT 7, 6, N'UI' UNION ALL
SELECT 8, 6, N'軟件開發' UNION ALL
SELECT 9, 8, N'內部開發'
GO

-- 查詢指定部門下面的所有部門, 並匯總各部門的下級部門數
DECLARE @Dept_name nvarchar(20)
SET @Dept_name = N'MIS'
;WITH
DEPTS AS(   -- 查詢指定部門及其下的所有子部門
 -- 定位點成員
 SELECT * FROM Dept
 WHERE name = @Dept_name
 UNION ALL
 -- 遞歸成員, 通過引用CTE自身與Dept基表JOIN實現遞歸
 SELECT A.*
 FROM Dept A, DEPTS B
 WHERE A.parent_id = B.id
),
DEPTCHILD AS(  -- 引用第1個CTE,查詢其每條記錄對應的部門下的所有子部門
 SELECT 
  Dept_id = P.id, C.id, C.parent_id
 FROM DEPTS P, Dept C
 WHERE P.id = C.parent_id
 UNION ALL
 SELECT 
  P.Dept_id, C.id, C.parent_id
 FROM DEPTCHILD P, Dept C
 WHERE P.id = C.parent_id
),
DEPTCHILDCNT AS( -- 引用第2個CTE, 匯總得到各部門下的子部門數
 SELECT 
  Dept_id, Cnt = COUNT(*)
 FROM DEPTCHILD
 GROUP BY Dept_id
)
SELECT    -- JOIN第1,3個CTE,得到最終的查詢結果
 D.*,
 ChildDeptCount = ISNULL(DS.Cnt, 0)
FROM DEPTS D
 LEFT JOIN DEPTCHILDCNT DS
  ON D.id = DS.Dept_id
GO

-- 刪除演示環境
DROP TABLE Dept



#2


引用 1 樓  的回復:
SQL code



--參考查詢指定父節點下的所有子節點:
USE tempdb
GO

-- 建立演示環境
CREATE TABLE Dept(
 id int PRIMARY KEY, 
 parent_id int,
 name nvarchar(20))
INSERT Dept
SELECT 0, 0, N'<全部>' UNION ALL
SELECT 1……


方法好像很NB,小弟需要好好研究才能看懂,但結果貌似不太對
例子里面查詢出來的結果里有ID分別為6,7,8,9。
如果要查詢id為4下的所有最底層的子部門,結果應該是5,7,9.  6和8都是別的部門的父部門不是我要的結果哦,而且查詢出來的結果里面沒有5,也不好對查詢出來的結果再進行篩選

#3



if OBJECT_ID('tempdb..#t_class') is not null
drop table #t_class

create table #t_class(cid int, cname varchar(10), p_cid int)

insert into #t_class
select 1, 'Class1', null union all
select 2, 'Class2', 1 union all
select 3, 'Class3', 1 union all
select 4, 'Class4', 2 union all
select 5, 'Class5', 2 union all
select 6, 'Class6', 3 union all
select 7, 'Class7', 3 union all
select 8, 'Class8', 5 
;
With cte as
(
 select cid,cname,p_cid,0 lvl, cast('/'+cname as nvarchar) pth from #t_class
 where p_cid is null
 union all
 select e.cid,e.cname,e.p_cid,c.lvl+1, cast(c.pth + '/' + e.cname as nvarchar) pth from cte c
 inner join #t_class e
 on c.cid = e.p_cid
)
select * from cte
where pth like '%Class2%' --找出class2下的班級

#4


引用 3 樓  的回復:
SQL code


if OBJECT_ID('tempdb..#t_class') is not null
drop table #t_class

create table #t_class(cid int, cname varchar(10), p_cid int)

insert into #t_class
select 1, 'Class1', null union ……


還是沒有實現,我只要抓最底層的,不再有子節點的那些子節點。

#5



if OBJECT_ID('tempdb..#t_class') is not null
drop table #t_class

create table #t_class(cid int, cname varchar(10), p_cid int)

insert into #t_class
select 1, 'Class1', null union all
select 2, 'Class2', 1 union all
select 3, 'Class3', 1 union all
select 4, 'Class4', 2 union all
select 5, 'Class5', 2 union all
select 6, 'Class6', 3 union all
select 7, 'Class7', 3 union all
select 8, 'Class8', 5 
;
With cte as
(
 select cid,cname,p_cid,0 lvl, cast('/'+cname as nvarchar) pth from #t_class
 where p_cid is null
 union all
 select e.cid,e.cname,e.p_cid,c.lvl+1, cast(c.pth + '/' + e.cname as nvarchar) pth from cte c
 inner join #t_class e
 on c.cid = e.p_cid
)
select * from cte
where pth like '%Class2%' --找出class2下的班級
and not exists(select 1 from #t_class
               where #t_class.p_cid = cte.cid) --去掉中間節點

#6


引用 2 樓  的回復:
引用 1 樓  的回復:
SQL code



--參考查詢指定父節點下的所有子節點:
USE tempdb
GO

-- 建立演示環境
CREATE TABLE Dept(
id int PRIMARY KEY,
parent_id int,
name nvarchar(20))
INSERT Dept
SELECT 0, 0, N'<全部>' UNION AL……


給出測試數據和你要的結果

#7



/*
標題:獲取某個節點所有的最底層節點
作者:愛新覺羅·毓華(十八年風雨,守得冰山雪蓮花開) 
時間:2009-07-16
地點:新疆烏魯木齊
*/

/*
有一個表名為:tb 
結構如下: 
id       --int 
parentid --int 上層的父節點 
sname    --varchar(50) 

如有以下數據: 
id  parentid  sname 
1  -1         根節點 
2   1         節點1 
3   1         節點2 
4   2         節點3 
5   2         節點4 
6   3         節點5 
7   3         節點6 
7   6         節點7 

假如:我要得到某個節點下所有的最底層的節點數據怎么實現: 
如:我要得到id=3 的所有最底層的數據是: 
id  parentid   sname 
7   3          節點6 
7   6          節點7 
請大家多多幫忙,謝謝! 
*/

create table tb(id int, parentid int, sname varchar(10))
insert into tb values(1 , -1 ,        '根節點') 
insert into tb values(2 ,  1 ,        '節點1') 
insert into tb values(3 ,  1 ,        '節點2') 
insert into tb values(4 ,  2 ,        '節點3') 
insert into tb values(5 ,  2 ,        '節點4') 
insert into tb values(6 ,  3 ,        '節點5') 
insert into tb values(7 ,  3 ,        '節點6') 
insert into tb values(7 ,  6 ,        '節點7') 
go

--查詢指定節點及其所有子節點的函數 
CREATE FUNCTION f_Cid(@ID int) RETURNS @t_Level TABLE(ID int,Level int) 
AS 
BEGIN 
  DECLARE @Level int 
  SET @Level=1 
  INSERT @t_Level SELECT @ID,@Level 
  WHILE @@ROWCOUNT>0 
  BEGIN 
    SET @Level=@Level+1 
    INSERT @t_Level SELECT a.ID,@Level 
    FROM tb a,@t_Level b 
    WHERE a.parentid=b.ID 
    AND b.Level=@Level-1 
  END 
  RETURN 
END 
GO 

SELECT distinct a.* FROM tb a,f_Cid(3) b WHERE a.ID=b.ID and a.id not in (select parentid from tb)

drop table tb
drop function dbo.f_cid

/*
id          parentid    sname      
----------- ----------- ---------- 
7           3           節點6
7           6           節點7

(所影響的行數為 2 行)
*/



--2005
create table tb(id int, parentid int, sname nvarchar(10))
insert into tb values(1 , -1 ,        N'根節點') 
insert into tb values(2 ,  1 ,        N'節點1') 
insert into tb values(3 ,  1 ,        N'節點2') 
insert into tb values(4 ,  2 ,        N'節點3') 
insert into tb values(5 ,  2 ,        N'節點4') 
insert into tb values(6 ,  3 ,        N'節點5') 
insert into tb values(7 ,  3 ,        N'節點6') 
insert into tb values(7 ,  6 ,        N'節點7') 
go

;WITH
TREE AS(
    SELECT * FROM TB
    WHERE parentid = 3  -- 要查詢的父 id
    UNION ALL
    SELECT TB.* FROM TB, TREE
    WHERE TB.parentid = TREE.id
)
SELECT * FROM TREE where id not in (select parentid from tb)

drop table tb

#8


在7樓的基礎上再加不是頂級節點的判斷即可.

#9


引用 6 樓  的回復:
引用 2 樓 的回復:

引用 1 樓 的回復:
SQL code



--參考查詢指定父節點下的所有子節點:
USE tempdb
GO

-- 建立演示環境
CREATE TABLE Dept(
id int PRIMARY KEY,
parent_id int,
name nvarchar(20))
INSERT Dept
SELECT 0, 0, N'<……




比如說
insert into #t_class
select 1, 'Class1', null union all
select 2, 'Class2', 1 union all
select 3, 'Class3', 1 union all
select 4, 'Class4', 2 union all
select 5, 'Class5', 2 union all
select 6, 'Class6', 3 union all
select 7, 'Class7', 3 union all
select 8, 'Class8', 5 union all
select 9,'Class9',8

我要查詢class2下面的所有最底層子節點
想要得到的結果是class4 和 class8
class5雖然也是class2的子節點,但是它不是最底層的

#10


上面說錯了 應該是Class4和Class9 

#11


引用 8 樓  的回復:
在7樓的基礎上再加不是頂級節點的判斷即可.


這方法不錯,問題已解決。
關鍵是加了一條 WHERE parentid = 3  -- 要查詢的父 id

只是還不太懂這種遞歸的原理,挺神奇的

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com