【发布时间】:2010-06-25 14:38:05
【问题描述】:
我有一个类似于以下(简化)的表架构:
CREATE TABLE Transactions
(
TransactionID int NOT NULL IDENTITY(1, 1) PRIMARY KEY CLUSTERED,
CustomerID int NOT NULL, -- Foreign key, not shown
TransactionDate datetime NOT NULL,
...
)
CREATE INDEX IX_Transactions_Customer_Date
ON Transactions (CustomerID, TransactionDate)
在这里提供一些背景知识,这个事务表实际上是从另一个供应商的数据库中整合了几种不同类型的事务(我们将其称为 ETL 过程),因此我没有太多的控制权它们插入的顺序。即使我这样做了,交易也可能会回溯,所以这里要注意的重要一点是任何给定customer 的最大TransactionID 不一定是最近的交易。
事实上,最近的交易是日期和 ID 的组合。日期不是唯一的 - 供应商经常会截断一天中的时间 - 所以要获得最近的交易,我必须首先找到最近的日期,然后找到该日期的最近 ID。
我知道我可以使用窗口查询 (ROW_NUMBER() OVER (PARTITION BY TransactionDate DESC, TransactionID DESC)) 来做到这一点,但这需要完整的索引扫描和非常昂贵的排序,因此在效率方面非常失败。一直写也挺别扭的。
使用两个 CTE 或嵌套子查询效率稍高,一个查找每个 CustomerID 的 MAX(TransactionDate),另一个查找 MAX(TransactionID)。同样,它可以工作,但需要第二次聚合和连接,这比 ROW_NUMBER() 查询稍好,但在性能方面仍然相当痛苦。
我也考虑过使用 CLR 用户定义的聚合,如有必要,我会使用它,但如果可能的话,我更愿意找到一个纯 SQL 解决方案来简化部署(任何地方都不需要 SQL-CLR其他在这个项目中)。
所以问题,具体来说是:
是否可以编写一个查询,该查询将返回 最新 TransactionID per CustomerID,定义为最近 TransactionDate 的最大 TransactionID,并实现与普通MAX/GROUP BY 查询性能相当的计划?
(换句话说,计划中唯一重要的步骤应该是索引扫描和流聚合。多次扫描、排序、连接等可能太慢了。)
【问题讨论】:
标签: sql-server sql-server-2008 aggregate composite-key