Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
SQL Server性能不佳,存在大量ORs,并且使用UPPER()重复条件_Sql_Sql Server_Tsql_Query Performance - Fatal编程技术网

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')