SELECT MAX() 太慢了 - 有没有其他选择?

sim*_*onl 5 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;
Run Code Online (Sandbox Code Playgroud)

提前致谢。

Rem*_*anu 5

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;
Run Code Online (Sandbox Code Playgroud)

SQL小提琴。

这在语义上与 OP 的查询不同,OP 会在 StatusTime 冲突时返回多行,我只是猜测这是所需的(“此 BItem 的最新 BData”)。

  • 也许,为了使其(更多)在语义上等于 OP,可以在 CROSS APPLY 中使用“TOP (1) WITH TIES”。 (3认同)

Aar*_*and 5

考虑创建以下索引:

CREATE INDEX LatestTime ON dbo.BData(BID, StatusTime DESC);
Run Code Online (Sandbox Code Playgroud)

这将支持具有 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;
Run Code Online (Sandbox Code Playgroud)

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

另一个想法是停止使用SELECT *两个表,只选择您实际需要的列。如果您确实需要两个表中的所有列(这种情况很少见,尤其是连接时),那么您将希望在两侧都有覆盖索引以防止查找。

我还建议在整个模型中将任何标识符称为相同的东西。为什么BID在一个表和InternalID另一个表中分别调用链接这些表的 ID ?

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

  • @user3061406 是的,抱歉,我抓住了你的子查询,但没有对其进行足够的修改。请参阅更新。 (2认同)

小智 5

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

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

就我而言,我没有分组依据,所以我所做的只是使用 ORDER BY DESC 子句和 SELECT TOP 1。查询从 1 多小时缩短到不到 5 分钟。也许,您可以按照 Gordon Linoff 的建议并使用 PARTITION BY。希望您的查询能够加快速度。

干杯!

  • ORDER BY DESC 更快!谢谢! (2认同)

sim*_*onl 1

[未解决] 但我已经继续前进了!

感谢所有提供答案/建议的人。不幸的是我无法进一步解决这个问题,所以暂时放弃了尝试。

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

再次感谢您的建议。