含SYSDATETIME()的UNION语句执行顺序及时间差异咨询
关于SQL Server中UNION查询里SYSDATETIME()调用的执行行为解答
首先先把你提供的查询语句整理成格式化的代码块:
( SELECT (...abbreviated...), r.CardInformation, SYSDATETIME(), NULL, '' FROM {1} r LEFT OUTER JOIN [{2}] p ON r.ProcessDate = p.ProcessDateTime AND r.ProcessType = p.ProcessType AND r.SalesAmount = p.SalesAmount AND r.TerminalID = p.TerminalID AND r.SlipNumber = p.SlipNumber WHERE r.ProcessDate >= '{3}' AND r.ProcessDate < '{4}' AND r.ProcessType >= '1' AND r.ProcessType <= '3' AND r.ErrorType = '0' AND r.ErrorCode = ' ' AND r.ProcessResult <> '0' AND r.ProcessResult <> ' ' AND r.ReversalType = '0' UNION SELECT (...abbreviated...), r.CardInformation, SYSDATETIME(), NULL, '' FROM {1} r LEFT OUTER JOIN [{2}] p ON r.ProcessDate = p.ProcessDateTime AND r.ProcessType = p.ProcessType AND r.SalesAmount = p.SalesAmount AND r.TerminalID = p.TerminalID AND r.SlipNumber = p.SlipNumber WHERE r.SalesFixDate = '{3}' AND r.ProcessDate < '{4}' AND r.ProcessType >= '1' AND r.ProcessType <= '3' AND r.ErrorType = '0' AND r.ErrorCode = ' ' AND r.ProcessResult <> '0' AND r.ProcessResult <> ' ' AND r.ReversalType = '0' )
一、查询的执行顺序
SQL Server处理这个带UNION的查询时,流程大致是这样的:
- 先分别解析并执行两个
SELECT子查询,各自生成独立的结果集; - 对两个结果集执行
UNION操作(默认会自动去重,如果用UNION ALL则跳过去重步骤); - 返回最终合并后的结果集。
核心点在于:两个SELECT子查询的执行在时间上存在先后顺序(哪怕间隔极短),并非完全同步执行。
二、两次SYSDATETIME()返回不同结果的场景
SYSDATETIME()是非确定性函数,每次调用都会获取SQL Server服务器当前的系统时间(精度可达100纳秒)。出现两次结果不同的情况,通常是因为:
- 第一个子查询的执行耗时哪怕只有几微秒,都会导致第二个子查询调用
SYSDATETIME()的时间点晚于第一个; - 优化器几乎总是选择串行执行两个子查询(先完成第一个再处理第二个),而非完全并行;
- 系统时钟本身一直在推进,哪怕两个调用间隔极短,时间值也会有差异。
三、0.003秒的差异是否属于预期行为?
完全是预期之内的。SQL Server从未保证过单个查询中多次调用SYSDATETIME()会返回相同值。0.003秒(3毫秒)的间隔非常合理——如果两个子查询涉及较大数据集、复杂连接或过滤逻辑,执行时间差只会更明显。
四、为什么单个查询里两次调用不返回相同结果?
很多开发者会误以为单个查询里的函数会在查询启动时统一执行一次,但这只适用于部分确定性函数。对于SYSDATETIME()这类非确定性函数,SQL Server会在需要计算该值的时机调用它:第一个SELECT里的调用发生在第一个子查询执行时,第二个则发生在第二个子查询执行时,两个时间点必然存在差异。
如果需要两次调用返回完全相同的时间值,可以先把SYSDATETIME()的结果存入变量,再在两个子查询中引用这个变量,示例如下:
DECLARE @CurrentTime DATETIME2 = SYSDATETIME(); ( SELECT (...abbreviated...), r.CardInformation, @CurrentTime, NULL, '' FROM {1} r LEFT OUTER JOIN [{2}] p ON r.ProcessDate = p.ProcessDateTime AND r.ProcessType = p.ProcessType AND r.SalesAmount = p.SalesAmount AND r.TerminalID = p.TerminalID AND r.SlipNumber = p.SlipNumber WHERE r.ProcessDate >= '{3}' AND r.ProcessDate < '{4}' AND r.ProcessType >= '1' AND r.ProcessType <= '3' AND r.ErrorType = '0' AND r.ErrorCode = ' ' AND r.ProcessResult <> '0' AND r.ProcessResult <> ' ' AND r.ReversalType = '0' UNION SELECT (...abbreviated...), r.CardInformation, @CurrentTime, NULL, '' FROM {1} r LEFT OUTER JOIN [{2}] p ON r.ProcessDate = p.ProcessDateTime AND r.ProcessType = p.ProcessType AND r.SalesAmount = p.SalesAmount AND r.TerminalID = p.TerminalID AND r.SlipNumber = p.SlipNumber WHERE r.SalesFixDate = '{3}' AND r.ProcessDate < '{4}' AND r.ProcessType >= '1' AND r.ProcessType <= '3' AND r.ErrorType = '0' AND r.ErrorCode = ' ' AND r.ProcessResult <> '0' AND r.ProcessResult <> ' ' AND r.ReversalType = '0' )
内容的提问来源于stack exchange,提问作者Andrew Winsinger
相关产品推荐
相关产品推荐

