【问题标题】:What's the difference between these T-SQL queries using OR?这些使用 OR 的 T-SQL 查询有什么区别?
【发布时间】:2012-03-29 19:30:52
【问题描述】:

我使用 Microsoft SQL Server 2008 (SP1, x64)。我有两个查询执行相同的操作,或者我认为是这样,但它们的查询计划和性能完全不同。

查询 1:

SELECT c_pk
FROM table_c
WHERE c_b_id IN (SELECT b_id FROM table_b WHERE b_z = 1)
  OR  c_a_id IN (SELECT a_id FROM table_a WHERE a_z = 1)

查询 2:

SELECT c_pk
FROM table_c
LEFT JOIN (SELECT b_id FROM table_b WHERE b_z = 1) AS b ON c_b_id = b_id
LEFT JOIN (SELECT a_id FROM table_a WHERE a_z = 1) AS a ON c_a_id = a_id
WHERE b_id IS NOT NULL
  OR  a_id IS NOT NULL

查询 1 和我预期的一样快,而查询 2 非常慢。 query plans 看起来完全不同。

我希望查询 2 与查询 1 一样快。我有使用查询 2 的软件,但我无法将其更改为查询 1。我可以更改数据库。

一些问题:

  • 为什么查询计划不同?
  • 我能否以某种方式“教”SQL Server 查询 2 等于查询 1?

所有表在所有列上都有(聚集的)主键和正确的索引:

CREATE TABLE table_a (
  a_pk   int NOT NULL PRIMARY KEY,
  a_id   int NOT NULL UNIQUE,
  a_z    int
)
GO
CREATE INDEX IX_table_a_z ON table_a (a_z)
GO

CREATE TABLE table_b (
  b_pk   int NOT NULL PRIMARY KEY,
  b_id   int NOT NULL UNIQUE,
  b_z    int
)
GO
CREATE INDEX IX_table_b_z ON table_b (b_z)
GO

CREATE TABLE table_c (
  c_pk   int NOT NULL PRIMARY KEY,
  c_a_id int,
  c_b_id int
)
GO
CREATE INDEX IX_table_c_a_id ON table_c (c_a_id)
GO
CREATE INDEX IX_table_c_b_id ON table_c (c_b_id)
GO

表格在最初填充后不会被修改。我是唯一一个询问他们的人。它们包含数百万条记录(table_a:5M,table_b:4M,table_c:12M),但仅使用 1% 会产生类似的结果。

编辑:我尝试为c_a_idc_b_id 添加外键,但这只会使查询1 变慢...

我希望有人可以看看query plans并解释其中的区别。

【问题讨论】:

  • 这样做的动机是什么? IN/EXISTS 在 SQL Server 中通常比 OUTER JOIN ... NULL 更有效,而且第一个查询对我来说似乎更清晰,那么为什么不直接使用第一个呢?
  • @Martin "我有使用查询 2 的软件,我无法更改"
  • 一般来说,查询是不一样的,因为 Join 可以带来重复的行,而 semi join 不会。虽然尚未检查您是否有任何限制阻止此操作。
  • @Martin a_idb_id 是唯一的,因此连接不会重复行。
  • 我 99% 确信在这种情况下它们确实具有相同的语义。但这并不意味着 QO 具有将一种转换为另一种的必要转换规则。查询的编写方式经常会影响计划。您是否尝试过在查询中使用计划 quide(带有 USE PLAN 提示)来尝试让第二个使用第一个的计划?

标签: sql-server sql-server-2008 database-performance sql-execution-plan


【解决方案1】:

加入速度较慢,让我说设计。第一个查询使用子查询(可缓存)来过滤记录,因此它将产生更少的数据(以及对每个表的访问更少)。

你读过这些吗:

我的意思是,使用 IN 数据库可以做更好的优化,比如删除重复项、在第一次匹配时停止和类似(这些来自学校的记忆,所以我'我相信它会做得更好)。所以我问题不在于 QP 为何不同,而在于深度优化有多聪明。

【讨论】:

  • IN 是半联接。不确定您所说的可缓存子查询是什么意思。
  • SQL Server 在优化 JOIN 和子查询方面非常出色,并且会使用最快的任何查找。但在这种情况下不是。我了解索引,我认为您的链接没有添加任何相关内容。
  • 添加了一些解释我的意思
【解决方案2】:

您正在比较非等效查询,并且您正在以非常不寻常的方式使用左连接。 一般来说,如果您的意图是选择 table_c 中的所有条目,这些条目在 table_a 或 table_b 中具有链接记录,您应该使用 exists 语句:

SELECT c_pk 
FROM table_c 
WHERE  Exists( 
 SELECT 1
 FROM table_b 
 WHERE b_z = 1 and c_b_id = b_id 
) OR  Exists( 
 SELECT 1 
 FROM table_a 
 WHERE a_z = 1 and c_a_id = a_id
) 

【讨论】:

  • 如果您发布代码、XML 或数据示例,在文本编辑器中突出显示这些行,然后单击编辑器上的“代码示例”按钮 ({ })工具栏以很好地格式化和语法突出显示它!
【解决方案3】:

既然不能改变查询,至少可以改善查询的环境。

  1. 突出显示您的查询,在 SSMS 中右键单击它并选择“分析 在数据库引擎优化顾问中查询。”
  2. 运行分析以确定是否需要任何其他索引或 已建立统计数据。
  3. 听取 SQL Server 的建议。

【讨论】:

  • 我的 SSMS 中没有看到任何“Tuning Advisor”。估计的执行计划没有显示任何缺失的索引。所有列都已编入索引,您认为还有什么要添加的?
  • @MicheldeRuiter - 怀疑有什么可以添加的。您需要重写查询或使用性能。在这种情况下,SQL Server 似乎无法将OR 转换为UNION,因此它正在处理table_c 外部连接到其他两个表的所有行,然后在最后进行过滤。
  • 你可能有免费版本,但它不可用或者你没有安装它。
猜你喜欢
  • 1970-01-01
  • 2020-03-22
  • 2012-04-19
  • 1970-01-01
  • 2012-11-21
  • 2014-02-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多