我已经阅读了很多关于处理表 RBAR 效率非常低的信息,虽然我知道这不是 SQL 的优化目标,但我很想看看是否有一种方法可以完成这项任务并避免循环或一组嵌套的 CASE 语句(因为我一次只能嵌套 10 个)。
问题:我有一个主库存文件(mif 表),其中包含我们所有的零件编号,并带有一个标志来表示该零件是否已被另一个零件取代/替换。还有另一个表包含该部分被替换的内容。问题在于,如果您愿意的话,替代级别是一个独立的单元。例如,如果第 123 部分被替换为 456,但 456 被替换为 789,那么到目前为止还没有任何东西可以直接告诉我们第 123 部分最终被替换为 789 部分。
我正在尝试创建一个表来记录初始编号和最终替换第 123 部分的链结束部分编号。
很抱歉,这很长,但这里有两个查询的简化版本,它们都可以工作(一个 LOOP 和一个 CASE 语句),但都有局限性。有没有办法既有效地做到这一点,又能让我扩展过去的 10 个 CASE 语句?
正在使用两个表:mif 和 dc_dim:
mif结构:
mfr_code | part_number | superseded
ABC | 123 | Y
DEF | 456 | Y
GHI | 789 | N
dc_dim 结构:
MFR_CODE | PART_NUM | REPLACED | REPLACED_MFR_CODE | REPLACED_PART_NUM
ABC | 123 | Y | DEF | 456
DEF | 456 | Y | GHI | 789
GHI | 789 | N | (null) | (null)
首先,使用循环:
/***Create dbo.MifSuperseded since such a large amount of records.
Dropped at end.
This table contains the next link for each part number that has been superseded.***/
SELECT id = IDENTITY(int,1,1)
,a.mfr_code, a.part_number
,b.REPLACED, b.REPLACED_MFR_CODE, b.REPLACED_PART_NUM
INTO dbo.MifSuperseded
FROM dbo.mif a
LEFT OUTER JOIN dc_dim b
ON a.mfr_code = b.MFR_CODE AND a.part_number = b.PART_NUM
LEFT OUTER JOIN mif c
ON b.REPLACED_MFR_CODE = c.mfr_code AND b.REPLACED_PART_NUM = c.PART_NUM
WHERE a.superseded = N'Y';
--Declare variables
DECLARE @id int = (SELECT MIN(id) FROM dbo.MifSuperseded);
DECLARE @Replaced nvarchar(3);
DECLARE @NumOfSupers int;
DECLARE @MfgCode nvarchar(5);
DECLARE @PartNum nvarchar(30);
DECLARE @ReplacedMfgCode nvarchar(5);
DECLARE @ReplacedPartNum nvarchar(30);
--Outside loop that goes row-by-row
WHILE @id IS NOT NULL
BEGIN
SET @ReplacedMfgCode = (SELECT REPLACED_MFR_CODE FROM dbo.MifSuperseded WHERE id = @id);
SET @ReplacedPartNum = (SELECT REPLACED_PART_NUM FROM dbo.MifSuperseded WHERE id = @id);
SET @Replaced = (SELECT REPLACED FROM dbo.MifSuperseded WHERE mfr_code = @MfgCode AND part_number = @PartNum);
SET @NumOfSupers = 1;
--Inside loop that discovers end-of-chain replacement
WHILE @Replaced = 'Y'
BEGIN
SET @MfgCode = @ReplacedMfgCode;
SET @PartNum = @ReplacedPartNum;
SET @ReplacedMfgCode = (SELECT REPLACED_MFR_CODE
FROM dbo.MifSuperseded
WHERE mfr_code = @MfgCode
AND part_number = @PartNum);
SET @ReplacedPartNum = (SELECT REPLACED_PART_NUM
FROM dbo.MifSuperseded
WHERE mfr_code = @MfgCode
AND part_number = @PartNum);
SET @Replaced = (SELECT REPLACED
FROM dbo.MifSuperseded
WHERE mfr_code = @ReplacedMfgCode
AND part_number = @ReplacedPartNum);
SET @MfgCode = @ReplacedMfgCode;
SET @PartNum = @ReplacedPartNum;
SET @NumOfSupers = @NumOfSupers + 1;
END
--Inserts final superseded part number into table
INSERT INTO dbo.SUPERSESSION_EOC
SELECT mfr_code
,part_number
,@NumOfSupers AS NUMBER_OF_SUPERSESSIONS
,@MfgCode AS REPLACED_MFR_CODE
,@PartNum AS REPLACED_PART_NUM
FROM dbo.MifSuperseded
WHERE id = @id
SELECT @id = MIN(id) FROM dbo.MifSuperseded WHERE id > @id;
END;
DROP TABLE dbo.MifSuperseded;
现在使用 CASE 语句:
/***Create dbo.MifSuperseded since such a large amount of records.
Dropped at end.
This table contains the next link for each part number that has been superseded.***/
SELECT id = IDENTITY(int,1,1)
,a.mfr_code, a.part_number
,b.REPLACED, b.REPLACED_MFR_CODE, b.REPLACED_PART_NUM
INTO dbo.MifSuperseded
FROM dbo.mif a
LEFT OUTER JOIN dc_dim b
ON a.mfr_code = b.MFR_CODE AND a.part_number = b.PART_NUM
LEFT OUTER JOIN mif c
ON b.REPLACED_MFR_CODE = c.mfr_code AND b.REPLACED_PART_NUM = c.PART_NUM
WHERE a.superseded = N'Y';
/***Select Into dbo.mif_supersession_final_replacement.***/
INSERT INTO dbo.SUPERSESSION_EOC
SELECT mif_1.mfr_code
,mif_1.part_number
,(CASE WHEN mif_10.REPLACED_MFR_CODE IS NULL THEN
CASE WHEN mif_9.REPLACED_MFR_CODE IS NULL THEN
CASE WHEN mif_8.REPLACED_MFR_CODE IS NULL THEN
CASE WHEN mif_7.REPLACED_MFR_CODE IS NULL THEN
CASE WHEN mif_6.REPLACED_MFR_CODE IS NULL THEN
CASE WHEN mif_5.REPLACED_MFR_CODE IS NULL THEN
CASE WHEN mif_4.REPLACED_MFR_CODE IS NULL THEN
CASE WHEN mif_3.REPLACED_MFR_CODE IS NULL THEN
CASE WHEN mif_2.REPLACED_MFR_CODE IS NULL THEN
CASE WHEN mif_1.REPLACED_MFR_CODE IS NULL THEN ''
ELSE 1 END
ELSE 2 END
ELSE 3 END
ELSE 4 END
ELSE 5 END
ELSE 6 END
ELSE 7 END
ELSE 8 END
ELSE 9 END
ELSE 10 END) AS NUMBER_OF_SUPERSESSIONS
,(CASE WHEN mif_10.REPLACED_MFR_CODE IS NULL THEN
CASE WHEN mif_9.REPLACED_MFR_CODE IS NULL THEN
CASE WHEN mif_8.REPLACED_MFR_CODE IS NULL THEN
CASE WHEN mif_7.REPLACED_MFR_CODE IS NULL THEN
CASE WHEN mif_6.REPLACED_MFR_CODE IS NULL THEN
CASE WHEN mif_5.REPLACED_MFR_CODE IS NULL THEN
CASE WHEN mif_4.REPLACED_MFR_CODE IS NULL THEN
CASE WHEN mif_3.REPLACED_MFR_CODE IS NULL THEN
CASE WHEN mif_2.REPLACED_MFR_CODE IS NULL THEN
CASE WHEN mif_1.REPLACED_MFR_CODE IS NULL THEN ''
ELSE mif_1.REPLACED_MFR_CODE END
ELSE mif_2.REPLACED_MFR_CODE END
ELSE mif_3.REPLACED_MFR_CODE END
ELSE mif_4.REPLACED_MFR_CODE END
ELSE mif_5.REPLACED_MFR_CODE END
ELSE mif_6.REPLACED_MFR_CODE END
ELSE mif_7.REPLACED_MFR_CODE END
ELSE mif_8.REPLACED_MFR_CODE END
ELSE mif_9.REPLACED_MFR_CODE END
ELSE mif_10.REPLACED_MFR_CODE END) AS REPLACED_MFR_CODE
,(CASE WHEN mif_10.REPLACED_PART_NUM IS NULL THEN
CASE WHEN mif_9.REPLACED_PART_NUM IS NULL THEN
CASE WHEN mif_8.REPLACED_PART_NUM IS NULL THEN
CASE WHEN mif_7.REPLACED_PART_NUM IS NULL THEN
CASE WHEN mif_6.REPLACED_PART_NUM IS NULL THEN
CASE WHEN mif_5.REPLACED_PART_NUM IS NULL THEN
CASE WHEN mif_4.REPLACED_PART_NUM IS NULL THEN
CASE WHEN mif_3.REPLACED_PART_NUM IS NULL THEN
CASE WHEN mif_2.REPLACED_PART_NUM IS NULL THEN
CASE WHEN mif_1.REPLACED_PART_NUM IS NULL THEN ''
ELSE mif_1.REPLACED_PART_NUM END
ELSE mif_2.REPLACED_PART_NUM END
ELSE mif_3.REPLACED_PART_NUM END
ELSE mif_4.REPLACED_PART_NUM END
ELSE mif_5.REPLACED_PART_NUM END
ELSE mif_6.REPLACED_PART_NUM END
ELSE mif_7.REPLACED_PART_NUM END
ELSE mif_8.REPLACED_PART_NUM END
ELSE mif_9.REPLACED_PART_NUM END
ELSE mif_10.REPLACED_PART_NUM END) AS REPLACED_PART_NUM
FROM dbo.MifSuperseded AS mif_1
LEFT OUTER JOIN dbo.MifSuperseded AS mif_2
ON mif_1.REPLACED_MFR_CODE = mif_2.mfr_code AND mif_1.REPLACED_PART_NUM = mif_2.part_number
LEFT OUTER JOIN dbo.MifSuperseded AS mif_3
ON mif_2.REPLACED_MFR_CODE = mif_3.mfr_code AND mif_2.REPLACED_PART_NUM = mif_3.part_number
LEFT OUTER JOIN dbo.MifSuperseded AS mif_4
ON mif_3.REPLACED_MFR_CODE = mif_4.mfr_code AND mif_3.REPLACED_PART_NUM = mif_4.part_number
LEFT OUTER JOIN dbo.MifSuperseded AS mif_5
ON mif_4.REPLACED_MFR_CODE = mif_5.mfr_code AND mif_4.REPLACED_PART_NUM = mif_5.part_number
LEFT OUTER JOIN dbo.MifSuperseded AS mif_6
ON mif_5.REPLACED_MFR_CODE = mif_6.mfr_code AND mif_5.REPLACED_PART_NUM = mif_6.part_number
LEFT OUTER JOIN dbo.MifSuperseded AS mif_7
ON mif_6.REPLACED_MFR_CODE = mif_7.mfr_code AND mif_6.REPLACED_PART_NUM = mif_7.part_number
LEFT OUTER JOIN dbo.MifSuperseded AS mif_8
ON mif_7.REPLACED_MFR_CODE = mif_8.mfr_code AND mif_7.REPLACED_PART_NUM = mif_8.part_number
LEFT OUTER JOIN dbo.MifSuperseded AS mif_9
ON mif_8.REPLACED_MFR_CODE = mif_9.mfr_code AND mif_8.REPLACED_PART_NUM = mif_9.part_number
LEFT OUTER JOIN dbo.MifSuperseded AS mif_10
ON mif_9.REPLACED_MFR_CODE = mif_10.mfr_code AND mif_9.REPLACED_PART_NUM = mif_10.part_number
DROP TABLE dbo.MifSuperseded;
这两个都将在 SUPERSESSION_EOC 表中正确插入 ABC 123 部分的记录,如下所示:
mfr_code | part_number | NUMBER_OF_SUPERSESSIONS | REPLACED_MFR_CODE | REPLACED_PART_NUM
ABC | 123 | 2 | GHI | 789
使用 CASE 语句的插入查询仅用了 2 多分钟就插入了 358,000 条记录。使用循环的插入运行了 20 分钟,当我停止查询时只插入了 9,000 条记录。除非我嵌套嵌套的 CASE 语句以一次获得 10 个以上的语句,否则使用 CASE 语句的插入限制为 10 个超级会话,而循环插入查询按原样无法使用。
关于如何使这个过程可扩展和高效的任何想法?任何帮助都将不胜感激。再一次,我很抱歉它这么长,但我想给你我正在使用的所有东西。
编辑:我刚刚尝试使用带有 OUTER APPLY 的递归 CTE,它有效,但仍然很慢:
/***Create dbo.MifSuperseded since such a large amount of records.
Dropped at end.
This table contains the next link for each part number that has been superseded.***/
SELECT id = IDENTITY(int,1,1)
,a.mfr_code, a.part_number
,b.REPLACED, b.REPLACED_MFR_CODE, b.REPLACED_PART_NUM
INTO dbo.MifSuperseded
FROM dbo.mif a
LEFT OUTER JOIN dc_dim b
ON a.mfr_code = b.MFR_CODE AND a.part_number = b.PART_NUM
LEFT OUTER JOIN mif c
ON b.REPLACED_MFR_CODE = c.mfr_code AND b.REPLACED_PART_NUM = c.PART_NUM
WHERE a.superseded = N'Y';
WITH cteMifSupersededRecursive AS
(
SELECT 1 AS NUMBER_OF_SUPERSESSIONS
,mfr_code, part_number
,REPLACED_MFR_CODE, REPLACED_PART_NUM
FROM dbo.MifSuperseded
UNION ALL
SELECT NUMBER_OF_SUPERSESSIONS + 1, cur.mfr_code, cur.part_number
,nxt.REPLACED_MFR_CODE, nxt.REPLACED_PART_NUM
FROM cteMifSupersededRecursive cur
INNER JOIN dbo.MifSuperseded nxt
ON cur.REPLACED_MFR_CODE = nxt.mfr_code AND cur.REPLACED_PART_NUM = nxt.part_number
)
SELECT a.mfr_code, a.part_number
,b.NUMBER_OF_SUPERSESSIONS ,b.REPLACED_MFR_CODE, b.REPLACED_PART_NUM
INTO dbo.SUPERSESSION_EOC
FROM dbo.MifSuperseded a
CROSS APPLY
(SELECT TOP 1 REPLACED_MFR_CODE, REPLACED_PART_NUM, NUMBER_OF_SUPERSESSIONS
FROM cteMifSupersededRecursive
WHERE mfr_code = a.mfr_code AND part_number = a.part_number
ORDER BY NUMBER_OF_SUPERSESSIONS DESC
) b
DROP TABLE dbo.MifSuperseded;