【问题标题】:How to optionally apply a WHERE IN clause based on a variable?如何根据变量选择性地应用 WHERE IN 子句?
【发布时间】:2019-07-30 23:05:56
【问题描述】:

我有一个存储过程,需要按以逗号分隔的列表(即'1,2,3')形式传递的 id 列表进行过滤。

我想应用一个 WHERE IN 子句来匹配这些 ID,但前提是变量包含任何内容 (IS NOT NULL AND <> '')。

这是一个简化的问题:http://sqlfiddle.com/#!18/5f6be/1

它目前适用于单个和多个 ID。但是当传递 ''NULL 时,它应该返回所有内容,但它没有返回任何内容。

CTE 和分页的东西是有原因的,请提供一个不会改变它的解决方案。

【问题讨论】:

标签: sql sql-server azure-sql-database sql-server-2016 where-in


【解决方案1】:

使用DelimitedSplit8k_lead,您可以通过以下方式实现:

CREATE PROC YourProc @List varchar(8000) = NULL AS
BEGIN

    SELECT {YourColumns}
    FROM YourTable YT
         OUTER APPLY dbo.DelimitedSplit8k_Lead(@List,',') DS
    WHERE DS.item = YT.YourColumn
       OR NULLIF(@List,'') IS NULL
    OPTION (RECOMPILE);
END

使用了OUTER APPLY,就像传递了NULL一样,数据集不会被淘汰。 RECOMPILE 在那里,因为它变成了一个“Catch-all 查询”,并添加了处理 NULL

【讨论】:

  • 我在本地使用 SQL 2016 和 SQL Azure。可以使用 STRING_SPLIT 代替自定义 UDF 吗?
  • 太棒了。这有效(使用 STRING_SPLIT)。另外,我不需要 OPTION (RECOMPILE) 来处理 '' 和 NULL 情况。
  • 是的,@emzero,这引出了一个问题,即为什么要使用 WHILE 来进一步拆分字符串? WHILE 是一种非常缓慢的方法来实现您所追求的目标。
  • @emzero 不要删除 RECOMPILE 选项,如果这是一个 SP。我把它放在那里是有充分理由的。除非您希望将来表现不佳?
  • @emzero,当下一次执行将有一个不同数量的参数(或根本没有参数),这将导致次优的执行计划。不要丢弃它。
【解决方案2】:

为什么不在WHERE 子句中使用JOIN 而不是子查询。

set @userIds = nullif(ltrim(@userIds),'')
select u.*
from Users u
left join string_split(@userIds,',') s on u.Id=s.value
where s.value is not null or @userIds is null

【讨论】:

【解决方案3】:

老派方法:

WHERE
(@userIds IS NULL OR @userIds = '' OR U.Id IN (SELECT * FROM STRING_SPLIT(@userIds, ',')))

在末尾添加 OPTION (RECOMPILE) 以使其正常工作并调整计划。


编辑: 基于cmets,这个生成两个表扫描。它对我的 LocalDb 设置没有任何影响,但无论如何都不要依赖它。

WHERE U.Id IN
(
   SELECT * FROM STRING_SPLIT(@userIds, ',')
   UNION ALL
   SELECT U.Id WHERE NULLIF(@userIds, '') IS NULL
)

【讨论】:

  • 重复上面的评论,OPTION(RECOMPILE) 用于阻止查询引擎缓存针对一组参数(或根本没有参数)优化的查询计划,当下一次执行将具有不同数量的参数(或根本没有参数),这将导致次优的执行计划。不要丢弃它。
  • 如果您想获得更好的执行计划,这是必要的。通常这种类型的查询可以/应该分成两个单独的过程,一个主过程根据参数调用正确的过程。这样,每个过程都可以保留一个非常适合当前结果集的缓存执行计划。
  • 此方法还会导致对表进行 2 次扫描,而不是 1 次,这可能会导致性能下降。
猜你喜欢
  • 2019-09-12
  • 1970-01-01
  • 2019-04-10
  • 2014-11-16
  • 1970-01-01
  • 2020-06-30
  • 1970-01-01
  • 2011-08-16
  • 2016-11-21
相关资源
最近更新 更多