【问题标题】:SQL Server - find a letter in a chain which doesn't belong to anyoneSQL Server - 在链中查找不属于任何人的字母
【发布时间】:2019-11-27 16:56:15
【问题描述】:

我有一个查询,它使用 itemnum 并搜索表以找到链中没有任何 replacementItemNum 的 itemnum。

就像一条链,a属于b,b属于c,需要在链中找到不属于任何人的字母。

表有 72k 条记录。

  • 如果我通过 A 或 B 或 C,我会得到字母 D,因为它没有任何替换项。

  • 如果我通过 D,那么结果将为 NULL

这是数据(链)的样子:

ItemNum - ReplacementItemNum
----------------------------    
    A     -  B
    B     -  C
    C     -  D
    D     -  

此查询耗时过长;可以重写一下,这样就不用那么长时间了?

DECLARE @CountryOID INT = 250 ,
        @ItemNum VARCHAR(50) = 'A';

DECLARE @LastItem VARCHAR(50);
DECLARE @NextItem VARCHAR(50);

SELECT @LastItem = ReplaceItemNum
FROM dbo.MacInventory
WHERE ItemNum = @ItemNum
  AND CountryOID = @CountryOID;

WHILE @LastItem <> ''
BEGIN
    SET @NextItem = @LastItem;

    SELECT @LastItem = ReplaceItemNum
    FROM dbo.MacInventory
    WHERE ItemNum = @LastItem
      AND CountryOID = @CountryOID;
END;

SELECT @NextItem; 

【问题讨论】:

标签: sql sql-server data-analysis


【解决方案1】:

Recursive cte 来救援!

首先,创建并填充示例表(在您以后的问题中保存我们这一步):

DECLARE @T AS TABLE
(
    ItemNum char(1),
    ReplacementItemNum char(1)
);

INSERT INTO @T (ItemNum, ReplacementItemNum) VALUES
('A', 'B'), 
('B', 'C'),
('C', 'D'),
('D', NULL),
('E', 'F'), -- added some more data to make sure we don't get the wrong result...
('F', NULL);

设定起点:

DECLARE @StartFrom char(1) = 'A';

递归cte:

WITH CTE AS
(
    SELECT ItemNum, ReplacementItemNum
    FROM @T
    WHERE ItemNum = @StartFrom

    UNION ALL

    SELECT T.ItemNum, T.ReplacementItemNum
    FROM @T As T
    JOIN CTE 
        ON T.ItemNum = CTE.ReplacementItemNum
)

查询:

SELECT IIF(ItemNum = @StartFrom, NULL, ItemNum) As ItemNum
FROM CTE
WHERE ReplacementItemNum IS NULL

最后,结果:

ItemNum
D

You can see a live demo on rextester.

【讨论】:

  • 如果我通过 D 则结果将为 NULL。
  • 简单。更新了答案和现场演示。
  • 查询有效,但与我的其他查询花费相同的时间:(
  • 表有 72k 条记录。
  • 相关列上有索引吗?你检查执行计划了吗?
【解决方案2】:

您对表中的列 (CountryOID, ItemNum) 是否有适当的索引?尝试以下递归 cte 解决方案:

;WITH cte_item (ItemNum, ReplaceItemNum)
AS
(
SELECT ItemNum, ReplaceItemNum
FROM dbo.MacInventory
WHERE   ItemNum = @ItemNum
AND CountryOID = @CountryOID
UNION
SELECT m.ItemNum, m.ReplaceItemNum
FROM dbo.MacInventory m
INNER JOIN cte_item i
ON m.ItemNum = i.ReplaceItemNum 
AND m.CountryOID = @CountryOID
)
SELECT * FROM cte_item WHERE ReplaceItemNum='';

【讨论】:

  • 应该被修复。抱歉,我无法测试它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-16
  • 2019-02-15
  • 2011-12-31
  • 1970-01-01
  • 2017-07-14
  • 1970-01-01
  • 2018-08-28
相关资源
最近更新 更多