在 SQL Server 查询中放置 ASSERT

usr*_*usr 7 sql-server sql-server-2014

作为MERGE我想运行的查询的一部分,我想在运行时断言某个条件成立。当MERGE找到 -match 时,我想更新某个列并执行以下逻辑:

  1. 如果目标列是NULL,则写入源值
  2. 如果目标是NOT NULL,则断言目标和源是相同的

我希望这两个值在情况 2 中总是相同的,但我可能犯了一个错误(有一个错误)。发生这种情况时,我想使语句崩溃并让我的应用报告错误。这是一种非常罕见的错误情况,通常处理过程中不会发生这种情况。

所以我在想我可以滥用被零除的异常来触发崩溃:

MERGE
...
WHEN MATCHED BY TARGET THEN UPDATE SET
  TargetCol = CASE
    WHEN TargetCol IS NULL THEN SourceCol
    WHEN TargetCol = SourceCol THEN SourceCol
    ELSE 0/0 END --crash!
Run Code Online (Sandbox Code Playgroud)

这会可靠地工作吗?有理由不这样做吗?

Pau*_*ite 3

这会可靠地工作吗?

这个问题的答案取决于您所需的保证的强度。过去围绕求值顺序存在一些错误CASE,例如常量折叠生成错误的表达式。据我所知,SQL Server 2014 中当前没有任何错误会影响您的查询,但这并不意味着它们将来不会发生。

当然,任何代码都存在风险,但我的评估是,将此技术与MERGE. 正如您所知,Merge 的实现很复杂,并且过去存在许多相关的错误。

有什么理由不应该这样做吗?

就我个人而言,我希望避免被零除作为解决方案的一部分。更好的选择可能是尝试写入一个违反CHECK目标表上的显式(或其他)约束的值。这将导致执行计划中出现显式的 Assert 运算符,位于 Merge 运算符之后。添加这样的约束可能并不理想(如果允许列保存其类型域中的所有值,则甚至可能),但这是需要考虑的一件事。

另一项一般建议:避免在值匹配时更新目标列。这在逻辑上是一个空操作,但在物理上并不总是如此。

DECLARE @target AS TABLE (pk integer PRIMARY KEY, col2 integer NULL CHECK (col2 <> -1));
DECLARE @source AS TABLE (pk integer PRIMARY KEY, col2 integer NULL);

INSERT @target VALUES (1, 100), (2, NULL), (3, 300);
INSERT @source VALUES (1, 100), (2, 200), (3, 301);

MERGE @target AS T
USING @source AS S
    ON S.pk = T.pk
WHEN MATCHED 
    AND
    (
        -- No update if values match
        T.col2 IS NULL 
        OR T.col2 <> S.col2
    )
    THEN UPDATE SET col2 = 
        CASE 
            WHEN T.col2 IS NULL THEN S.col2
            ELSE -1 -- violates CHECK constraint
        END;
Run Code Online (Sandbox Code Playgroud)

与 Assert 合并计划