我有一个查询可以为客户提取月/年总数,并添加ntile排名。如果我能够得出ntile 1,2,3,4和5的最大小计,我几乎会得到我所追求的,但我不知道如何继续。
例如,我想要的结果如下:
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
除了 - 我希望排名与第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 :(得分:3)
如果我正确地读了你,你所追求的是
对于范围内的每个月,
显示当月具有最大SUM的5位客户
并针对每个客户,显示相应的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
为了解释,第一部分(WITH块)只是编写子查询的一种奇特方式,它按月和客户对数据进行GROUP。表达式DATEADD(D,1-DAY(i.DateOrdered),i.DateOrdered)
将每个日期转换为该月的第一天,以便可以按月轻松地对数据进行分组。以传统形式编写的下一个子查询在每个月内通过小计添加RANK列,最后选择该列以获得前5 *。
请注意,RANK允许相同的排名,如果其中3个在第4位排名相同,最终可能会显示6个客户一个月。如果这不是您想要的,那么您可以更改单词{{1到RANK
,它会在相等的小计之间随机打破。
答案 1 :(得分: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
,以便获得正确的输出。
答案 2 :(得分: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
答案 3 :(得分:0)
如果没有 double 排名,我无法看到如何解决这个问题:
您需要获得每位客户的最大金额&amp;一个月。
然后,您需要每个月检索找到的前五笔款项。
以下是我如何做到这一点:
;
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
确定每个客户的最大小计数&amp;月。涉及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()
不同,它不会在排名顺序中产生差距
参考文献: