在 SQL 服务器存储过程中显示父行下的子行
Display child rows under parent rows in SQL Server Stored Procedure
有一个 table,其中存储了有关项目的信息。每个项目都有一个 ID 列 (FPNNumber) 和一个父 ID 列。如果项目有父项目,则用户选择父项目的 ID 号以进入该行的父项目单元格。如果它没有父项目,则父项目列将有一个 0。我在这里看到了一些类似于我的问题,但看起来它们都在使用我没有或不想使用的级别列在我的查询中使用。
我从数据库中获取数据的存储如下所示:
SELECT h.[Priority]
,h.[FPNNumber]
,h.[ProjectName]
,sp.SponsorName
,pm.ProjectManagerName
,o.[OrgName]
,HealthID
,[StrategicAlignmentID] + [FinancialAlignmentID] + [TechnologyAlignmentID] As ProjectScore
,h.[Cost]
,h.[Budget]
,h.[PercentComplete]
,Convert(varchar(10),h.[EstimatedImpDt], 101) As EstimatedImpDt
,[EstimatedROI]
,h.[ExpectedBenefit]
,CONVERT(Decimal(10, 2),((DATEDIFF(day, h.[PaybackPerioddt], GETDATE()))/365.0)) As PaybackPerioddt
FROM [ProjectNew].[Header] h
join ProjectNew.Sponsor sp ON h.SponsorID = sp.SponsorID
join ProjectNew.ProjectManager pm ON h.ProjectManagerID = pm.ProjectManagerID
Join ProjectNew.Organization o ON h.OrgID = o.OrgID
where StatusID is null or StatusID < 12
有一件事我已经尝试过并部分工作但不能完全按照我的需要工作
;WITH Parents
AS(
SELECT h.[Priority]
,h.[FPNNumber]
,h.[ProjectName]
,sp.SponsorName
,pm.ProjectManagerName
,o.[OrgName]
,HealthID
,[StrategicAlignmentID] + [FinancialAlignmentID] + [TechnologyAlignmentID] As ProjectScore
,h.[Cost]
,h.[Budget]
,h.[PercentComplete]
,Convert(varchar(10),h.[EstimatedImpDt], 101) As EstimatedImpDt
,[EstimatedROI]
,h.[ExpectedBenefit]
,CONVERT(Decimal(10, 2),((DATEDIFF(day, h.[PaybackPerioddt], GETDATE()))/365.0)) As PaybackPerioddt
FROM [FPN].[ProjectNew].[Header] h
join ProjectNew.Sponsor sp ON h.SponsorID = sp.SponsorID
join ProjectNew.ProjectManager pm ON h.ProjectManagerID = pm.ProjectManagerID
Join ProjectNew.Organization o ON h.OrgID = o.OrgID
WHERE StatusID is null or StatusID < 12 and h.ParentID = 0
Union All
SELECT he.[Priority]
,he.[FPNNumber]
,he.[ProjectName]
,sp.SponsorName
,pm.ProjectManagerName
,o.[OrgName]
,he.HealthID
,[StrategicAlignmentID] + [FinancialAlignmentID] + [TechnologyAlignmentID] As ProjectScore
,he.[Cost]
,he.[Budget]
,he.[PercentComplete]
,Convert(varchar(10),he.[EstimatedImpDt], 101) As EstimatedImpDt
,he.[EstimatedROI]
,he.[ExpectedBenefit]
,CONVERT(Decimal(10, 2),((DATEDIFF(day, he.[PaybackPerioddt], GETDATE()))/365.0)) As PaybackPerioddt
FROM [FPN].[ProjectNew].[Header] he
join ProjectNew.Sponsor sp ON he.SponsorID = sp.SponsorID
join ProjectNew.ProjectManager pm ON he.ProjectManagerID = pm.ProjectManagerID
Join ProjectNew.Organization o ON he.OrgID = o.OrgID
Join Parents cte on cte.FPNNumber = he.ParentID
WHERE StatusID is null or StatusID < 12
)
Select * from Parents
当我执行它时,结果如下所示:
ID | ParentID
----------------
1 | 0
2 | 0
4 | 2
5 | 2
3 | 1
6 | 1
7 | 1
我需要它看起来像这样:
ID | ParentID
----------------
1 | 0
3 | 1
6 | 1
7 | 1
2 | 0
4 | 2
5 | 2
所以ID为2的行下面有两个对应的子项目,但是ID为1的子项目在下面。
我做错了什么?有没有办法让所有子项目显示在他们的父项目下?如果是这样,有没有办法做到这一点,然后当我将该数据调用到 Visual Studio 中的 ListView 时,如果用户单击排序按钮,它不会扰乱子项目?
Select * from Parents
ORDER BY
CASE WHEN ParentId = 0 THEN Id ELSE ParentId END
,ID
有趣的是,这个问题似乎仍然受到一些关注,所以我将稍微扩展一下我的叙述,使递归过程中发生的事情更清楚。
你的锚 table 开始并抓取 2 行,它保存在内存中,然后将这两行中的每一行连接到它们的子行,然后在下一次迭代中,这些子行然后连接到它们的子行。所以它同时遍历所有谱系,而不是一次一个,这就是为什么你得到显示它的顺序。首先是 0 级,然后是 1、2 级等
因此,如果您希望结果按特定顺序排列,例如 parent then children,您必须告诉 sql 使用 order by 语句对它们进行排序。在您的特定情况下,由于您在递归期间识别和继续您的 ParentIds 的方式,您需要使 Parentless Parent (0) 等于其自身,然后按 ids 排序。这可以在如上所示的 case 表达式中完成。
如果我理解你的问题,你似乎想确保层次结构的顺序正确。
以下是一个通用示例,在这种情况下使用基于标题的序列,但可以是任何可用字段。
此外,范围键是完全可选的。 (如果不需要,请删除 cteR1 和 cteR2)
Declare @Table table (ID int,Pt int,Title varchar(50))
Insert into @Table values (0,null,'Tags'),(1,0,'Transportation'),(2,1,'Boats'),(3,1,'Cars'),(4,1,'Planes'),(5,1,'Trains'),(6,0,'Technology'),(7,6,'FTP'),(8,6,'HTTP'),(9,0,'Finance'),(10,9,'FTP'),(11,9,'401K'),(12,2,'Sail'),(13,2,'Powered'),(14,6,'Internet'),(15,6,'Database'),(16,15,'SQL Server'),(17,15,'MySQL'),(18,15,'MS Access')
Declare @Top int = null --<< Sets top of Hier Try 9
Declare @Nest varchar(25) ='|-----' --<< Optional: Added for readability
;with cteHB as (
Select Seq = cast(1000+Row_Number() over (Order by Title) as varchar(500))
,ID
,Pt
,Lvl=1
,Title
From @Table
Where IsNull(@Top,-1) = case when @Top is null then isnull(Pt,-1) else ID end
Union All
Select Seq = cast(concat(cteHB.Seq,'.',1000+Row_Number() over (Order by cteCD.Title)) as varchar(500))
,cteCD.ID
,cteCD.Pt,cteHB.Lvl+1
,cteCD.Title
From @Table cteCD
Join cteHB on cteCD.Pt = cteHB.ID)
,cteR1 as (Select Seq,ID,R1=Row_Number() over (Order By Seq) From cteHB)
,cteR2 as (Select A.Seq,A.ID,R2=Max(B.R1) From cteR1 A Join cteR1 B on (B.Seq like A.Seq+'%') Group By A.Seq,A.ID )
Select B.R1
,C.R2
,A.ID
,A.Pt
,A.Lvl
,Title = Replicate(@Nest,A.Lvl-1) + A.Title
,A.Seq -- < Included for Illustration
From cteHB A
Join cteR1 B on A.ID=B.ID
Join cteR2 C on A.ID=C.ID
Order By B.R1
Returns
您可以尝试类似于以下堆栈溢出问题中提供的解决方案:
或者您可以尝试以下解决方案。
我在下面提供了我的解决方案以获得您想要的订单。我使用 rnk 作为附加列,您可以在最终结果集中忽略它。
CREATE TABLE #ParentChild (
projectId INT
,parentProjectId INT
)
INSERT INTO #ParentChild
VALUES (1,0),(2,0),(3,1),(4,1),(5,1),(6,2),(7,2),(8,2);
SELECT projectid
,parentprojectid
,row_number() OVER (
PARTITION BY parentconsider ORDER BY projectid
) AS rnk
FROM (
SELECT projectId
,parentProjectId
,CASE
WHEN parentProjectId = 0
THEN projectId
ELSE parentProjectId
END AS parentconsider
FROM #ParentChild
) AS x
有一个 table,其中存储了有关项目的信息。每个项目都有一个 ID 列 (FPNNumber) 和一个父 ID 列。如果项目有父项目,则用户选择父项目的 ID 号以进入该行的父项目单元格。如果它没有父项目,则父项目列将有一个 0。我在这里看到了一些类似于我的问题,但看起来它们都在使用我没有或不想使用的级别列在我的查询中使用。
我从数据库中获取数据的存储如下所示:
SELECT h.[Priority]
,h.[FPNNumber]
,h.[ProjectName]
,sp.SponsorName
,pm.ProjectManagerName
,o.[OrgName]
,HealthID
,[StrategicAlignmentID] + [FinancialAlignmentID] + [TechnologyAlignmentID] As ProjectScore
,h.[Cost]
,h.[Budget]
,h.[PercentComplete]
,Convert(varchar(10),h.[EstimatedImpDt], 101) As EstimatedImpDt
,[EstimatedROI]
,h.[ExpectedBenefit]
,CONVERT(Decimal(10, 2),((DATEDIFF(day, h.[PaybackPerioddt], GETDATE()))/365.0)) As PaybackPerioddt
FROM [ProjectNew].[Header] h
join ProjectNew.Sponsor sp ON h.SponsorID = sp.SponsorID
join ProjectNew.ProjectManager pm ON h.ProjectManagerID = pm.ProjectManagerID
Join ProjectNew.Organization o ON h.OrgID = o.OrgID
where StatusID is null or StatusID < 12
有一件事我已经尝试过并部分工作但不能完全按照我的需要工作
;WITH Parents
AS(
SELECT h.[Priority]
,h.[FPNNumber]
,h.[ProjectName]
,sp.SponsorName
,pm.ProjectManagerName
,o.[OrgName]
,HealthID
,[StrategicAlignmentID] + [FinancialAlignmentID] + [TechnologyAlignmentID] As ProjectScore
,h.[Cost]
,h.[Budget]
,h.[PercentComplete]
,Convert(varchar(10),h.[EstimatedImpDt], 101) As EstimatedImpDt
,[EstimatedROI]
,h.[ExpectedBenefit]
,CONVERT(Decimal(10, 2),((DATEDIFF(day, h.[PaybackPerioddt], GETDATE()))/365.0)) As PaybackPerioddt
FROM [FPN].[ProjectNew].[Header] h
join ProjectNew.Sponsor sp ON h.SponsorID = sp.SponsorID
join ProjectNew.ProjectManager pm ON h.ProjectManagerID = pm.ProjectManagerID
Join ProjectNew.Organization o ON h.OrgID = o.OrgID
WHERE StatusID is null or StatusID < 12 and h.ParentID = 0
Union All
SELECT he.[Priority]
,he.[FPNNumber]
,he.[ProjectName]
,sp.SponsorName
,pm.ProjectManagerName
,o.[OrgName]
,he.HealthID
,[StrategicAlignmentID] + [FinancialAlignmentID] + [TechnologyAlignmentID] As ProjectScore
,he.[Cost]
,he.[Budget]
,he.[PercentComplete]
,Convert(varchar(10),he.[EstimatedImpDt], 101) As EstimatedImpDt
,he.[EstimatedROI]
,he.[ExpectedBenefit]
,CONVERT(Decimal(10, 2),((DATEDIFF(day, he.[PaybackPerioddt], GETDATE()))/365.0)) As PaybackPerioddt
FROM [FPN].[ProjectNew].[Header] he
join ProjectNew.Sponsor sp ON he.SponsorID = sp.SponsorID
join ProjectNew.ProjectManager pm ON he.ProjectManagerID = pm.ProjectManagerID
Join ProjectNew.Organization o ON he.OrgID = o.OrgID
Join Parents cte on cte.FPNNumber = he.ParentID
WHERE StatusID is null or StatusID < 12
)
Select * from Parents
当我执行它时,结果如下所示:
ID | ParentID
----------------
1 | 0
2 | 0
4 | 2
5 | 2
3 | 1
6 | 1
7 | 1
我需要它看起来像这样:
ID | ParentID
----------------
1 | 0
3 | 1
6 | 1
7 | 1
2 | 0
4 | 2
5 | 2
所以ID为2的行下面有两个对应的子项目,但是ID为1的子项目在下面。
我做错了什么?有没有办法让所有子项目显示在他们的父项目下?如果是这样,有没有办法做到这一点,然后当我将该数据调用到 Visual Studio 中的 ListView 时,如果用户单击排序按钮,它不会扰乱子项目?
Select * from Parents
ORDER BY
CASE WHEN ParentId = 0 THEN Id ELSE ParentId END
,ID
有趣的是,这个问题似乎仍然受到一些关注,所以我将稍微扩展一下我的叙述,使递归过程中发生的事情更清楚。
你的锚 table 开始并抓取 2 行,它保存在内存中,然后将这两行中的每一行连接到它们的子行,然后在下一次迭代中,这些子行然后连接到它们的子行。所以它同时遍历所有谱系,而不是一次一个,这就是为什么你得到显示它的顺序。首先是 0 级,然后是 1、2 级等
因此,如果您希望结果按特定顺序排列,例如 parent then children,您必须告诉 sql 使用 order by 语句对它们进行排序。在您的特定情况下,由于您在递归期间识别和继续您的 ParentIds 的方式,您需要使 Parentless Parent (0) 等于其自身,然后按 ids 排序。这可以在如上所示的 case 表达式中完成。
如果我理解你的问题,你似乎想确保层次结构的顺序正确。
以下是一个通用示例,在这种情况下使用基于标题的序列,但可以是任何可用字段。
此外,范围键是完全可选的。 (如果不需要,请删除 cteR1 和 cteR2)
Declare @Table table (ID int,Pt int,Title varchar(50))
Insert into @Table values (0,null,'Tags'),(1,0,'Transportation'),(2,1,'Boats'),(3,1,'Cars'),(4,1,'Planes'),(5,1,'Trains'),(6,0,'Technology'),(7,6,'FTP'),(8,6,'HTTP'),(9,0,'Finance'),(10,9,'FTP'),(11,9,'401K'),(12,2,'Sail'),(13,2,'Powered'),(14,6,'Internet'),(15,6,'Database'),(16,15,'SQL Server'),(17,15,'MySQL'),(18,15,'MS Access')
Declare @Top int = null --<< Sets top of Hier Try 9
Declare @Nest varchar(25) ='|-----' --<< Optional: Added for readability
;with cteHB as (
Select Seq = cast(1000+Row_Number() over (Order by Title) as varchar(500))
,ID
,Pt
,Lvl=1
,Title
From @Table
Where IsNull(@Top,-1) = case when @Top is null then isnull(Pt,-1) else ID end
Union All
Select Seq = cast(concat(cteHB.Seq,'.',1000+Row_Number() over (Order by cteCD.Title)) as varchar(500))
,cteCD.ID
,cteCD.Pt,cteHB.Lvl+1
,cteCD.Title
From @Table cteCD
Join cteHB on cteCD.Pt = cteHB.ID)
,cteR1 as (Select Seq,ID,R1=Row_Number() over (Order By Seq) From cteHB)
,cteR2 as (Select A.Seq,A.ID,R2=Max(B.R1) From cteR1 A Join cteR1 B on (B.Seq like A.Seq+'%') Group By A.Seq,A.ID )
Select B.R1
,C.R2
,A.ID
,A.Pt
,A.Lvl
,Title = Replicate(@Nest,A.Lvl-1) + A.Title
,A.Seq -- < Included for Illustration
From cteHB A
Join cteR1 B on A.ID=B.ID
Join cteR2 C on A.ID=C.ID
Order By B.R1
Returns
您可以尝试类似于以下堆栈溢出问题中提供的解决方案:
或者您可以尝试以下解决方案。
我在下面提供了我的解决方案以获得您想要的订单。我使用 rnk 作为附加列,您可以在最终结果集中忽略它。
CREATE TABLE #ParentChild (
projectId INT
,parentProjectId INT
)
INSERT INTO #ParentChild
VALUES (1,0),(2,0),(3,1),(4,1),(5,1),(6,2),(7,2),(8,2);
SELECT projectid
,parentprojectid
,row_number() OVER (
PARTITION BY parentconsider ORDER BY projectid
) AS rnk
FROM (
SELECT projectId
,parentProjectId
,CASE
WHEN parentProjectId = 0
THEN projectId
ELSE parentProjectId
END AS parentconsider
FROM #ParentChild
) AS x