我在分层 CTE 和一些我们需要解决的奇怪逻辑方面遇到了一个令人烦恼的问题,我真的希望有人能帮助指出我在用 CTE 解决这种情况时做错了什么。
这是我们在此示例中处理的分层数据:
这是有问题的 SQL,后面是问题的描述和用于创建包含数据的测试表的 SQL 语句:
DECLARE @UserId nvarchar(50);
SET @UserId = 'A';
DECLARE @StatusType int;
SET @StatusType = '2';
;WITH recursiveItems (Id, Depth)
AS
(
SELECT Id, 0 AS Depth
FROM dbo.CteTest
WHERE UserId = @UserId
--AND StatusType = @StatusType
-- This would also be incorrect for the issue
AND ParentId IS NULL
UNION ALL
SELECT dbo.CteTest.Id, Depth + 1
FROM dbo.CteTest
INNER JOIN recursiveItems
ON dbo.CteTest.ParentId = recursiveItems.Id
WHERE UserId = @UserId
AND StatusType = @StatusType
)
SELECT A.*, recursiveItems.Depth
FROM recursiveItems
INNER JOIN dbo.CteTest A WITH(NOLOCK) ON
recursiveItems.Id = A.Id
ORDER BY A.Id
这不是返回所需的数据。当前返回的数据位于下图中的 NOT CORRECT 部分。Id 为 10 的行是我们要省略的行。
本质上,逻辑应该是任何其子项的状态类型等于 2 的任何父记录(带有子项的记录)都应与其子项一起返回。在示例中,这是 ID 为:1、5、6、7、9 的行。
目前 CTE/SQL/Code 无论如何都返回所有父记录,
应该返回 ID 为 1 的记录,即使它的状态类型为 1,因为它的至少一个孩子、他们的孩子、孙辈等的状态类型等于 2。
不应返回 ID 为 10 的记录,因为它的状态不等于 2 或任何子级。如果记录在没有子记录时的状态类型为 2,则它也应该被返回。
这是创建有助于显示问题的测试表的 DDL:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[CteTest](
[Id] [int] IDENTITY(1,1) NOT NULL,
[StatusType] [int] NOT NULL,
[UserId] [nvarchar](50) NOT NULL,
[ParentId] [int] NULL,
CONSTRAINT [PK_CteTest] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
这是表格的种子数据,可以证明问题:
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (1,'A',NULL)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (1,'B',NULL)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (2,'B',NULL)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (1,'A',1)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (2,'A',1)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (2,'A',5)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (2,'A',6)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (3,'A',6)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (2,'A',NULL)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (4,'A',NULL)
INSERT INTO [dbo].[CteTest]([StatusType],[UserId],[ParentId]) VALUES (3,'A',10)