2

首先,我看到了这个问题:SQL MAX of column including its primary key 我的问题不同,因为我需要多于一行,因为我需要所有客户ID。我认为自己是一个有能力的SQL开发人员,但我一直在处理一个我无法控制的神秘数据库设计(但我离题了)。SQL Server - MAX时间戳记行的主键由客户和时间戳分组

我正在寻找更多的performant方法来获取过滤器,最大聚合和组之后的主键行。

我正在处理版本化表格(意味着许多副本的同一行与次要数据元素更改,直到它“关闭”)。对于包含特定OrderItem(OrderItem ='1111')的一组“订单”,我需要每天在每个客户的一个时间段(OrderDateTime)之间获得最近的Closed(Closed = 1)订单。我不确定我是否理解这一点。 :-)

*请注意,为了简洁和易于理解,我尽我所能将我的用例转换为通用术语。订单和OrderItems(因为这些都是相当学术的),而不是我实际寻找的东西。

传统上,我写过类似这样的东西。

SELECT 
    Order.Order_ID 
FROM 
(
    SELECT 
     Customer_ID, 
     MAX(OrderedDateTime) AS OrderedDateTime 
    FROM 
     Order_versioned 
    JOIN 
     OrderItems_versioned 
      ON Order_versioned.OrderID = OrderItems_versioned.OrderID 
       AND OrderItem.Item_ID = '1111' 
    WHERE 
     Order_versioned.Closed = 1 
     AND Order_versioned.OrderedDateTime BETWEEN '2012-01-01 00:00:00' AND '2012-01-31 23:59:59' 
    GROUP BY 
     Order.Customer_ID 
     , CAST(Order.OrderedDateTime AS DATE) 
) t1 
JOIN 
    Order 
     ON t1.Customer_ID = Order.Customer_ID 
      t1.OrderedDateTime = Order.OrderedDateTime 

背景:CUSTOMER_ID和OrderedDateTime将构成唯一行,这就是为什么我能加入他们,有信心这是一个单行。

注意:在Order_versioned.Closed和所有* ID列上都有索引。

问题在于,虽然Order_versioned.Customer_ID已编入索引,但Order_versioned.OrderedDateTime未被编入索引,并且我无法(由于很多原因...感谢您支持合同)添加索引。不用说这种方法需要一段时间(20,000,000个订单中只有274,000,000个订单项)。

我可以在添加更多在我的子查询中编入索引的字段并将其添加到我的连接中,但理想情况下,我需要一种新的方法。

我希望有人比我拥有更多的绝地武器,但是我不知道他们的袖子,可以指引我朝着正确的方向发展。我认为SQL Server的窗口化功能(OVER,PARTITION等)以及适当的聚合可能会让我得到我需要的东西,但我对这些新功能不够熟悉(是的,我知道它们来自2005年)。然后,再次,这可能是考虑到我的限制,最好的方法。我所希望的是,SQL Server在MAX聚合中维护某种内部指针,并且我不知道如何去实现它。

感谢您的时间。

回答

2

也许这会帮助:
我做了一个使用货币和货币值的小例子。我的例子是任务是获取货币的最新货币价值。我认为你可以很容易地将这个例子应用到你的代码中。所以这里是例子:

DECLARE @tblCurrency TABLE 
    (
     pkCurrencyID INT, 
     name VARCHAR(100) 
    ) 
DECLARE @tblCurrencyValues TABLE 
    (
     pkCurrencyValueID INT, 
     currencyDate DATETIME, 
     fkCurrencyID INT, 
     rate FLOAT 
    ) 

INSERT INTO @tblCurrency 
(
    pkCurrencyID, 
    name 
) 
SELECT 1,'SEK' 
UNION ALL 
SELECT 2,'EURO' 
UNION ALL 
SELECT 3, 'DKK' 

INSERT INTO @tblCurrencyValues 
(
    pkCurrencyValueID, 
    fkCurrencyID, 
    currencyDate, 
    rate 
) 
SELECT 1,1,GETDATE(),1.4 
UNION ALL 
SELECT 2,1,GETDATE()-2,1.4 
UNION ALL 
SELECT 3,1,GETDATE()-1,5 
UNION ALL 
SELECT 4,2,GETDATE(),1.4 
UNION ALL 
SELECT 5,2,GETDATE()-2,1.4 
UNION ALL 
SELECT 6,2,GETDATE()-1,5 
UNION ALL 
SELECT 7,3,GETDATE(),1.4 
UNION ALL 
SELECT 8,3,GETDATE()-2,1.4 
UNION ALL 
SELECT 9,3,GETDATE()-1,5 

;WITH CTE 
AS 
(
    SELECT 
     RANK() OVER(PARTITION BY tblCurrencyValues.fkCurrencyID order by tblCurrencyValues.currencyDate) as currencyValueRank, 
     tblCurrencyValues.fkCurrencyID, 
     tblCurrencyValues.currencyDate, 
     tblCurrencyValues.rate 
    FROM 
     @tblCurrencyValues AS tblCurrencyValues 
) 
SELECT 
    * 
FROM 
    CTE 
    JOIN @tblCurrency AS tblCurrency 
     ON CTE.fkCurrencyID=tblCurrency.pkCurrencyID 
WHERE 
    CTE.currencyValueRank=1 
2

要采取什么Arion建议更进一步。这里是我使用窗口和CTE(Arion的建议)在原始问题中提供的准确端口。

;WITH t1 
AS 
(
    SELECT 
     RANK() OVER(PARTITION BY o.Customer_ID, CAST(o.OrderedDateTime AS Date) ORDER BY o.OrderedDateTime DESC) as iRank 
     , o.Order_ID 
    FROM 
     Order_versioned o WITH(NOLOCK) 
    JOIN 
     OrderItems_version AS oi WITH(NOLOCK) 
      ON 
      o.OrderID = oi.OrderID 
      AND oi.Item_ID = '1111' 
WHERE 
    o.Closed = 1 
    AND o.OrderedDateTime BETWEEN '2012-01-01 00:00:00' AND '2012-01-31 23:59:59' 
) 
SELECT 
    t1.Order_ID 
FROM 
    t1 
WHERE 
    t1.iRank = 1 

它速度非常快。但我正在做更多的调查,以确保这是最好的表现。