我在SQL中执行“树状”查询(我们称之为什么?)时遇到了一些麻烦。
请看下面的图表(表格和列名称都是丹麦语 - 抱歉):
DB diagram http://img197.imageshack.us/img197/8721/44060572.jpg 使用MSSQL Server 2005,目标是为每个客户(Kunde)找到最多父组(Gruppe)。
每个组可以有许多父组和许多子组。
而且,我还想知道如何显示这样的树:
Customer 1 - Parent group 1 - Child group 1 - ChildChild group n - Child group n - Parent group n - ... - ... Customer n - ...
另一个问题:
查询如何查找所有客户的所有组?父母和子女团体。
答案 0 :(得分:5)
您可以使用CTE来动态构建“完整路径”列
--DROP TABLE Gruppe, Kunde, Gruppe_Gruppe, Kunde_Gruppe
CREATE TABLE Gruppe (
Id INT PRIMARY KEY
, Name VARCHAR(100)
)
CREATE TABLE Kunde (
Id INT PRIMARY KEY
, Name VARCHAR(100)
)
CREATE TABLE Gruppe_Gruppe (
ParentGruppeId INT
, ChildGruppeId INT
)
CREATE TABLE Kunde_Gruppe (
KundeId INT
, GruppeId INT
)
INSERT Gruppe
VALUES (1, 'Group 1'), (2, 'Group 2'), (3, 'Group 3')
, (4, 'Sub-group A'), (5, 'Sub-group B'), (6, 'Sub-group C'), (7, 'Sub-group D')
INSERT Kunde
VALUES (1, 'Kunde 1'), (2, 'Kunde 2'), (3, 'Kunde 3')
INSERT Gruppe_Gruppe
VALUES (1, 4), (1, 5), (1, 7)
, (2, 6), (2, 7)
, (6, 1)
INSERT Kunde_Gruppe
VALUES (1, 1), (1, 2)
, (2, 3), (2, 4)
;WITH CTE
AS (
SELECT CONVERT(VARCHAR(1000), REPLACE(CONVERT(CHAR(5), k.Id), ' ', 'K')) AS TheKey
, k.Name AS Name
FROM Kunde k
UNION ALL
SELECT CONVERT(VARCHAR(1000), REPLACE(CONVERT(CHAR(5), x.KundeId), ' ', 'K')
+ REPLACE(CONVERT(CHAR(5), g.Id), ' ', 'G')) AS TheKey
, g.Name
FROM Gruppe g
JOIN Kunde_Gruppe x
ON g.Id = x.GruppeId
UNION ALL
SELECT CONVERT(VARCHAR(1000), p.TheKey + REPLACE(CONVERT(CHAR(5), g.Id), ' ', 'G')) AS TheKey
, g.Name
FROM Gruppe g
JOIN Gruppe_Gruppe x
ON g.Id = x.ChildGruppeId
JOIN CTE p
ON REPLACE(CONVERT(CHAR(5), x.ParentGruppeId), ' ', 'G') = RIGHT(p.TheKey, 5)
WHERE LEN(p.TheKey) < 32 * 5
)
SELECT *
, LEN(TheKey) / 5 AS Level
FROM CTE c
ORDER BY c.TheKey
如果您有大量的读取和罕见的修改,性能可能不是最佳的。
答案 1 :(得分:4)
我不能说比Joe Celko更好。问题通常在于构建的模型不适合构建层次结构,并且这些模型必须考虑层次结构的特征。太深了吗?太宽了吗?它是狭窄的吗?
在宽树和浅树上成功的关键之一是在列中的层次结构中有完整路径,就像Celko在第一个链接中提到的那样。
答案 2 :(得分:3)
我提出了一个解决方案,解决了为每个客户列出所有组的问题。父母和子女团体。
您怎么看?
WITH GroupTree AS ( SELECT kg.KundeId, g.Id GruppeId FROM ActiveDirectory.Gruppe g INNER JOIN ActiveDirectory.Kunde_Gruppe kg ON g.Id = kg.GruppeId AND (EXISTS (SELECT * FROM ActiveDirectory.Gruppe_Gruppe WHERE ParentGruppeId = g.Id) OR NOT EXISTS (SELECT * FROM ActiveDirectory.Gruppe_Gruppe WHERE ParentGruppeId = g.Id)) UNION ALL SELECT GroupTree.KundeId, gg.ChildGruppeId FROM ActiveDirectory.Gruppe_Gruppe gg INNER JOIN GroupTree ON gg.ParentGruppeId = GroupTree.GruppeId ) SELECT KundeId, GruppeId FROM GroupTree OPTION (MAXRECURSION 32767)
答案 3 :(得分:2)
这样的事情怎么样:
DECLARE @Customer TABLE(
CustomerID INT IDENTITY(1,1),
CustomerName VARCHAR(MAX)
)
INSERT INTO @Customer SELECT 'Customer1'
INSERT INTO @Customer SELECT 'Customer2'
INSERT INTO @Customer SELECT 'Customer3'
DECLARE @CustomerTreeStructure TABLE(
CustomerID INT,
TreeItemID INT
)
INSERT INTO @CustomerTreeStructure (CustomerID,TreeItemID) SELECT 1, 1
INSERT INTO @CustomerTreeStructure (CustomerID,TreeItemID) SELECT 2, 12
INSERT INTO @CustomerTreeStructure (CustomerID,TreeItemID) SELECT 3, 1
INSERT INTO @CustomerTreeStructure (CustomerID,TreeItemID) SELECT 3, 12
DECLARE @TreeStructure TABLE(
TreeItemID INT IDENTITY(1,1),
TreeItemName VARCHAR(MAX),
TreeParentID INT
)
INSERT INTO @TreeStructure (TreeItemName,TreeParentID) SELECT '001', NULL
INSERT INTO @TreeStructure (TreeItemName,TreeParentID) SELECT '001.001', 1
INSERT INTO @TreeStructure (TreeItemName,TreeParentID) SELECT '001.001.001', 2
INSERT INTO @TreeStructure (TreeItemName,TreeParentID) SELECT '001.001.002', 2
INSERT INTO @TreeStructure (TreeItemName,TreeParentID) SELECT '001.001.003', 2
INSERT INTO @TreeStructure (TreeItemName,TreeParentID) SELECT '001.002', 1
INSERT INTO @TreeStructure (TreeItemName,TreeParentID) SELECT '001.003', 1
INSERT INTO @TreeStructure (TreeItemName,TreeParentID) SELECT '001.003.001', 7
INSERT INTO @TreeStructure (TreeItemName,TreeParentID) SELECT '001.001.002.001', 4
INSERT INTO @TreeStructure (TreeItemName,TreeParentID) SELECT '001.001.002.002', 4
INSERT INTO @TreeStructure (TreeItemName,TreeParentID) SELECT '001.001.002.003', 4
INSERT INTO @TreeStructure (TreeItemName,TreeParentID) SELECT '002', NULL
INSERT INTO @TreeStructure (TreeItemName,TreeParentID) SELECT '002.001', 12
INSERT INTO @TreeStructure (TreeItemName,TreeParentID) SELECT '002.001.001', 13
INSERT INTO @TreeStructure (TreeItemName,TreeParentID) SELECT '002.001.002', 13
;WITH Structure AS (
SELECT TreeItemID,
TreeItemName,
TreeParentID,
REPLICATE('0',5 - LEN(CAST(TreeItemID AS VARCHAR(MAX)))) + CAST(TreeItemID AS VARCHAR(MAX)) + '\\' TreePath
FROM @TreeStructure ts
WHERE ts.TreeParentID IS NULL
UNION ALL
SELECT ts.*,
s.TreePath + REPLICATE('0',5 - LEN(CAST(ts.TreeItemID AS VARCHAR(5)))) + CAST(ts.TreeItemID AS VARCHAR(5)) + '\\' TreePath
FROM @TreeStructure ts INNER JOIN
Structure s ON ts.TreeParentID = s.TreeItemID
)
SELECT c.CustomerName,
Children.TreeItemName,
Children.TreePath
FROM @Customer c INNER JOIN
@CustomerTreeStructure cts ON c.CustomerID = cts.CustomerID INNER JOIN
Structure s ON cts.TreeItemID = s.TreeItemID INNER JOIN
(
SELECT *
FROM Structure
) Children ON Children.TreePath LIKE s.TreePath +'%'
ORDER BY 1,3
OPTION (MAXRECURSION 0)
答案 4 :(得分:1)
在T-SQL中,您可以编写while循环。未测试的:
@group = <starting group>
WHILE (EXISTS(SELECT * FROM Gruppe_Gruppe WHERE ChildGruppeId=@group))
BEGIN
SELECT @group=ParentGruppeId FROM Gruppe_Gruppe WHERE ChildGruppeId=@group
END
答案 5 :(得分:1)
我们使用SQL Server 2000,并且有一个使用SQL Books Online中的堆栈扩展层次结构的示例,我已经为我们的ERP系统编写了许多变体
http://support.microsoft.com/kb/248915
我认为在SQL 2005中有一个使用CTE的Native方法,但我自己没有使用它