T-SQL。实现“ any” where语句的最佳方法是什么?

时间:2019-03-13 21:04:06

标签: sql sql-server tsql

想象一下,您有一个状态表的订单:

Id (PK)
Status (INT) 
... 

有一个用户界面(UI),业务用户可以在其中选择感兴趣的订单的状态。神奇的数字“ -1”表示-用户对每种状态都感兴趣

实现查询的最佳方法是什么?

1. select * from orders where status = @user-selected-status-id or @user-selected-status-id = -1

2. select * from orders where status = ISNULL(NULLIF(@user-selected-status-id, -1), status) 

3. Dynamic sql approach with string concatenation "where" operator is joined only if required (EXEC sp_executesql at the very end) 

我对所有三种方法都使用了“执行计划”,它告诉我们,对于动态sql来说,估计成本是最好的选择,然后isnull / nullif和“ or”是局外人。

我曾经认为“或”方法的优胜之处在于它的简洁性isull / nullif方法

让我感到更开心的是-结果(估计)取决于订单表的大小。当它增长十倍时(从10K-> 100K行),局外人就是“动态SQL”。

我可以相信吗?

1 个答案:

答案 0 :(得分:3)

盖尔·肖(Gail Shaw)在这方面做了几篇很棒的文章:Catch-all QueriesRevisting Catch-all Queries

这些内容的摘要是,您是在2008年,然后使用参数化动态语句。如果您使用的是SQL Server 2012+,则使用WHERE Column = @Variable OR @Variable IS NULL并在末尾添加OPTION (RECOMPILE)

但是,如果在生成查询计划方面确实有昂贵的查询,那么您可能仍要使用动态SQL。然后,只要经常调用它们,每个语句都会有自己的缓存计划。

尽管如此,但不要使用status = ISNULL(NULLIF(@user-selected-status-id, -1), status)。该子句是非SARGable的,并且性能最差。

因此,对于采用非高复杂度查询计划的生成方式,2012 +方法应为:

SELECT {Columns}
FROM YourTable YT
WHERE (YT.Column = @Variable
   OR  @Variable IS NULL)
OPTION (RECOMPILE);

OPTION (RECOMPILE)很重要,因为它会使数据引擎在每次运行查询时重新创建查询计划(和估计值)。如果您有一个查询,其中@Variable有一个值,那么将要返回的行数将分别估计为“低”。另一方面,对于@Variable值为NULL的查询,这意味着将返回行(在此简单查询中);或“高”行数。 SQL Server缓存计划,这意味着数量少时的计划就不合适。错误的估计可能(并且确实)意味着性能不佳,因为事情可能会从tempdb中的RAM中溢出,并且资源时间不是预期的。强制数据引擎重新创建计划意味着将为每次运行重新创建这些估计(这确实是有代价的),但对于正在运行的查询会更好。只要您的统计信息也是最新的。

但是,如果您使用的是SQL Server 2008,则您无权访问OPTION (RECOMPILE),因此您需要沿着动态路线走下去:

DECLARE @SQL nvarchar(MAX);

SET @SQL = N'SELECT {Columns}' + NCHAR(13) + NCHAR(10) +
           N'FROM YourTable' + NCHAR(13) + NCHAR(10) +
           CASE WHEN @Variable IS NOT NULL THEN N'WHERE YT.Column = @Variable' ELSE '' END + N';';

EXEC sp_executesql @SQL, N'@Variable {DataType}', @Variable = @Variable;