比较 SQL Server 2012 中的两个查询

Cra*_*ein 14 performance sql-server-2012 query-performance

我正在比较 SQL Server 2012 中的两个查询。目标是在选择最佳查询时使用查询优化器提供的所有相关信息。两个查询产生相同的结果;所有客户的最大 orderid。

在使用 FREEPROCCACHE 和 DROPCLEANBUFFERS 执行每个查询之前清除缓冲池

使用下面提供的信息,哪个查询是更好的选择?

-- Query 1 - return the maximum order id for a customer
SELECT orderid, custid
FROM Sales.Orders AS O1
WHERE orderid = (SELECT MAX(O2.orderid)
                 FROM Sales.Orders AS O2
                 WHERE O2.custid = O1.custid);


-- Query 2 - return the maximum order id for a customer
SELECT MAX(orderid), custid
FROM Sales.Orders AS O1
group by custid
order by custid
Run Code Online (Sandbox Code Playgroud)

统计时间

查询 1 STATISTICS TIME:CPU 时间 = 0 毫秒,经过时间 = 24 毫秒

查询 2 STATISTICS TIME:CPU 时间 = 0 ms,经过时间 = 23 ms

统计数据

查询 1 STATISTICS IO:表“订单”。扫描计数 1,逻辑读 5,物理读 2,预读 0,lob 逻辑读 0,lob 物理读 0,lob 预读 0。

查询 2 STATISTICS IO:表“订单”。扫描计数 1,逻辑读 4,物理读 1,预读 8,lob 逻辑读 0,lob 物理读 0,lob 预读 0。

执行计划

在此处输入图片说明

SELECT 属性查询 1

在此处输入图片说明

SELECT 属性查询 2

在此处输入图片说明

结论:

查询 1

  1. 批量成本 48%
  2. 逻辑读取 5
  3. 物理读取 2
  4. 预读次数:0
  5. CPU时间:0ms
  6. 已用时间 24ms
  7. 估计子树成本:0.0050276
  8. 编译CPU:2
  9. 编译内存:384
  10. 编译时间:2

查询 2

  1. 批量成本 52%
  2. 逻辑读取 4
  3. 物理阅读 1
  4. 预读次数:8
  5. CPU 时间 0
  6. 已用时间 23ms
  7. 估计子树成本:0.0054782
  8. 编译CPU:0
  9. 编译内存:192
  10. 编译时间:0

就个人而言,即使根据图形计划,查询 2 的批处理成本更高,但我认为它比查询 1 更有效。这是因为查询 2 需要较少的逻辑读取,运行时间略低,compilecpu、compilememory 和 compiletime 值是降低。预读读取对于查询 2 为 8,对于查询 1 为 0。

更新 12:03

聚集索引定义

ALTER TABLE [Sales].[Orders] ADD  CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED 
(
    [orderid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
Run Code Online (Sandbox Code Playgroud)

非聚集索引 idx_nc_custid

CREATE NONCLUSTERED INDEX [idx_nc_custid] ON [Sales].[Orders]
(
    [custid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
Run Code Online (Sandbox Code Playgroud)

Mik*_*lsh 10

我喜欢你仔细考虑查询调整和审查选项和计划的方法。我希望更多的开发者这样做。一个警告是 - 总是测试很多行,查看逻辑读取,这是一个小表。尝试生成示例负载并再次运行查询。一个小问题 - 在您的顶部查询中,您没有要求订购,在您的底部查询中您是。您应该通过排序来比较和对比它们。

我只是快速创建了一个 SalesOrders 表,其中包含 200,000 个销售订单 - 无论如何都不是很大。并在每个查询中使用 ORDER BY 运行查询。我也玩了一点索引。

OrderID 上没有聚集索引,只有 CustID 上的非聚集索引第二个查询表现出色。特别是每个都包含 order by 。第一个查询的读取次数是第二个查询的两倍,查询之间的成本百分比为 67% / 33%。

使用 OrderID 上的聚集索引和 CustID 上的非聚集索引,它们以相似的速度和完全相同的读取次数执行。

因此,我建议您增加行数并进行更多测试。但我对您的查询的最终分析-

当您增加行数时,您可能会发现它们的行为比您意识到的更相似,因此请记住这一警告并以这种方式进行测试。

如果您只想返回每个客户的最大 OrderID,并且您想通过 OrderID 来确定最大的 OrderID,那么这两个查询中的第二个查询是从我的心态出发的最佳方式 - 有点更简单,虽然基于子树成本稍微贵一点,但它是一种更快、更容易破译的语句。如果您打算有一天将其他列添加到您的结果集中?然后第一个查询允许您这样做。

更新: 您在问题下的评论之一是:

请记住,在这个问题中找到最佳查询是一种改进用于比较它们的技术的方法。

但是最好的方法是使用更多数据进行测试 - 始终确保您的数据与生产和预期的未来生产一致。当您为表提供更多行时,查询计划开始查看数据,并尝试保持您在生产中期望的分布。并注意诸如是否包含 Order By 之类的事情,在这里我认为它最终不会产生太大的不同,但仍然值得深入研究。

您比较这种详细程度和数据的方法是一种很好的方法。子树成本大多是任意且毫无意义的,但至少仍然值得查看以比较编辑/更改之间甚至查询之间的比较。查看时间统计数据和 IO 非常重要,就像查看计划以应对与您正在处理的数据的大小和您正在尝试做的事情不合适的任何事情。