使用Replace和Length检查避免SQL Not IN

我有一种情况,我必须动态创建我的SQL字符串,我试图尽可能使用参数和sp_executesql,所以我可以重用查询计划。 在进行大量的在线阅读和个人体验时,我发现“NOT IN”和“INNER / LEFT JOIN”是表现缓慢且基本(最左侧)表很大(1.5M行,50列) )。 我也读过,应该避免使用任何类型的函数,因为它会减慢查询速度,所以我想知道哪个更糟糕?

我过去曾经使用过这种解决方法,虽然我不确定这是最好的办法,但是避免在项目列表中使用“NOT IN”时,例如我传入3个字符串的列表例如,使用管道分隔符(仅在元素之间):

LEN(@param1) = LEN(REPLACE(@param1, [col], '')) 

代替:

 [col] NOT IN('ABD', 'RDF', 'TRM', 'HYP', 'UOE') 

…想象一下字符串列表的长度是1到大约80个可能的值,并且这种方法也不能将它自己用于paraterization。

在这个例子中,我可以使用“=”表示NOT IN,我会使用传统的列表技术作为我的IN,或者!=如果这是一个更快,虽然我怀疑它。 这比使用NOT IN快吗?

作为一种可能的第三种选择,如果我知道所有其他可能性(IN可能性,可能是80-95倍更长的列表)并且通过那些可能性,那该怎么办呢? 这将在应用程序的业务层中完成,以便将工作负载从SQL Server中删除。 查询计划重用不是很好的可能性,但如果它在一个大讨厌的查询中刮了一两秒,为什么不是。

我也擅长SQL CLR函数创建。 由于以上是字符串操作,CLRfunction最好吗?

思考?

在此先感谢任何和所有的帮助/建议/等。

正如唐纳德·克努特经常(误)引用的那样,“过早优化是所有邪恶的根源”。
所以,首先,你确定如果你以最清晰简单的方式编写代码(写入和读取),它执行得很慢吗? 如果没有,请检查它,然后再开始使用任何“聪明”的优化技巧。

如果代码很慢,请彻底检查查询计划。 大多数情况下,查询执行所需的时间比查询编译要长得多,因此通常您不必担心查询计划的重用。 因此,构建最佳索引和/或表结构通常会比调整查询构建方式提供更好的结果。

例如,我严重怀疑使用LEN和REPLACE的查询具有比NOT IN更好的性能 – 在任何一种情况下都将扫描所有行并检查匹配。 对于足够长的列表,MSSQL优化器会自动创建临时表以优化相等性比较。
更重要的是,这样的技巧往往会引入错误:比如说,如果[col] =’AB’,你的例子将无法正常工作。

IN查询通常比NOT IN更快,因为对于IN查询,只有部分行足以被检查。 该方法的效率取决于您是否能够足够快地获得IN的正确列表。

说到将可变长度列表传递给服务器,SO和其他地方有很多讨论。 通常,您的选择是:

  • 表值参数(仅限MSSQL 2008+),
  • 动态构造的SQL(容易出错和/或不安全),
  • 临时表(适用于长列表,可能在写入时占用过多的开销,而对于短列表则执行时间),
  • 分隔字符串(适用于’表现良好’值的短列表 – 比如少数整数),
  • XML参数(有点复杂,但效果很好 – 如果你使用一个好的XML库而不是’手工构建复杂的XML文本’)。

这篇文章对这些技术有了很好的概述,还有一些。

我发现“NOT IN”和“INNER / LEFT JOIN”是表现缓慢且基本(最左侧)表很大时价格昂贵

如果正确索引表,它应该不会很慢。 如果你有一个从属子查询,那么可以使查询变慢的东西。 也就是说,必须为表中的每一行重新计算查询,因为子查询引用外部查询中的值。

我还读过,应该避免使用任何类型的函数,因为它会减慢查询速度

这取决于。 SELECT function(x) FROM ...可能不会对性能产生巨大影响。 问题是当您在查询中的其他位置使用列的函数时,例如JOIN条件,WHERE子句或ORDER BY,因为它可能意味着无法使用索引。 但是,常数值的函数不是问题。

关于你的查询,我首先尝试使用[col] NOT IN ('ABD', 'RDF', 'TRM', 'HYP', 'UOE') 。 如果这很慢,请确保已正确索引表。

首先,由于你只是过滤了一小部分记录,因此很可能根本没有使用col的索引,因此SARG能力没有实际意义。

这使得查询计划重用。

  • 如果您使用的是SQL Server 2008,请将@param1替换为表值参数,并让应用程序传递该参数而不是分隔列表。 这完全解决了您的问题。

  • 如果您使用的是SQL Server 2005,我认为这并不重要。 您可以拆分分隔列表并对表使用NOT IN / NOT EXISTS ,但如果您不在col搜索索引,那又有什么意义呢?

谁能说到最后一点? 将列表拆分为表格var然后反加入它可以节省足够的CPU周期来抵消设置成本吗?

编辑 ,使用XML的SQL Server 2005的第三种方法,灵感来自OMG Ponies的链接:

 DECLARE @not_in_xml XML SET @not_in_xml = N'ABDRDF' SELECT * FROM Table1 WHERE @not_in_xml.exist('/values/value[text()=sql:column("col")]') = 0 

与分隔列表或TVP相比,我不知道它的表现如何。