SELECT MAX()太慢 - 任何替代方案?

时间:2013-12-03 12:34:41

标签: sql sql-server timeout max

我继承了一个基于SQL Server的应用程序,它有一个包含以下内容的存储过程,但它会超时。我相信我已经将问题分离到了SELECT MAX()部分,但我无法弄清楚如何使用替代方法,例如ROW_NUMBER()OVER(PARTITION BY ...

有人有任何想法吗?

这是“违规”代码:

SELECT BData.*, B.* 
FROM BData
INNER JOIN
(
    SELECT MAX( BData.StatusTime ) AS MaxDate, BData.BID
    FROM BData
    GROUP BY BData.BID
) qryMaxDates
ON ( BData.BID = qryMaxDates.BID ) AND ( BData.StatusTime = qryMaxDates.MaxDate )
INNER JOIN BItems B ON B.InternalID = qryMaxDates.BID
WHERE  B.ICID = 2
ORDER BY BData.StatusTime DESC;

提前致谢。

6 个答案:

答案 0 :(得分:4)

重写查询很少解决SQL性能问题。无论如何,编译器已经知道如何重写它。问题始终是索引。要有效获得MAX(StatusTime ) ... GROUP BY BID,您需要BData(BID, StatusTime)上的索引。为了有效地搜索WHERE B.ICID = 2,您需要BItems.ICID上的索引。

查询也可能表示为相关的APPLY,因为它似乎是真正需要的:

SELECT D.*, B.* 
FROM BItems B
CROSS APPLY
(
    SELECT TOP(1) *
    FROM BData
    WHERE B.InternalID = BData.BID
    ORDER BY StatusTime DESC
) AS D
WHERE  B.ICID = 2
ORDER BY D.StatusTime DESC;

SQL Fiddle

这在语义上与OP不同,OP会在StatusTime冲突时返回多行,我只是猜测这是想要的('这个BItem的最新BData' )。

答案 1 :(得分:2)

考虑创建以下索引:

CREATE INDEX LatestTime ON dbo.BData(BID, StatusTime DESC);

这将支持使用CTE的查询,例如:

;WITH x AS
(
  SELECT *, rn = ROW_NUMBER() OVER (PARTITION BY BID ORDER BY StatusDate DESC)
  FROM dbo.BData
)
SELECT * FROM x 
INNER JOIN dbo.BItems AS bi
  ON x.BID = bi.InternalID
WHERE x.rn = 1 AND bi.ICID = 2
ORDER BY x.StatusDate DESC;

查询是否仍然从BItems上的任何索引获得效率是另一个问题,但这至少应该使聚合操作更简单(尽管它仍然需要查找以获取其余列)。

另一个想法是停止使用两个表中的SELECT *并仅选择您实际需要的列。如果你真的需要两个表中的所有列(这很少见,特别是对于连接),那么你需要在两边都有覆盖索引以防止查找。

我还建议在整个模型中调用任何标识符。为什么链接这些表的ID在一个表中调用BID而在另一个表中调用InternalID

另外,请始终使用其架构引用表。

答案 2 :(得分:2)

这可能是一个迟到的响应,但我最近遇到了同样的性能问题,其中涉及max()的简单查询执行时间超过1小时。

在查看执行计划之后,似乎为了执行max()函数,将获取满足where子句条件的每条记录。在您的情况下,在执行max()函数之前,需要获取表中的每条记录。此外,索引BData.StatusTime不会加快查询速度。索引对于查找特定记录很有用,但它无助于执行比较。

在我的情况下,我没有这个组,所以我所做的只是使用ORDER BY DESC子句和SELECT TOP 1.查询从超过1小时下降到5分钟以下。也许,你可以做Gordon Linoff建议并使用PARTITION BY。希望您的查询可以加快。

干杯!

答案 3 :(得分:0)

以下是使用row_number()的查询版本:

SELECT bd.*, b.* 
FROM (select bd.*, row_number() over (partition by bid order by statustime desc) as seqnum
      from BData bd
     ) bd INNER JOIN
     BItems b
     ON b.InternalID = bd.BID and bd.seqnum = 1
WHERE B.ICID = 2
ORDER BY BData.StatusTime DESC;

如果速度不快,那么查看查询和查询的查询计划以了解如何优化它们会很有用。

答案 4 :(得分:0)

完全取决于您拥有的数据类型。一种可能更快的替代方法是使用CROSS APPLY而不是MAX子查询。但更有可能的是它不会产生任何更快的结果。

最好的选择可能是在BID上添加索引,INCLUDE包含StatusTime,如果可能,可以通过InternalID匹配的BItems.ICID = 2过滤。

答案 5 :(得分:0)

[UNSOLVED]但我已经继续前进了!

感谢所有提供答案/建议的人。不幸的是,我无法继续这样做,所以暂时放弃尝试。

看起来最好的解决方案是重新编写应用程序以将最新数据更新到另一个表中,这样就可以快速简单地选择最新读数。

再次感谢您的建议。