我正在尝试将序列化的某些对象与数据库进行比较。对于任何这样的比较,我知道两个这样的对象的ID。我们称他们为o1
和o2
。
包含X
,A
和B
列的表格C
包含分别与o1
和o2
相关联的元组。因此,X
中的每一行都由其父(o1
或o2
)和属于同一父级的所有行中唯一的ID
列标识。
o1
和o2
的比较是通过查找X
中仅存在于o1
或仅存在于o2
的项目来完成的(假定项目相等)基于其本地ID
),并查找至少其中一列A
,B
或C
具有不同值的项目对。对于每个差异,需要将一行添加到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 。是否有更简洁的方法进行这种比较,特别是通常用于此类用例的典型模式?
显然,我对我的示例数据库架构和代码进行了大量匿名化和概括。在实际代码中,不仅有三列A
,B
和C
,更像是20;不仅有一个表X
,而且从X
链接的其他子元素和Diff
表格相当复杂,需要更详细的数据。我现在拥有的实际代码因此更加复杂。
1 :例如,稍后可能会有更多列添加到X
。此外,Diff
的结构有可能在将来进行修订。此外,一旦其他数据库供应商成为目标,所有这些可能需要转换为不同的SQL方言。
编辑:作为完全外部联接的替代方法,我还可以在那里使用内部联接并检测仅X
或o1
分别存在的o2
行。不确定这是否会更好地表现;至少,当只找到一个对象时,它将删除禁止基于列的比较的必要性,我发布的当前源代码尚未完成。
EDIT2:重复并澄清:Diff
表必须在一对等效X
行中的每个差异行中收到一行。第一个检测到的差异不仅仅是一行,而是每个检测到的差异都有一行。如果两个X
行在A
和C
列中具有不同的值,则必须在Diff
中使用相应的消息创建两个行。这是非常棘手的部分,而这一方面意味着我不能只过滤X
对的集合。
答案 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}}
结果:
请注意这一点: