Mic*_*een 11 sql-server functions sql-server-2019
SQL Server 2019 引入了标量 UDF 内联,又名“Froid”。这“.. 在调用 SQL 查询中嵌入 [标量 UDF]。”
以前,标量 UDF 在它们自己的执行上下文下运行,与周围的查询分开。这样做的一个后果是,在读取提交的快照隔离 (RCSI) 下,该函数可能会看到一组与包含查询所看到的值不同的值集 ( link )。
包含标量函数的查询是否有可能在 RCSI 中以并发写入运行时,会根据函数是否内联而产生不同的结果?
是的,内联函数可以显示与其外联 (!?) 对应物不同的结果。以下可靠地重现了我机器上的情况(Windows 10,4 核 + HT @ 2GHz,16GB RAM,SSD)。
将数据库和会话配置为使用已提交读快照隔离 (RCSI):
alter database Sandpit
set read_committed_snapshot on
with rollback immediate;
GO
set transaction isolation level read committed;
GO
Run Code Online (Sandbox Code Playgroud)
该表将提供一个共享对象,并发工作负载可以对其进行操作。
drop table if exists t;
go
create table t(c int);
insert t(c) values (1);
go
Run Code Online (Sandbox Code Playgroud)
该表用于捕获测试结果,希望揭示内联函数与非内联函数之间的不同行为:
drop table if exists #Out;
go
create table #Out(Base int, Old int, New int);
go
Run Code Online (Sandbox Code Playgroud)
为了演示不同的行为,我希望在单个 SELECT 中执行两个函数,其中一个是内联的,另一个不是。该文件说:
标量 T-SQL UDF 可以是内联的,如果 .. UDF 不调用任何内部函数 .. 例如 GETDATE()
为了确保一个 UDF 不能内嵌,我添加了一个对 GETDATE 的引用。请注意,此附加语句在 UDF 的逻辑中没有任何作用,它只是抑制了内联。(确实,这个功能可以优化掉。也许未来的某个版本会实现这样的优化?)
create or alter function dbo.Old_skool()
returns int
as
begin
declare @tot int = 0;
declare @d date = GETDATE(); -- inhibits in-lining
select @tot = SUM(C) from t;
return @tot;
end
go
create or alter function dbo.New_kid_on_the_block()
returns int
as
begin
declare @tot int = 0;
select @tot = SUM(C) from t;
return @tot;
end
go
Run Code Online (Sandbox Code Playgroud)
为了引用我任意选择使用 SUM 的共享表。我相信,但还没有测试过,任何其他技术可以显示函数看到的行中的差异以及包含的 SELECT (MIN, MAX, TOP(1)) 也可以。
接下来我开始两个会话。第一个是执行 SELECT,第二个是对共享表进行并发写入。
-- Session 1 for reads
set transaction isolation level read committed;
GO
truncate table #Out;
declare @c int = 0;
while @c < 99 -- large enough to exhibit the behaviour
begin
insert #Out(Base, Old, New)
select
c,
dbo.Old_skool(),
dbo.New_kid_on_the_block()
from t;
set @c += 1;
end
-- Session 2 for writes
declare @c int = 0;
while @c < 99999
begin
update t set c = c + 1;
set @c += 1;
end
Run Code Online (Sandbox Code Playgroud)
我设置运行会话执行写入。在我的机器上,它运行了大约 24 秒,这是切换到会话 1(读取)并启动它的充足时间。
对于一次超过 99 个 SELECT 的运行,有 12 个实例,其中内嵌和传统执行机制返回不同的结果。在每种情况下,内联函数都返回与包含查询相同的结果(这并不是说这个测试表明这种行为是有保证的)。
Base Old New
----------- ----------- -----------
1801 1802 1801
1803 1804 1803
1814 1815 1814
1841 1842 1841
1856 1857 1856
1857 1858 1857
1860 1861 1860
1861 1862 1861
1864 1865 1864
1883 1884 1883
1884 1885 1884
1890 1891 1890
Run Code Online (Sandbox Code Playgroud)