SQL While循环查找链替换/替代的结束

时间:2014-07-21 17:13:54

标签: sql while-loop case-statement rbar

我已经阅读了大量处理表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)

首先,使用LOOP:

/***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;

0 个答案:

没有答案