2012-09-14 119 views
3

我有一個查詢可以爲客戶提取月/年總計,並添加ntile排名。如果我能夠取出第1,2,3,4和5號的最大小計,我幾乎可以得到我所追求的,但我不知道如何繼續。SELECT top top 5 SUM(每個客戶一個)範圍內的每個月

例如,結果我想會是這個樣子:

Month Year CustomerCode SubTotal ntile 
1  2012 CCC   131.45  1 
1  2012 CCC   342.95  2 
1  2012 ELITE   643.92  3 
1  2012 CCC   1454.05 4 
1  2012 CCC   12971.78 5 
2  2012 CCC   135.99  1 
2  2012 CCI   370.47  2 
2  2012 NOC   766.84  3 
2  2012 ELITE   1428.26 4 
2  2012 VBC   5073.20 5 
3  2012 CCC   119.02  1 
3  2012 CCC   323.78  2 
3  2012 HUCC   759.66  3 
3  2012 ELITE   1402.95 4 
3  2012 CCC   7964.20 5 

EXCEPT - 我希望排名是不同的客戶像2月,但我的基本查詢是不是給我,結果 - 而且我顯然不知道如何在SQL SERVER 2005的T-SQL中獲得它 - 實際上我不確定我得到了什麼。

我的下一個選項是拉在C#的DataTable,並做一些體操到那裏,但必須有一個更簡單的方法:)

我的基本查詢是

SELECT 
i.DateOrdered 
,LTRIM(STR(DATEPART(MONTH,i.DateOrdered))) AS [Month] 
,LTRIM(STR(YEAR(i.Dateordered))) AS [Year] 
,c.CustomerCode 
,SUM(i.Jobprice) AS Subtotal 
,NTILE(5) OVER(ORDER BY SUM(i.JobPrice)) AS [ntile] 
FROM Invoices i 
JOIN 
Customers c 
ON i.CustomerID = c.ID 
WHERE i.DateOrdered >= '1/1/2012' 
AND i.DateOrdered <= '9/30/2012' 
GROUP BY YEAR(i.DateOrdered), MONTH(i.DateOrdered), i.DateOrdered, c.CustomerCode 
ORDER BY LTRIM(STR(DATEPART(MONTH,i.DateOrdered))), 
TRIM(STR(YEAR(i.Dateordered))),  
SUM(i.JobPrice), c.CustomerCode ASC 

我會真的很感謝幫助獲得這個權利。

在此先感謝

+0

順便說一句,'DATEPART(MONTH,...)'可以用'MONTH替換( ...)',並且你並不真的需要圍繞'MONTH()'和'YEAR()'調用'STR()'。你也可能不需要'LTRIM()'。 (至少,你可以在GROUP BY中省略LTRIM,如果需要的話,可以將它們留在SELECT中) –

+0

你想確保每個月不超過5個不同*客戶*或者沒有輸出中每月超過5個不同的*金額*換句話說,如果兩位顧客的每日總數相同,你是否希望他們算作一個(比如#3)還是兩個(比如#3和#4)? –

回答

3

如果我正確地讀你,你是什麼後

對於範圍內的每個月份,
顯示5個客戶誰擁有最大的SUM在那個月
並針對每個客戶,顯示相應的SUM。

在這種情況下,此SQL Fiddle將創建一個樣本表並運行提供上述輸出的查詢。如果您想查看創建表中的內容,只需在右側面板上執行簡單的SELECT操作即可。

查詢是:

;  WITH G as -- grouped by month and customer 
(
    SELECT DATEADD(D,1-DAY(i.DateOrdered),i.DateOrdered) [Month], 
      c.CustomerCode, 
      SUM(i.Jobprice) Subtotal 
     FROM Invoices i 
     JOIN Customers c ON i.CustomerID = c.ID 
    WHERE i.DateOrdered >= '1/1/2012' AND i.DateOrdered <= '9/30/2012' 
    GROUP BY DATEADD(D,1-DAY(i.DateOrdered),i.DateOrdered), c.CustomerCode 
) 
    SELECT MONTH([Month]) [Month], 
      YEAR([Month]) [Year], 
      CustomerCode, 
      SubTotal, 
      Rnk [Rank] 
     FROM 
(
    SELECT *, RANK() OVER (partition by [Month] order by Subtotal desc) Rnk 
     FROM G 
) X 
    WHERE Rnk <= 5 
    ORDER BY Month, Rnk 

爲了解釋,第一部分(帶座)是寫一個子查詢的只是一個奇特的方式,即通過組月份和客戶數據。表達式DATEADD(D,1-DAY(i.DateOrdered),i.DateOrdered)將每個日期轉換爲該月的第一天,以便數據可以很容易地按月分組。以傳統形式編寫的下一個子查詢在每個月的小計中添加一個RANK列,最後選擇該列給出前5 *。

請注意,RANK允許相同的排名,如果其中3個排名相同,則可能最終顯示一個月的6位客戶。如果這不是您想要的,那麼您可以將單詞RANK更改爲ROW_NUMBER這將在相同的小計之間隨機打破。

+0

@cyberkiwi - 這個答案是完美的,提供所需的行。非常感謝。我現在需要將它存入存儲過程。我有代碼來生成圖的字段,但是這個輸出需要進入臨時表。這使用CTE生成正確?我在哪裏將INTO#收入(我的第一張臨時表)放在G之前?這會在SS Management Studio中產生大量紅色波浪線。 –

+0

@cyberkiwi - 「選擇月份([月])[月], YEAR([月])[年], CustomerCode, 小計, RNK [排名] INTO #Revenues FROM」 - 我把它放在這裏並編譯它(通常這對我來說是個好兆頭:)所以現在就去試試看吧。我會稍微檢查一下,看看你是否留下評論。再次感謝。 –

+0

#收入正在創建。它一定是 - 美中不足的是沒有來自SP的數據。我將這些字段作爲CSV字符串返回到OUTPUT參數中,這些字符串用圖列標籤分開:SELECT LTRIM(STR([Year]))+' - '+ STUFF([Month],1,0,REPLICATE ( '0',2 - LEN([月]))) AS [日期] ,小計 INTO#Revenues2 FROM #Revenues - 爲OUTPUT參數 - SELECT @years = STUFF((SELECT DISTINCT '],[' + [日期] FROM#Revenues2 ORDER BY '],[' + [日期] FOR XML PATH( '') ),1,2, '')+ ']' –

0

試試這個:

declare @tab table 
(
[month] int, 
[year] int, 
CustomerCode varchar(20), 
SubTotal float 
) 
insert into @tab 
select 
1,2012,'ccc',131.45 union all 
select 
1,2012,'ccc',343.45 union all 
select 
1,2012,'ELITE',643.92 union all 
select 
2,2012,'ccc',131.45 union all 
select 
2,2012,'ccc',343.45 union all 
select 
2,2012,'ELITE',643.92 union all 
select 
3,2012,'ccc',131.45 union all 
select 
3,2012,'ccc',343.45 union all 
select 
3,2012,'ELITE',643.92 

;with cte as 
(
select NTILE(3) OVER(partition by [month] ORDER BY [month]) AS [ntile],* from @tab 
) 
select * from cte 

即使在你的基地查詢,你需要添加partition by,這樣你會得到正確的輸出。

0

查詢需要修改才能得到月份和年份的日期部分。您在同一個月中多次顯示同一客戶的問題是由於在select和group by子句中包含了i.DateOrdered。

以下查詢應該給你你需要的東西。另外,我懷疑它是查詢的最後一行的錯字,但tsql沒有TRIM()函數,只有LTRIM和RTRIM。

SELECT 

LTRIM(STR(DATEPART(MONTH,i.DateOrdered))) AS [Month] 
,LTRIM(STR(YEAR(i.Dateordered))) AS [Year] 
,c.CustomerCode 
,SUM(i.Jobprice) AS Subtotal 
,NTILE(5) OVER(ORDER BY SUM(i.JobPrice)) AS [ntile] 
FROM Invoices i 
JOIN 
Customers c 
ON i.CustomerID = c.ID 
WHERE i.DateOrdered >= '1/1/2012' 
AND i.DateOrdered <= '9/30/2012' 
GROUP BY YEAR(i.DateOrdered), MONTH(i.DateOrdered), c.CustomerCode 
ORDER BY LTRIM(STR(DATEPART(MONTH,i.DateOrdered))), 
LTRIM(STR(YEAR(i.Dateordered))),  
SUM(i.JobPrice), c.CustomerCode ASC 

這給了這些結果

Month Year CustomerCode Subtotal ntile 
1  2012 ELITE   643.92  2 
1  2012 CCC   14900.23  5 
2  2012 CCC    135.99  1 
2  2012 CCI    370.47  1 
2  2012 NOC    766.84  3 
2  2012 ELITE   1428.26  4 
2  2012 VBC    5073.20  4 
3  2012 HUCC    759.66  2 
3  2012 ELITE   1402.95  3 
3  2012 CCC    8407.00  5 
0

我看不出如何在不排名解決了這個問題:

  1. 你需要得到每個客戶的最大金額&一個月。

  2. 然後,您需要每個月檢索前五個找到的總和。

這裏是我會怎麼處理這:

; 
WITH MaxSubtotals AS (
    SELECT DISTINCT 
    CustomerID, 
    MonthDate = DATEADD(MONTH, DATEDIFF(MONTH, 0, DateOrdered), 0), 
    Subtotal = MAX(SUM(JobPrice)) OVER (
     PARTITION BY Customer, DATEADD(MONTH, DATEDIFF(MONTH, 0, DateOrdered), 0) 
     ORDER BY SUM(JobPrice) 
    ) 
    FROM Invoices 
    GROUP BY 
    CustomerID, 
    DateOrdered 
), 
TotalsRanked AS (
    SELECT 
    CustomerID, 
    MonthDate, 
    Subtotal, 
    Ranking = ROW_NUMBER() OVER (PARTITION BY MonthDate ORDER BY Subtotal DESC) 
    FROM MaxDailyTotals 
) 
SELECT 
    Month = MONTH(i.MonthDate), 
    Year = YEAR(i.MonthDate), 
    c.CustomerCode, 
    i.Subtotal, 
    i.Ranking 
FROM TotalsRanked i 
    INNER JOIN Customers ON i.CustomerID = c.ID 
WHERE i.Ranking <= 5 
; 

第一CTE,MaxSubtotals,決定了每個用戶的最大小計&一個月。涉及DISTINCT和窗口聚合函數,它本質上是一個「捷徑」爲以下兩個步驟組成的查詢:

SELECT 
    CustomerID, 
    MonthDate, 
    Subtotal = MAX(Subtotal) 
FROM (
    SELECT 
    CustomerID, 
    MonthDate = DATEADD(MONTH, DATEDIFF(MONTH, 0, DateOrdered), 0), 
    Subtotal = SUM(JobPrice) 
    FROM Invoices 
    GROUP BY 
    CustomerID, 
    DateOrdered 
) s 
GROUP BY 
    CustomerID, 
    MonthDate 

其他CTE,TotalsRanked,只是增加了由客戶和月排名爲發現susbtotals號,分區。作爲最後一步,您只需將行限制爲排名不超過5的行(或者您可能選擇其他時間的行)。

請注意,在這種情況下使用ROW_NUMBER()對行進行排名可以確保您使用Ranking <= 5篩選器時不會超過5行。如果有具有相同大部兩行或多行,則將會獲得不同的排名,最後你可能最終得到這樣的輸出:

Month Year CustomerCode Subtotal Ranking 
    ----- ---- ------------ -------- ------- 
    1  2012 CCC   1500.00 1 
    1  2012 ELITE   1400.00 2 
    1  2012 NOC   900.00 3 
    1  2012 VBC   700.00 4 
    1  2012 HUCC   700.00 5 

-- 1  2012 ABC   690.00 6 -- not returned 
-- 1  2012 ...   ...  ... 

即使有可能是其他客戶的700.00分類彙總在同一個月,他們不會被退回,因爲他們將被分配到5後的排名。

您可以使用RANK()而不是ROW_NUMBER()來解決這個問題。但請注意,你可能最終超過5個每月然後,像這樣的輸出:

Month Year CustomerCode Subtotal Ranking 
    ----- ---- ------------ -------- ------- 
    1  2012 CCC   1500.00 1 
    1  2012 ELITE   1400.00 2 
    1  2012 NOC   900.00 3 
    1  2012 VBC   700.00 4 
    1  2012 HUCC   700.00 4 
    1  2012 ABC   700.00 4 

-- 1  2012 DEF   690.00 7 -- not returned 
-- 1  2012 ...   ...  ... 

客戶提供小計小於700.00不會使它的輸出,因爲他們將有排名從7開始,如果按ROW_NUMBER()排列,這將對應於第一低於700.00的總和的排名。

還有另一個選項DENSE_RANK()。如果您希望在輸出中每月最多輸入5個不同的彙總數,則可能需要使用它。使用DENSE_RANK(),您的輸出可能每月包含的行數多於RANK(),但不同小計的數量恰好爲5(如果原始數據集無法提供5,則數量會更少)。也就是說,你的輸出可能會再看看這樣的:

Month Year CustomerCode Subtotal Ranking 
    ----- ---- ------------ -------- ------- 
    1  2012 CCC   1500.00 1 
    1  2012 ELITE   1400.00 2 
    1  2012 NOC   900.00 3 
    1  2012 VBC   700.00 4 
    1  2012 HUCC   700.00 4 
    1  2012 ABC   700.00 4 
    1  2012 DEF   650.00 5 
    1  2012 GHI   650.00 5 
    1  2012 JKL   650.00 5 

-- 1  2012 MNO   600.00 5 -- not returned 
-- 1  2012 ...   ...  ... 

RANK(),該DENSE_RANK()功能分配相同的排名爲相同的值,但是,不像RANK(),它不會在排名序列中產生差距。


參考文獻: