我们正在优化看起来设计不佳的查询。 有一个到多行表值函数的联接,该联接很容易成为内联函数,因此我们对其进行了更新..但是查询变得慢得多。 多行函数返回一个以单列为主键的临时表,而内联TVF刚返回一个直的单列表。

进行一些研发,我们发现使用多行TVF作为子句可以显着提高性能。 以下示例可以在任何数据库上运行,最好具有悠久的对象更新历史。 我们认为第一个查询是最好的设计,但是第二个查询的性能要好得多(通常快20倍)。 这实际上是查询成本,而不是速度

create function udf_ObjectsModifiedBetweenDates
(
    @DateFrom datetime,
    @Dateto datetime
)
returns @t table(object_id int primary key)
as
begin
insert into @t
(
    object_id
)
select object_id
from sys.objects 
where modify_date between @DateFrom and @Dateto
return
end

GO

declare @datefrom datetime = '2017-05-01' --Please adjust these dates to get good sample
declare @dateto datetime = '2018-08-02'

--slow
select object_id, parent_object_id, is_ms_shipped
from sys.objects
where modify_date between @datefrom and @dateto option (recompile)

--fast
select o.object_id, o.parent_object_id, o.is_ms_shipped
from sys.objects o
inner join udf_ObjectsModifiedBetweenDates(@datefrom, @dateto) ombd on o.object_id = ombd.object_id option (recompile)

函数和查询与我们在系统上处理的函数和查询类似,但是在这里我们用系统表代替了用户表。 我们已经在其他用户表上尝试了同样的方法,再次从MTVF中获得了巨大收益。 执行计划表明,在TVF上存在针对较快查询的聚集索引扫描。 谁能解释正在发生的事情,这是否是提高查询性能的可行方法?

在此处输入图片说明

#1楼 票数:1 已采纳

似乎您在执行计划中误将“查询成本(相对于批次)”解释为“查询成本(查询将花费多长时间运行)”。 查询的成本与查询的效率或速度几乎没有关系。

我在以下两个条件上运行了该查询(更改日期):

SET STATISTICS IO ON;
SET STATISTICS TIME ON;

对于标记为“慢”的查询(没有UDF的查询),结果为:

The "slow" one

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.
SQL Server parse and compile time: 
   CPU time = 8 ms, elapsed time = 8 ms.

(19305 rows affected)
Table 'sysschobjs'. Scan count 1, logical reads 379, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row affected)

 SQL Server Execution Times:
   CPU time = 62 ms,  elapsed time = 61 ms.

对于标记为“快速”(UDF)的那个,输出为:

The "fast" one

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 7 ms.

(19305 rows affected)
Table 'sysschobjs'. Scan count 0, logical reads 38610, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table '#B1BBD6DD'. Scan count 1, logical reads 34, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row affected)

 SQL Server Execution Times:
   CPU time = 266 ms,  elapsed time = 329 ms.

从这些统计数据可以看出,到目前为止,非UDF版本是赢家。 非UDF的执行时间快了5倍以上,但是UDF查询的IO太疯狂了(读取次数增加了1000倍以上)。

老实说,多行表值函数通常比性能杀手更重要。 在3种类型的函数(内联表值,标量值和多行表值)中,它们可能是最慢的,而内联通常是最快的。 在传闻中,我听说标量函数在SQL Server 2019中是“更好”的,但是,我还没有进行自我测试,也没有看到任何实际证据。 只是在交谈中。

  ask by cloudsafe translate from so

未解决问题?本站智能推荐:

2回复

更改表'DEFAULT'未设置默认值 - SQL Server

我正在尝试更改现有列并添加整数列。 在添加整数列时,我将DEFAULT设置为10,但在添加列时默认不设置 我需要使用显式更新脚本将所有过去的记录设置为默认值 如果列为“非空”,则所有记录的默认值为10。 为什么可以为空的列没有设置默认值? 在我的实际表中,我有接近850
2回复

SQL查询-数据透视分组或任何获取我的结果表的概念

我有一个名为#MyTable的表。 MyTable的 我的输出应该是这样的 不确定在SQL查询中是否可行。 我尝试使用SQL Pivot,但没有锻炼。 请提出编写查询的建议以获得此结果。
1回复

如何提高另一个表中按日期时间字段排序的速度?

我有一个包含数十万条记录的产品表。 基本的表格结构如下: 我还有一个用于对象的表,该表存储有关不同对象的信息,但是主要存储对象的创建者和创建时间。 以下查询选择“产品”,运行时间为半秒: 以下查询耗时将近3.5秒 Products表中的ObjectID已设置为O
31回复

如何检查 SQL Server 表中是否存在列?

如果它不存在,我需要添加一个特定的列。 我有类似以下内容,但它总是返回 false: 如何检查 SQL Server 数据库表中是否存在列?
1回复

如何在具有数百万行的表上有效地获取最新的插入时间戳

我对 SQL 中的表设计/查询效率有疑问。 我有两个表,表 A 包含客户端列表,表 B 包含客户端 ID 和最后一次收到来自客户端的消息。 客户端的数量在不断增长,以 1000 为单位,每个客户端至少每分钟发送一次消息,有时更多,有时更少,但平均来说就是这样。 表 B 增长得相当快。 问题是
1回复

SQL Server中的分区或索引大表

我有一个包含40亿行和50列的大表,其中大多数是datetime或numeric除了一些是varchar 。 数据将每周插入表格(约2000万行)。 我希望查询一些datetime列的where子句和几个varchar列。 表中没有主键。 没有索引,表也没有分区。 我正在使
3回复

SQL用群集列存储索引替换所有表

我们正在进行一个迁移项目,并希望将大型数据仓库的大多数行存储索引替换为集群列存储索引。 我们在标识列上添加了唯一索引。 有没有人有脚本来更改所有100多个表的运行,并用Columnstore Index替换主键聚集索引? 测试以查看列存储索引是否在迁移时有助于调整性能。 *顺便说一句,是否在
1回复

索引以检查非常大的表在特定列中是否包含确切的字符串

我有一个非常大的SQL表(约5亿行) 我想在表中插入新记录,但是首先我需要确保记录不存在。 我实质上是用要插入的值( @TestDefinitionInput -TVP)构建一个表,然后将其传递到存储过程中。 我的问题是,这确实有5亿行。 我确实不是很精通SQL,并且想知