【问题标题】:Will GETUTCDATE() return the same value if used twice in the same statement?如果在同一语句中使用两次,GETUTCDATE() 会返回相同的值吗?
【发布时间】:2011-05-17 19:43:06
【问题描述】:

我有一个触发器,只要输入一个值,它就会自动将给定条目的 CreationDate 和 ModifiedDate 设置为当前的 UTC 时间。 (CreationDate 此后将保持不变,而 ModifiedDate 将在每次更新时通过另一个触发器进行更新)。

我想确保插入且从未更新的项目的 CreationDate 和 ModifiedDate 具有完全相同的值,因此我使用了这样的变量:

DECLARE @currentTime DATETIME
SELECT @currentTime = GETUTCDATE()
UPDATE dbo.MyTable SET CreationDate = @currentTime, ModifiedDate = @currentTime
    ...

在我的命令式编程心态中,我假设这可以防止GETUTCDATE() 被调用两次,并可能产生略有不同的结果。这真的有必要吗?如果不是,这会更贵,更便宜,还是和下面的代码完全一样?

UPDATE dbo.MyTable SET CreationDate = GETUTCDATE(), ModifiedDate = GETUTCDATE()
    ...

【问题讨论】:

    标签: sql-server


    【解决方案1】:

    感谢gbn提供的链接,相信this answers my question

    与 rand() 一样,它每列计算一次,但所有行的计算一次保持不变。 ... 查看实际执行计划中的 ComputeScalar 运算符属性,您会看到 GetDate() 被计算了两次。

    我检查了一下,似乎这在 SQL Server 2008 中仍然以同样的方式发生:GetUtcDate() 在执行计划中被评估了两次。它不会每行产生不同的结果,但如果时机恰到好处,它可能会在每列产生不同的结果。

    编辑

    我实际上可以证明这种行为!试试这个:

    select GETUTCDATE(), RAND(), RAND(), ...[~3000 RAND()s]..., RAND(), GETUTCDATE()
    from [TableOfYourChoice]
    

    在我的实验中,我在第一列中得到了2011-05-17 20:47:34.247,在最后一列中得到了2011-05-17 20:47:34.250,由于评估了第一列和最后一列之间的所有RAND()s,结果显示了三毫秒的差异第二次调用 GETUTCDATE()。

    【讨论】:

      【解决方案2】:
      DECLARE @Counter INT = 1
      
      WHILE (1 = (SELECT 1 WHERE GETUTCDATE()  = GETUTCDATE()))
      SET @Counter = @Counter+1
      
      SELECT @Counter /*Returns almost immediately with a number in the 000s for me.*/
      

      只是为了证明在SELECT 列表中也会发生这种情况。

      DECLARE @T TABLE 
      (
      rownum INT IDENTITY(1,1) PRIMARY KEY,
      d1 datetime,
      d2 datetime
      )
      
      WHILE (NOT EXISTS(SELECT * FROM @T WHERE d1 <> d2))
          BEGIN
          DELETE FROM @T
          INSERT INTO @T 
          SELECT GETUTCDATE(),GETUTCDATE()
          END
      
      SELECT * FROM @T
      

      顺便说一句:如果出于某种原因您想逐行评估 GETUTCDATE(),您可以将其包装在标量 UDF 中。

      CREATE FUNCTION dbo.GETUTCDATE()
      RETURNS DATETIME
      WITH SCHEMABINDING
      AS
      BEGIN
      RETURN GETUTCDATE()
      END
      GO
      
      SELECT GETUTCDATE(),dbo.GETUTCDATE()
      FROM master..spt_values
      

      【讨论】:

      • 优秀。这是很直接的证明,它并不总是返回相同的值。
      【解决方案3】:

      将 GETUTCDATE() 保留在变量中是一个更好的选择,因为它可以确保 CreationDate 和 ModifiedDate 保持不变。但是,我调用了GETUTCDATE() 每次查询的次数,它们都返回了相同的值,所以在我看来,每次查询的 GETUTCDATE() 值都保持不变。

      【讨论】:

        【解决方案4】:

        这将是相同的值。

        GETDATE 和 GETUTCDATE 是每个查询评估一次的一些函数:不是该查询中的每行或每列。优化器将确保它们相同,因为它会同时更新值

        另一种选择是定义一个 DEFAULT 约束,这样您就可以做到这一点而不必担心。

        UPDATE dbo.MyTable
        SET CreationDate = DEFAULT, ModifiedDate = DEFAULT, ...
        ...
        

        我的表具有类似列的默认约束,并且从未遇到过问题。这也意味着我永远不必考虑我在代码中使用什么函数。

        编辑:

        我可能错了:SQL Server: intrigued by GETDATE()

        或者我可能是对的:Selecting GETDATE() function twice in a select list-- same value for both?

        文章:Conor Cunnigham mentions it the behaviour

        Edit2:我显然错了:请参阅 StriplingWarrior 的自我回答。它是按每列计算的(不是每行也不是每个查询)

        【讨论】:

        • 你知道这是否是任何地方的官方记录吗?我不想依赖这种行为,除非它是一个记录在案的功能,而且我过去一直没有成功找到这样的东西。
        • @Jeffrey L Whitledge:很多地方都间接记录了每个查询的函数评估,而不是每行。包括这里。不过会检查。
        • +1,感谢您提供的出色链接。你是对的,它没有被评估每行,但从我收集的信息来看,它似乎被评估每列一次,但它发生得如此之快以至于它' 99.99% 的时间可能会出现相同的结果。
        • @StriplingWarrior - 如果 99.99% 的时间相同,并且您的逻辑取决于列是否相同,并且如果您的表中有 100 万行,那么您将有 100 行那是错的!可能最好坚持使用变量。
        • @Jeffrey L Whitledge:同意。这就是为什么我给了 +1,但没有将此答案标记为正确。
        【解决方案5】:

        基于 SQL Server 2008 中的大量实验,我认为以下表征是正确的:

        在单个 SELECT、INSERT 或 UPDATE 查询中,日期和时间函数的每次出现都将返回相同的值,它出现在所有行和列中,包括列默认值。

        两个不同的日期和时间函数(例如,GETUTCDATE() 和 GETDATE())可能会返回彼此不同的时间(即使在调整了时区等之后)。

        单个批次中的多个查询可能会返回不同的值。

        SELECT GETUTCDATE(), GETUTCDATE() -- These will be the same
        
        SELECT GETUTCDATE()  --  These may
        SELECT GETUTCDATE()  --  be different
        

        这种行为在任何地方都没有记录,使用变量可以明确意图,所以我可能不会依赖这种行为,除非避免这种依赖是一个主要负担。

        【讨论】:

        • 感谢您花时间对此进行测试,但这实际上是不正确的。请参阅我的更新答案。
        • 同意。执行我的答案中的代码也清楚地表明了这一点。
        猜你喜欢
        • 2013-09-24
        • 2020-09-30
        • 2020-04-10
        • 2016-01-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-09
        • 1970-01-01
        相关资源
        最近更新 更多