Mat*_*att 5 sql t-sql sql-server
也就是说,为什么这样:
select *
from tableA
/* Bunch of inner joins */
where
/* Bunch of clauses */
and (
exists (
select *
from tableB, tableC, tableD
where (tableB.fieldNameA = 'foo') and
/* More clauses */
) or
exists (
select *
from tableB, tableC, tableD
where (tableB.fieldNameA = 'bar') and
/* More clauses */
)
)
Run Code Online (Sandbox Code Playgroud)
跑得快近500倍?
select *
from tableA
/* Bunch of inner joins */
where
/* Bunch of clauses */
and exists (
select *
from tableB, tableC, tableD
where (tableB.fieldNameA = 'foo' or tableB.fieldNameA = 'bar') and
/* More clauses */
)
Run Code Online (Sandbox Code Playgroud)
我喜欢不重复代码的想法,所以想巩固到第二个版本.它们都产生相同的结果集,但是第一次运行得更快,我不能不使用它.思考?
我一直在研究查询计划,但并没有真正得到它的底部.我注意到的一件事是,对于一个考虑因素的例子,Clustered Index Seek at [tree].[PK__tree__09746778]的实际行数接近93,000.另一个例子并不接近线数这么高.这是文本输出 - 不确定这是多么有用.
第一个例子(未定义)产生:
|--Nested Loops(Left Semi Join, OUTER REFERENCES:([initialCategory].[inode]))
|--Nested Loops(Inner Join, OUTER REFERENCES:([OPUSDev2].[dbo].[tree].[child]))
| |--Hash Match(Inner Join, HASH:([OPUSDev2].[dbo].[course].[inode])=([OPUSDev2].[dbo].[tree].[parent]), RESIDUAL:([OPUSDev2].[dbo].[course].[inode]=[OPUSDev2].[dbo].[tree].[parent]))
| | |--Nested Loops(Inner Join, OUTER REFERENCES:([OPUSDev2].[dbo].[section].[inode], [Expr1037]) WITH UNORDERED PREFETCH)
| | | |--Nested Loops(Inner Join, OUTER REFERENCES:([OPUSDev2].[dbo].[course_term].[inode], [Expr1036]) WITH UNORDERED PREFETCH)
| | | | |--Hash Match(Inner Join, HASH:([OPUSDev2].[dbo].[course].[course_number])=([OPUSDev2].[dbo].[course_term].[course_number]), RESIDUAL:([OPUSDev2].[dbo].[course_term].[course_number]=[OPUSDev2].[dbo].[course].[course_number]))
| | | | | |--Clustered Index Scan(OBJECT:([OPUSDev2].[dbo].[course].[PK__course__31B762FC]), WHERE:(CONVERT_IMPLICIT(tinyint,[OPUSDev2].[dbo].[course].[show_on_web],0)=(1) AND CONVERT_IMPLICIT(tinyint,[OPUSDev2].[dbo].[course].[show_on_nav],0)=(1)))
| | | | | |--Hash Match(Inner Join, HASH:([OPUSDev2].[dbo].[term].[term_id])=([OPUSDev2].[dbo].[course_term].[term_id]))
| | | | | |--Clustered Index Scan(OBJECT:([OPUSDev2].[dbo].[term].[PK__term__0697FACD]), WHERE:(CONVERT_IMPLICIT(tinyint,[OPUSDev2].[dbo].[term].[active_on_web],0)=(1)))
| | | | | |--Clustered Index Scan(OBJECT:([OPUSDev2].[dbo].[course_term].[course_term_pk]))
| | | | |--Index Seek(OBJECT:([OPUSDev2].[dbo].[section].[section_idx2]), SEEK:([OPUSDev2].[dbo].[section].[course_term_inode]=[OPUSDev2].[dbo].[course_term].[inode]) ORDERED FORWARD)
| | | |--Clustered Index Seek(OBJECT:([OPUSDev2].[dbo].[section].[PK__section__7B264821]), SEEK:([OPUSDev2].[dbo].[section].[inode]=[OPUSDev2].[dbo].[section].[inode]), WHERE:([OPUSDev2].[dbo].[section].[status]='Active') LOOKUP ORDERED FORWARD)
| | |--Clustered Index Scan(OBJECT:([OPUSDev2].[dbo].[tree].[PK__tree__09746778]))
| |--Clustered Index Seek(OBJECT:([OPUSDev2].[dbo].[category].[PK__category__2739D489] AS [initialCategory]), SEEK:([initialCategory].[inode]=[OPUSDev2].[dbo].[tree].[child]), WHERE:([OPUSDev2].[dbo].[category].[active] as [initialCategory].[active]=(1)) ORDERED FORWARD)
|--Concatenation
|--Nested Loops(Inner Join)
| |--Clustered Index Seek(OBJECT:([OPUSDev2].[dbo].[category].[PK__category__2739D489] AS [childCategory]), SEEK:([childCategory].[inode]=[OPUSDev2].[dbo].[category].[inode] as [initialCategory].[inode]) ORDERED FORWARD)
| |--Nested Loops(Inner Join, OUTER REFERENCES:([parentCategory].[inode]))
| |--Index Seek(OBJECT:([OPUSDev2].[dbo].[category].[idx_category_2] AS [parentCategory]), SEEK:([parentCategory].[category_key]='noncredit_subjects') ORDERED FORWARD)
| |--Clustered Index Seek(OBJECT:([OPUSDev2].[dbo].[tree].[PK__tree__09746778]), SEEK:([OPUSDev2].[dbo].[tree].[child]=[OPUSDev2].[dbo].[category].[inode] as [initialCategory].[inode] AND [OPUSDev2].[dbo].[tree].[parent]=[OPUSDev2].[dbo].[category].[inode] as [parentCategory].[inode]) ORDERED FORWARD)
|--Nested Loops(Inner Join)
|--Clustered Index Seek(OBJECT:([OPUSDev2].[dbo].[category].[PK__category__2739D489] AS [childCategory]), SEEK:([childCategory].[inode]=[OPUSDev2].[dbo].[category].[inode] as [initialCategory].[inode]) ORDERED FORWARD)
|--Nested Loops(Inner Join, OUTER REFERENCES:([parentCategory].[inode]))
|--Index Seek(OBJECT:([OPUSDev2].[dbo].[category].[idx_category_2] AS [parentCategory]), SEEK:([parentCategory].[category_key]='credit_subjects') ORDERED FORWARD)
|--Clustered Index Seek(OBJECT:([OPUSDev2].[dbo].[tree].[PK__tree__09746778]), SEEK:([OPUSDev2].[dbo].[tree].[child]=[OPUSDev2].[dbo].[category].[inode] as [initialCategory].[inode] AND [OPUSDev2].[dbo].[tree].[parent]=[OPUSDev2].[dbo].[category].[inode] as [parentCategory].[inode]) ORDERED FORWARD)
Run Code Online (Sandbox Code Playgroud)
第二个例子(因素)产生:
|--Nested Loops(Left Semi Join, OUTER REFERENCES:([initialCategory].[inode]))
|--Nested Loops(Inner Join, OUTER REFERENCES:([OPUSDev2].[dbo].[tree].[child]))
| |--Hash Match(Inner Join, HASH:([OPUSDev2].[dbo].[course].[inode])=([OPUSDev2].[dbo].[tree].[parent]), RESIDUAL:([OPUSDev2].[dbo].[course].[inode]=[OPUSDev2].[dbo].[tree].[parent]))
| | |--Nested Loops(Inner Join, OUTER REFERENCES:([OPUSDev2].[dbo].[section].[inode], [Expr1029]) WITH UNORDERED PREFETCH)
| | | |--Nested Loops(Inner Join, OUTER REFERENCES:([OPUSDev2].[dbo].[course_term].[inode], [Expr1028]) WITH UNORDERED PREFETCH)
| | | | |--Hash Match(Inner Join, HASH:([OPUSDev2].[dbo].[course].[course_number])=([OPUSDev2].[dbo].[course_term].[course_number]), RESIDUAL:([OPUSDev2].[dbo].[course_term].[course_number]=[OPUSDev2].[dbo].[course].[course_number]))
| | | | | |--Clustered Index Scan(OBJECT:([OPUSDev2].[dbo].[course].[PK__course__31B762FC]), WHERE:(CONVERT_IMPLICIT(tinyint,[OPUSDev2].[dbo].[course].[show_on_web],0)=(1) AND CONVERT_IMPLICIT(tinyint,[OPUSDev2].[dbo].[course].[show_on_nav],0)=(1)))
| | | | | |--Hash Match(Inner Join, HASH:([OPUSDev2].[dbo].[term].[term_id])=([OPUSDev2].[dbo].[course_term].[term_id]))
| | | | | |--Clustered Index Scan(OBJECT:([OPUSDev2].[dbo].[term].[PK__term__0697FACD]), WHERE:(CONVERT_IMPLICIT(tinyint,[OPUSDev2].[dbo].[term].[active_on_web],0)=(1)))
| | | | | |--Clustered Index Scan(OBJECT:([OPUSDev2].[dbo].[course_term].[course_term_pk]))
| | | | |--Index Seek(OBJECT:([OPUSDev2].[dbo].[section].[section_idx2]), SEEK:([OPUSDev2].[dbo].[section].[course_term_inode]=[OPUSDev2].[dbo].[course_term].[inode]) ORDERED FORWARD)
| | | |--Clustered Index Seek(OBJECT:([OPUSDev2].[dbo].[section].[PK__section__7B264821]), SEEK:([OPUSDev2].[dbo].[section].[inode]=[OPUSDev2].[dbo].[section].[inode]), WHERE:([OPUSDev2].[dbo].[section].[status]='Active') LOOKUP ORDERED FORWARD)
| | |--Clustered Index Scan(OBJECT:([OPUSDev2].[dbo].[tree].[PK__tree__09746778]))
| |--Clustered Index Seek(OBJECT:([OPUSDev2].[dbo].[category].[PK__category__2739D489] AS [initialCategory]), SEEK:([initialCategory].[inode]=[OPUSDev2].[dbo].[tree].[child]), WHERE:([OPUSDev2].[dbo].[category].[active] as [initialCategory].[active]=(1)) ORDERED FORWARD)
|--Nested Loops(Inner Join)
|--Clustered Index Seek(OBJECT:([OPUSDev2].[dbo].[category].[PK__category__2739D489] AS [childCategory]), SEEK:([childCategory].[inode]=[OPUSDev2].[dbo].[category].[inode] as [initialCategory].[inode]) ORDERED FORWARD)
|--Nested Loops(Inner Join, OUTER REFERENCES:([OPUSDev2].[dbo].[tree].[parent]))
|--Clustered Index Seek(OBJECT:([OPUSDev2].[dbo].[tree].[PK__tree__09746778]), SEEK:([OPUSDev2].[dbo].[tree].[child]=[OPUSDev2].[dbo].[category].[inode] as [initialCategory].[inode]) ORDERED FORWARD)
|--Index Seek(OBJECT:([OPUSDev2].[dbo].[category].[idx_category_2] AS [parentCategory]), SEEK:([parentCategory].[category_key]='credit_subjects' AND [parentCategory].[inode]=[OPUSDev2].[dbo].[tree].[parent] OR [parentCategory].[category_key]='noncredit_subjects' AND [parentCategory].[inode]=[OPUSDev2].[dbo].[tree].[parent]) ORDERED FORWARD)
Run Code Online (Sandbox Code Playgroud)
确定速度差异的唯一方法是查看每个查询的查询计划,并查看优化程序的不同操作.根据经验,其他人发现将OR条款更改为UNION子句有助于优化器更好地使用索引,从而缩短查询执行时间.
本文在这里给出了一个很好的解释,一些影响查询计划,包括选择性的变量.
为什么UNION会导致更多的搜索而不是扫描,因为每个操作都需要满足一定的选择性要求才有资格进行搜索.(选择性是被过滤的特定列的唯一性).OR在单个操作中发生,因此当组合每个列的选择性并且它超过一定百分比时,则认为扫描更有效.
您似乎认为以下块有所不同:
where (table.fieldNameA = 'foo' or table.fieldNameA = 'bar')
Run Code Online (Sandbox Code Playgroud)
由于UNION默认为每个语句执行单独的操作,因此不会合并每列的选择性,从而使其更有可能执行搜索.现在,由于UNION执行两个操作,因此需要使用上面的连接操作匹配其结果集.通常,这不是昂贵的操作.
为什么我在UNION这里提到的原因是因为优化的SQL与a的行为非常相似UNION.
exists (
select *
from tableB, tableC, tableD
where (table.fieldNameA = 'foo') and
/* More clauses */
) or
exists (
select *
from tableB, tableC, tableD
where (table.fieldNameA = 'bar') and
/* More clauses */
)
Run Code Online (Sandbox Code Playgroud)
您有两个单独的子查询,每个子查询可以并行化,然后在完成时连接在一起.这些子查询中的每一个也具有更好的选择性比率,鼓励使用搜索而不是扫描.
您的(更新的)查询计划支持此功能.在您的第一个计划中,在底部,您有:
Concatenation
|--Nested Loops(Inner Join)
| |--Clustered Index Seek
| |--Nested Loops
| |--Index Seek
| |--Clustered Index Seek
|--Nested Loops(Inner Join)
|--Clustered Index Seek
|--Nested Loops
|--Index Seek
|--Clustered Index Seek
Run Code Online (Sandbox Code Playgroud)
显示OR条件已分成两个可并行化的独立作业,索引搜索很可能表现更好,因为选择性将得到改善.
每当您的查询性能不佳时,都需要查看查询计划以查看哪些子部分需要很长时间,并尝试先修复这些部分.与您优化程序性能的方式非常相似.
由于许多变量会影响查询计划,例如表中的行数,可用的索引和某些列的选择性,因此查询很难通过眼睛进行优化.