比较存储过程中的元组并将差异写入差异表

时间:2017-01-11 17:30:05

标签: sql tsql stored-procedures

我正在尝试将序列化的某些对象与数据库进行比较。对于任何这样的比较,我知道两个这样的对象的ID。我们称他们为o1o2

包含XAB列的表格C包含分别与o1o2相关联的元组。因此,X中的每一行都由其父(o1o2)和属于同一父级的所有行中唯一的ID列标识。

o1o2的比较是通过查找X中仅存在于o1或仅存在于o2的项目来完成的(假定项目相等)基于其本地ID),并查找至少其中一列ABC具有不同值的项目对。对于每个差异,需要将一行添加到Diff表。

我的目标是使用存储过程进行此比较。

对于X中的每个项目,Diff表可以接收任意数量的行。因此,我认为INSERT ... SELECT ...陈述不是正确的方法。 (至少我看不到如何继续使用它。)

相反,似乎我必须以某种方式“程序性地”实现这种比较,通过“手动”逐列比较,在外连接之后。因此,我这样开始:

CREATE PROCEDURE Compare (@o1 INT, @o2 INT) AS
BEGIN
    DECLARE @o1x INT; @o2x INT;
    DECLARE @o1A INT, @o2A INT;
    DECLARE @o1B INT, @o2B INT;
    DECLARE @o1C INT, @o2C INT;

    DECLARE XCursor CURSOR FOR
        SELECT x1.ID,
               x1.A,
               x1.B,
               x1.C,
               x2.ID,
               x2.A,
               x2.B,
               x2.C
        FROM X x1 FULL OUTER JOIN X x2 ON x1.ID = x2.ID
        WHERE x1.PARENT = o1 AND x2.PARENT = o2;

    OPEN XCursor

    WHILE 1=1
    BEGIN
        FETCH NEXT FROM XCursor INTO @o1x,
                                     @o1A,
                                     @o1B,
                                     @o1C,
                                     @o2x,
                                     @o2A,
                                     @o2B,
                                     @o2C
        IF @@fetch_status <> 0 BREAK

        IF @o1x IS NULL
            INSERT INTO Diff (Object, DiffDescription) VALUES (@o2x, 'O1 X is missing');

        IF @o2x IS NULL
            INSERT INTO Diff (Object, DiffDescription) VALUES (@o1x, 'O2 X is missing');

        IF @o1A <> @o2A
            INSERT INTO Diff (Object, DiffDescription) VALUES (@o1x, 'A is different.');

        IF @o1B <> @o2B
            INSERT INTO Diff (Object, DiffDescription) VALUES (@o1x, 'B is different.');

        IF @o1C <> @o2C
            INSERT INTO Diff (Object, DiffDescription) VALUES (@o1x, 'C is different.');
    END

    CLOSE XCursor

    DEALLOCATE XCursor
END

(这不能保证在语法上100%正确。我试图说明一般的想法。)

我的问题是:这似乎非常麻烦且难以维持 1 。是否有更简洁的方法进行这种比较,特别是通常用于此类用例的典型模式?

显然,我对我的示例数据库架构和代码进行了大量匿名化和概括。在实际代码中,不仅有三列ABC,更像是20;不仅有一个表X,而且从X链接的其他子元素和Diff表格相当复杂,需要更详细的数据。我现在拥有的实际代码因此更加复杂。

1 :例如,稍后可能会有更多列添加到X。此外,Diff的结构有可能在将来进行修订。此外,一旦其他数据库供应商成为目标,所有这些可能需要转换为不同的SQL方言。

编辑:作为完全外部联接的替代方法,我还可以在那里使用内部联接并检测仅Xo1分别存在的o2行。不确定这是否会更好地表现;至少,当只找到一个对象时,它将删除禁止基于列的比较的必要性,我发布的当前源代码尚未完成。

EDIT2:重复并澄清:Diff表必须在一对等效X行中的每个差异行中收到一行。第一个检测到的差异不仅仅是一行,而是每个检测到的差异都有一行。如果两个X行在AC列中具有不同的值,则必须在Diff中使用相应的消息创建两个行。这是非常棘手的部分,而这一方面意味着我不能只过滤X对的集合。

2 个答案:

答案 0 :(得分:0)

如果列的值永远不会发生,则更简单 - 在下面的示例中,我使用-1您可以使用'<null>'作为字符串。你没有说哪些是ID,哪些是字符串

SELECT
  x1.ID as ID,
  x1.A as x1_A, x1.B as x1_B, x1.C as x1_C,
  x2.A as x2_A, x2.B as x2_B, x2.C as x2_C,
  CASE
    -- I don't bother to check for the NULLS as a specific case but you could add in those 
    -- before these tests if you wanted to show them.
    WHEN ISNULL(x1.A, -1) <> ISNULL(x2.A, -1) THEN 'A column does not match'
    WHEN ISNULL(x1.B, -1) <> ISNULL(x2.B, -1) THEN 'B column does not match'
    WHEN ISNULL(x1.C, -1) <> ISNULL(x2.C, -1) THEN 'C column does not match'
  END as REASON
FROM X x1 
LEFT JOIN X x2 ON x1.ID = x2.ID
WHERE 
      x1.PARENT = o1 
  AND x2.PARENT = o2
  AND (
       ISNULL(x1.A, -1) <> ISNULL(x2.A, -1)
    OR ISNULL(x1.B, -1) <> ISNULL(x2.B, -1)
    OR ISNULL(x1.C, -1) <> ISNULL(x2.C, -1)
  )

答案 1 :(得分:0)

我的测试数据:

SELECT  ID, A, B, C
INTO    #o1
FROM    #X
WHERE   PARENT = 1

SELECT  ID, A, B, C
INTO    #o2
FROM    #X
WHERE   PARENT = 2

--check if one of them is not existing
INSERT INTO #DIFF (OBJ,DIFF_DESC)
SELECT  ISNULL(#o1.ID,#o2.ID), 
        CASE WHEN #o1.ID IS NULL
            THEN 'O1 X is missing'
            ELSE 'O2 X is missing'
        END
FROM    #o1 
        FULL JOIN #o2 ON #o1.ID = #o2.ID
WHERE   #o1.ID IS NULL
        OR #o2.ID IS NULL

-- Just a check, if one the two objects does not exist 
-- (@@ROWCOUNT>0), then there will be no check for A,B,C
IF @@ROWCOUNT = 0
BEGIN
    INSERT INTO #DIFF (OBJ,DIFF_DESC)
    SELECT  ISNULL(#o1.ID,#o2.ID),
            'A is different'
    FROM    #o1 
            INNER JOIN #o2 ON #o1.ID = #o2.ID 
    WHERE   #o1.A <> #o2.A
    UNION ALL
    SELECT  ISNULL(#o1.ID,#o2.ID),
            'B is different'
    FROM    #o1 
            INNER JOIN #o2 ON #o1.ID = #o2.ID
    WHERE   #o1.B <> #o2.B
    UNION ALL
    SELECT  ISNULL(#o1.ID,#o2.ID),
            'C is different'
    FROM    #o1 
            INNER JOIN #o2 ON #o1.ID = #o2.ID 
    WHERE   #o1.C <> #o2.C
END

解决方案:我为每个“对象”创建了一个临时表,然后开始比较它们:

{{1}}

结果:

result

请注意这一点:

  • 如果两个对象中的一个不存在,则不检查A,B,C
  • 您可以添加支票,如果#o1和#o2为空,那么您的表格不会包含此PARENTS的任何数据