【发布时间】:2018-01-12 13:48:38
【问题描述】:
让我们使用以下脚本创建一个简单的表order(id: int, category: int, order_date: int)
IF OBJECT_ID('dbo.orders', 'U') IS NOT NULL DROP TABLE dbo.orders
SELECT TOP 1000000
NEWID() id,
ABS(CHECKSUM(NEWID())) % 100 category,
ABS(CHECKSUM(NEWID())) % 10000 order_date
INTO orders
FROM sys.sysobjects
CROSS JOIN sys.all_columns
现在,我有两个等价的查询(至少我相信它们是等价的):
-- Q1
select distinct o1.category,
(select count(*) from orders o2 where order_date = 1 and o1.category = o2.category)
from orders o1
-- Q2
select o1.category,
(select count(*) from orders o2 where order_date = 1 and o1.category = o2.category)
from (select distinct category from orders) o1
但是,当我运行这些查询时,它们具有明显不同的特征。 Q2 对我的数据来说快了两倍,这显然是由于查询计划在加入之前首先找到唯一类别(以下查询计划中的哈希匹配)。
如果添加请求的索引,差异仍然存在
CREATE NONCLUSTERED INDEX ix_order_date ON orders(order_date)
INCLUDE (category)
此外,Q2 还可以有效地使用以下索引,而 Q1 保持不变:
CREATE NONCLUSTERED INDEX ix_orders_kat ON orders(category, order_date)
我的问题是:
- 这些查询是否等效?
- 如果是,SQL Server 2016 查询优化器在 Q1 的情况下找到第二个查询计划的障碍是什么(我相信这种情况下搜索空间一定很小)?
- 如果没有,您能否发布一个反例?
编辑
我提出这个问题的动机是,我想了解为什么查询优化器在重写简单查询方面如此糟糕,而且它们如此严重地依赖 SQL 语法。 SQL 语言是一种声明性语言,因此,为什么 SQL 查询处理器经常由语法驱动,即使对于这样的简单查询也是如此?
【问题讨论】:
-
为什么不只是
SELECT category, COUNT(*) FROM dbo.orders WHERE order_date = 1 GROUP BY category;?您是否简化了一个更复杂的问题? -
您可以使用@AaronBertrand 的查询发布查询计划吗?这将是您想要的结果的最典型示例。
-
@justiceorjustus 实际上,它不是前两个的等效查询。当没有
order_date = 1时,它会忽略类别 -
@RadimBača 也许然后去
SELECT category, COUNT(CASE WHEN order_date = 1 THEN 1 END) FROM dbo.orders GROUP BY category; -
@justiceorjustus 谢谢,条件聚合这会导致一次顺序扫描,这可能是好是坏取决于选择性,如下所示:stackoverflow.com/questions/45795898/… 也许我应该在我的问题中更清楚,我会想了解为什么查询优化器在重写简单查询方面如此糟糕,而且它们如此严重地依赖 SQL 语法。
标签: sql performance query-optimization sql-server-2016