SQL Server性能不佳,存在大量ORs,并且使用UPPER()重复条件
软件会生成许多这样的非最佳查询:SQL Server性能不佳,存在大量ORs,并且使用UPPER()重复条件,sql,sql-server,tsql,query-performance,Sql,Sql Server,Tsql,Query Performance,软件会生成许多这样的非最佳查询: SELECT <List of Columns> FROM <Table> WHERE( ([COL1] = UPPER('CONST_VALUE') AND [COL2] = UPPER('v1')) OR ([COL1] = UPPER('CONST_VALUE') AND [COL2] = UPPER('v2')) OR ([COL1] = UPPER('CONST_VALUE') AND
SELECT
<List of Columns>
FROM <Table>
WHERE(
([COL1] = UPPER('CONST_VALUE') AND [COL2] = UPPER('v1')) OR
([COL1] = UPPER('CONST_VALUE') AND [COL2] = UPPER('v2')) OR
([COL1] = UPPER('CONST_VALUE') AND [COL2] = UPPER('v4')) OR
([COL1] = UPPER('CONST_VALUE') AND [COL2] = UPPER('v6')) OR
([COL1] = UPPER('CONST_VALUE') AND [COL2] = UPPER('v8')) OR
<...>
)
选择
从…起
在哪里(
([COL1]=上限('CONST_VALUE')和[COL2]=上限('v1'))或
([COL1]=上限('CONST_VALUE')和[COL2]=上限('v2'))或
([COL1]=上限('CONST_VALUE')和[COL2]=上限('v4'))或
([COL1]=上限('CONST_VALUE')和[COL2]=上限('v6'))或
([COL1]=上限('CONST_VALUE')和[COL2]=上限('v8'))或
)
执行计划:
执行此查询将导致索引查找,执行该查找大约需要1s时间。将查询重构为以下语句将导致3ms的执行时间:
SELECT
<List of Columns>
FROM <Table>
WHERE([COL1] = UPPER('CONST_VALUE') AND (
[COL2] = UPPER('v1') OR
[COL2] = UPPER('v2') OR
[COL2] = UPPER('v4') OR
[COL2] = UPPER('v6') OR
[COL2] = UPPER('v8') OR
<...>
))
选择
从…起
式中([COL1]=上限('CONST_VALUE'),以及(
[COL2]=上部('v1')或
[COL2]=上部('v2')或
[COL2]=上部('v4')或
[COL2]=上部('v6')或
[COL2]=上部('v8')或
))
这些索引在afaik中看起来是最优的,COL1和COL2上的索引包括所有选定的其他列。既然我们现在不能更改软件,有没有办法加快执行时间?添加不同类型的索引。我也在考虑类似查询重写之类的事情,但在SQL Server中找不到这样的事情。如果您能够对查询进行更改,那么请删除
上限
-如果您使用不区分大小写的排序规则(目前最常见的情况),可以直接删除此选项-否则,您需要添加逻辑,以确保在将值添加到查询之前将其大写UPPER
不是常数折叠,可能会给出比下面各种示例中所示的简单字符串文字更糟糕的计划
示例数据
CREATE TABLE [Table]
(
[COL1] VARCHAR(20),
[COL2] VARCHAR(10),
PRIMARY KEY ([COL1],[COL2])
)
INSERT INTO [Table]
SELECT TOP 100 'CONST_VALUE', CONCAT('v', ROW_NUMBER() OVER (ORDER BY @@SPID))
FROM sys.all_columns
查询1
SELECT *
FROM [Table]
WHERE(
([COL1] = 'CONST_VALUE' AND [COL2] = 'V1') OR
([COL1] = 'CONST_VALUE' AND [COL2] = 'V1') OR
([COL1] = 'CONST_VALUE' AND [COL2] = 'V4')
)
此操作的执行计划具有索引查找运算符。查看计划的属性表明,seek实际上包含两个不同的多列seek谓词(不是三个seek。执行两次“V1”seek并返回两次这些行是错误的,即使它在WHERE
子句中出现两次)
查询2
SELECT *
FROM [Table]
WHERE(
([COL1] = 'CONST_VALUE' AND [COL2] = UPPER('v1')) OR
([COL1] = 'CONST_VALUE' AND [COL2] = UPPER('V1')) OR
([COL1] = 'CONST_VALUE' AND [COL2] = UPPER('v2'))
)
此执行计划看起来很有希望,但仔细检查后,查找仅在单列COL1
-因为表中的所有行都具有值“CONST\u value”
。在这种情况下,查找没有任何结果,所有工作都是通过剩余谓词完成的
查询3
SELECT *
FROM [Table] WITH (FORCESEEK)
WHERE(
([COL1] = 'CONST_VALUE' AND [COL2] = UPPER('v1')) OR
([COL1] = 'CONST_VALUE' AND [COL2] = UPPER('V1')) OR
([COL1] = 'CONST_VALUE' AND [COL2] = UPPER('v2'))
)
这与前面的相同,但添加了FORCESEEK
提示。由于某种原因,UPPER
的结果在编译时不是常数折叠的,因此它向计划中添加了额外的运算符来计算UPPER
,然后将相同的结果折叠起来,以执行两个所需的多列索引搜索
查询4
SELECT *
FROM [Table]
WHERE(
([COL1] = UPPER('CONST_VALUE') AND [COL2] = UPPER('v1')) OR
([COL1] = UPPER('CONST_VALUE') AND [COL2] = UPPER('V1')) OR
([COL1] = UPPER('CONST_VALUE') AND [COL2] = UPPER('v2'))
)
现在SQLServer放弃了,只进行了一次扫描
问题5
此重写给出了与查询2相同的执行计划-在Col1
上进行搜索,在Col2
上进行剩余谓词,这对我的示例数据不有用,但在更实际的情况下会有用
问题6
选择*
从sys.all_对象
其中“v1”“v1”
SQLServer在编译时检测到矛盾,并给出了一个非常简单的计划
问题7
选择*
从sys.all_对象
其中上('v1')上('v1'))
尽管表达式是确定性的,并且具有完全相同的输入值,但不会发生矛盾检测
问题在于,在这种情况下,查询优化器不能做很多事情。您陷入了与中类似的陷阱,有许多参数->回退是表扫描
设置一个表变量,其中包含比较值、2个字段和主键,然后您可以在联接中使用该表变量,主键上的索引为查询优化器提供统计信息。您可以在操作符中使用
。类似于其中col1='const_value'和col2 in('v1','v2','v3')
。它可读性更好,但内部编译到第二个示例中。如果无法更改查询,则您的选项将受到限制。你在COL上试过索引吗!和COL2中包含的列?您是否检查了实际执行计划?有关在问题中包含执行计划的方法,请参阅。索引统计数据是最新的吗?有关更多信息,请参阅。使用UPPER
会导致问题,否则会将谓词精细地折叠。出于某种原因,它不会对该函数进行常数折叠,可能只是将其视为对UPPER('000049')
的不同引用可能不会对同一事物求值。问题是,如果没有常数折叠,它就不能盲目地重复搜索。UPPER('a')和UPPER('a')的计算结果相同,但不应将匹配的行加倍。因此,它需要一个步骤来消除重复。手动重写会以合并间隔完成。不确定当两列可能不同时,此选项是否不可用
SELECT *
FROM [Table]
WHERE [COL1] = UPPER('CONST_VALUE') AND
(
[COL2] = UPPER('v1') OR
[COL2] = UPPER('V1') OR
[COL2] = UPPER('v2')
)
SELECT *
FROM sys.all_objects
where 'v1' <> 'v1'
SELECT *
FROM sys.all_objects
where UPPER('v1') <> UPPER('v1')