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 

它速度非常快。但我正在做更多的調查,以確保這是最好的表現。