【问题标题】:Would `merge` statement guarantee the same value of a `default current_timestamp` column for all inserted rows?`merge` 语句会保证所有插入行的 `default current_timestamp` 列的值相同吗?
【发布时间】:2019-05-02 20:56:21
【问题描述】:

我相信即使在事务中,插入到具有 default current_timestamp 列的表中的一堆,可能会根据行创建时间具有不同的值,但我不确定 原子 merge 声明是:

Is MERGE an atomic statement in SQL2008?

这是我的测试脚本:

  • 代码

    if type_id(N'ValuableRow') is not null 
        drop type ValuableRow
    
    create type ValuableRow as table (
        JustAValue nvarchar(max)
    )
    
    drop table if exists 
        TestTable
    
    create table TestTable(
        JustAValue nvarchar(max) 
        , Birth datetime2 default current_timestamp
    ) 
    
    go
    
    --// delete TestTable
    
    insert TestTable(JustAValue)
        values ('1234'), ('5678')
    
    declare 
        @rows ValuableRow, @alsoRows ValuableRow
    
    insert @rows(JustAValue)
        values ('abcd'), ('1234'), ('5678'), ('wxyz')
    
    merge 
        TestTable y
    using
        @rows x
    on (y.JustAValue=x.JustAValue)  
    when not matched then  
    insert (JustAValue) 
        values (JustAValue)
    output 
        inserted.JustAValue
    into 
        @alsoRows
    ;
    
    select 
        *
    from 
        @alsoRows
    
    select 
        *
    from 
        TestTable
    

结果显示:

显然,如果操作需要更长的执行时间会发生什么情况是不够的,我想知道merge 是否仍会使所有插入的行具有相同的创建时间?

【问题讨论】:

    标签: sql-server merge


    【解决方案1】:

    无论MERGE 需要多长时间,所有行的默认输入时间都相同。

    这与MERGE 的原子性无关。

    CURRENT_TIMESTAMP 结束调用 GETDATE() 函数。 GETDATE()runtime constant。保证计划中的每个单独引用在整个语句持续时间内具有相同的值(尽管如果计划有多个对 GETDATE() 的引用,这些将是两个不同的运行时常量,可以在不同的时间进行评估)。

    这里的执行计划只有一个对函数的引用。计算一次并给出表达式标签Expr1010

    StmtText
      |--Table Insert(OBJECT:(@alsoRows), SET:([JustAValue] = [tempdb].[dbo].[TestTable].[JustAValue] as [y].[JustAValue]))
           |--Table Merge(OBJECT:([tempdb].[dbo].[TestTable] AS [y]), SET:([tempdb].[dbo].[TestTable].[JustAValue] as [y].[JustAValue] = @rows.[JustAValue] as [x].[JustAValue],[tempdb].[dbo].[TestTable].[Birth] as [y].[Birth] = [Expr1010]) ACTION:([Action1009]))
                |--Compute Scalar(DEFINE:([Expr1010]=CONVERT_IMPLICIT(datetime2(7),getdate(),0)))
                     |--Table Spool
                          |--Filter(WHERE:([Action1009] IS NOT NULL))
                               |--Compute Scalar(DEFINE:([Action1009]=ForceOrder(CASE WHEN [TrgPrb1007] IS NOT NULL THEN NULL ELSE (4) END)))
                                    |--Nested Loops(Left Outer Join, OUTER REFERENCES:([x].[JustAValue]))
                                         |--Table Scan(OBJECT:(@rows AS [x]))
                                         |--Compute Scalar(DEFINE:([TrgPrb1007]=(1)))
                                              |--Filter(WHERE:([tempdb].[dbo].[TestTable].[JustAValue] as [y].[JustAValue]=@rows.[JustAValue] as [x].[JustAValue]))
                                                   |--Table Scan(OBJECT:([tempdb].[dbo].[TestTable] AS [y]))
    

    RAND() 也是一个运行时常量,并且在实践中更容易看到这种行为,因为它不依赖于设计一个长时间运行的语句。

    DECLARE @T TABLE(X FLOAT DEFAULT RAND());
    
    MERGE INTO @T 
    USING sys.objects o ON o.object_id = X
    WHEN NOT MATCHED THEN INSERT DEFAULT VALUES;
    
    SELECT *
    FROM @T; /*All rows will have the same value*/
    

    【讨论】:

    • 我也想知道如何生成执行计划的文本版本..
    • @KenKin - SET SHOWPLAN_TEXT ON 然后执行您想要文本计划的语句。虽然这只是生成计划而不是执行计划
    【解决方案2】:

    Current_timestamp 不能保证在长时间运行的合并语句中插入的所有行都具有相同的时间戳。如果您希望所有行都具有相同的时间戳,我建议在合并之前将时间戳存储在变量中。然后您可以使用时间戳变量作为插入语句的一部分。

    【讨论】:

    • 我想知道的是这样做的必要性;不知道megre是否已经在幕后完成了。
    • 答案是错误的。只有两个基本答案。时间保证是一样的或者时间不保证是一样的,这个选错了
    • 这也是我的想法,但是当我们仍在使用 sql 2008 时(如问题中所述),我们遇到了一个问题,即接口在午夜左右进行了巨大的合并(不要让我开始了解他们为什么这样编码)。无论如何,有些记录以第二天的时间戳结束,导致人们认为接口失败。无论如何,底线是,如果这不再是问题,那么我很乐意纠正。当所有记录都获得一个恒定值确实很重要时,我仍然可能会使用一个变量,但这可能只是我此时的偏执:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多