比较SQL Server 2012中的两个查询


14

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

在使用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

统计时间

查询1 STATISTICS TIME:CPU时间= 0ms,已用时间= 24 ms

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

统计IO

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

查询2统计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

非聚集索引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

评论不作进一步讨论;此对话已转移至聊天
保罗·怀特9

Answers:


10

我喜欢您仔细考虑查询调整和检查选项和计划的方法。我希望有更多的开发人员这样做。一个警告是-总是测试很多行,看逻辑读,这是一个很小的表。尝试生成样本负载,然后再次运行查询。一个小问题-在最上面的查询中,您不要求要订购,在最下面的查询中,您不要求。您应该将它们与订购进行比较和对比。

我刚刚快速创建了一个SalesOrders表,其中包含200,000个销售订单-凭空想像仍然不算太大。并使用每个查询中的ORDER BY运行查询。我也玩了一些索引。

在OrderID上没有聚簇索引,而在CustID上只有一个非聚簇索引第二个查询的表现要好。特别是按顺序包含在每个中。第一个查询的读取次数是第二个查询的两倍,两次查询之间的费用百分比为67%/ 33%。

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

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

您可能会发现它们的行为比增加行时的行为更相似,因此请记住这一警告并进行测试。

如果您要返回的只是每个客户的最大OrderID,并且您要确定OrderID是最大的OrderID,那么这两个查询中的第二个查询是我心中最好的方式-这有点基于子树的成本更简单,更昂贵,但解密语句则更快,更容易。如果打算在某天将其他列添加到结果集中?然后,第一个查询允许您执行此操作。

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

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

但是,进行该测试的最佳方法-测试更多数据-始终确保您的数据与生产和预期的未来生产一致。当您向表中添加更多行时,查询计划便开始查找数据,并尝试保持生产中期望的分布。并且注意诸如是否包括“按订单排序”之类的事情,在这里我认为这最终并不会带来可怕的变化,但仍然值得深入研究。

比较这种详细程度和数据水平的方法是一个很好的方法。子树的成本大部分是任意的,毫无意义,但仍然值得至少在编辑/更改之间甚至查询之间进行比较。查看时间统计信息和IO非常重要,对于任何因您正在使用的数据大小和正在尝试执行的操作而感到不合适的事情,请查看计划。


再次您好,谢谢您关于使用大量数据的观点。这不是有人第一次提出来。尽管最后一次是考虑页面拆分可能造成的碎片。在您的200,000行样本中,您是否检查了碎片?
Craig Efrein

好吧,在我快速的200k行示例中,我不是在关注碎片,不是。但是我做的方式不会有。我创建了表,填充了表,然后创建了索引,因此它们是新创建的索引。这不会改变查看查询计划的方法,这似乎是主要问题。准确查看查询计划的数据量很大-确实很大。我经常看到这样的情况,它在开发中看起来很棒(有1-10行),而在带有真实数据的产品中却令人恐惧。但是您的方法很好,希望此信息以及评论中的对话会有所帮助
Mike Walsh 2013年

由于我们是按“客户”分类的,因此您如何使“客户”值足够随机?我从阅读中记得的一件事是独特价值的重要性。如果custid仅拥有少量不同的客户,那么流聚合的成本将是不现实的。
Craig Efrein

我只是使用RAND函数创建了100个客户,并为每个orderID随机分配了一个。.我正在做一个快速检查。:)
Mike Walsh 2013年

感谢Mike的所有帮助。最后一个问题。从我在问题中提供的2012年执行计划的SELECT属性屏幕中,您需要注意哪些值?
Craig Efrein
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.