SQL Sever查询联接优化

时间:2018-07-19 09:44:38

标签: sql sql-server join optimization

我已经在网上寻找答案,但是找不到确切的答案。例如,您有2个连接子句:

1。

JOIN T2 ON T1.[ID] = T2.[ID]

2。

JOIN T2 ON T1.[ID] = REPLACE(T2.[ID],'A', '')

由于连接子句的功能,现在第二个程序的性能更差。确切原因是什么?

例如,如果此代码在存储过程中,那么最好对其进行优化?要删除替换功能并将其添加到表级别,以便所有这些操作都在任何联接之前完成?

任何建议或指向更多信息的链接都将非常有用。谢谢

3 个答案:

答案 0 :(得分:3)

在第二个示例中,您尝试在T2中查找记录-但不是将值作为T1.ID值,而是将函数应用于T2.ID-REPLACE(T2.[ID],'A', '')

如果您在T2.ID上有索引-充其量只能扫描该索引而不查找它-从而导致性能差异。

这是很难解释的地方-索引存储为表中T2.ID值的b +树。索引了解该字段并可以对其进行搜索/排序,但是它不了解应用于该字段的任何逻辑。

不知道REPLACE('A123','A', '') = 123-不对索引中的值执行函数并检查结果是否相等。

AAA123也将等于1A23、12A3、123A等,实际上有不计其数的组合会匹配-但是唯一能够确定单个索引条目是否匹配的方法是运行该组合通过该函数的值,然后检查是否相等。

如果仅在通过函数运行索引值时才能弄清楚-如果它对索引中的每个条目都执行了查询,则只能正确回答查询。每个条目的索引扫描,传递给函数并检查输出。

正如Jeroen所说的,该术语是SARGable或SARGability,S搜索ARG ument ABLE,尽管我个人更喜欢将其解释为S eek ARG ument ABLE,因为它与查询计划运算符更接近。

应该注意的是,此概念与联接无关,SQL中的任何谓词都有此限制-具有where谓词的单个表查询可能存在相同的问题。

可以避免此问题吗?它只能但在某些情况下可以撤消操作。

考虑一个带有ID列的表,我可以构造这样的谓词: WHERE ID * 2 = @paramValue

SQL Server知道ID条目乘以2是否是传入值的唯一方法是处理每个条目,将其加倍并检查。这就是索引扫描的情况了。

在这种情况下,我们可以重写它: WHERE ID = @paramValue / 2.0

现在,SQL Server将执行一次数学运算,将传入的值相除,然后可以以可搜索的方式针对索引进行检查。所写SQL的差异看起来可能是陈述问题的微不足道的差异,但对数据库如何解析谓词却产生了很大的差异。

答案 1 :(得分:3)

SQL Server具有四种处理联接的基本方法(与其他数据库一样):

  1. 没有索引的嵌套循环。这就像两个嵌套的for循环,通常是最慢的方法。
  2. 索引循环(带有索引的嵌套循环)。这是扫描一个表并在第二个表中进行查找。
  3. 合并加入。假设这两个表是有序的,并且同时遍历两个表(这也可以使用索引来完成)。
  4. 哈希联接。这两个表的键被散列,并且散列表用于匹配。

通常,其中的第一个是最慢的,而第二个(使用索引)是最快的。 (有例外)。第二个通常是最快的。

当您在表中的两列之间使用相等性比较时,SQL Server提供了很多信息来决定要使用的最佳联接算法:

  • 它具有索引信息。
  • 该列上有统计信息。

在没有此信息的情况下,SQL Server通常默认为嵌套循环联接。我发现即使可以将表达式用于基于合并或基于哈希的联接,它也可以做到这一点。

请注意,您可以使用计算列来解决此问题:

alter table t2 add id_no_a as (replace(id, 'A', '')) persisted;

create index idx_t2_id_no_a on t2(id_no_a);

然后短语

on T1.[ID] = t2.id_no_a

答案 2 :(得分:-2)

使用联合避免搜索无索引的示例:

DECLARE @T1 TABLE (ID VARCHAR(16), CODE INT)
DECLARE @T2 TABLE (ID VARCHAR(16), CODE INT)

INSERT INTO @T1 VALUES ('ASD',1)
INSERT INTO @T1 VALUES ('DFG',2)
INSERT INTO @T1 VALUES ('RTY',3)
INSERT INTO @T1 VALUES ('AZX',4)
INSERT INTO @T1 VALUES ('GTY',5)
INSERT INTO @T1 VALUES ('KKO',6)

INSERT INTO @T2 VALUES ('ASD',1)
INSERT INTO @T2 VALUES ('SD',2)
INSERT INTO @T2 VALUES ('DFG',3)
INSERT INTO @T2 VALUES ('RTY',4)
INSERT INTO @T2 VALUES ('AZX',5)
INSERT INTO @T2 VALUES ('ZX',6)
INSERT INTO @T2 VALUES ('GTY',7)
INSERT INTO @T2 VALUES ('GTYA',8)
INSERT INTO @T2 VALUES ('KKO',9)
INSERT INTO @T2 VALUES ('KKOA',10)
INSERT INTO @T2 VALUES ('AKKOA',11)



SELECT * FROM @T1 T1 INNER JOIN (SELECT ID FROM @T2 WHERE ID NOT LIKE '%A%')T2 ON T2.ID = T1.ID
UNION ALL 
SELECT * FROM @T1 T1 INNER JOIN (SELECT REPLACE(ID,'A','')ID FROM @T2 WHERE ID LIKE '%A%')T2 ON T2.ID = T1.ID

这是无需更改架构即可执行的操作。 通过更改架构,您需要在T2中创建一个计算出的索引列,并与之连接。这要快得多,并且大部分精力都放在插入/更新上,以维护额外的列和索引。