2010-12-07 130 views
2

我有一個查詢,我建立的數據可以將淨銷售額YTD提高到最近完成的月份。查詢聯合會根據發票和貸項憑證彙總。它效果很好。我在另一個工具中使用查詢,通過卡片碼進行求和,並允許我執行有趣的操作等等。下面是該查詢:按年份選擇最高百分比

select x.cardcode, 
     x.customer, 
     case 
     when x.rep is null then (select slpname from ocrd inner join oslp on ocrd.slpcode = oslp.slpcode where ocrd.cardcode = x.cardcode) 
     else 
      x.rep 
     end as rep, 
     x.city, 
     x.state, 
     x.country, 
     case 
     when isnumeric(x.total) = 0 then 0 
     else x.total 
     end as [net total], 
     x.docdate 

from (
    select t0.cardcode as cardcode, 
     t0.[cardname] as customer, 
     t1.city as city, 
     t1.state as state, 
     t1.country as country, 
     t4.slpname as rep, 
     sum(t3.linetotal) - t2.discsum as total, 
     t2.docdate as [docdate] 

    from ocrd t0 
     inner join crd1 t1 on (t0.cardcode = t1.cardcode and t0.shiptodef = t1.address) 
     left outer join oinv t2 on t0.cardcode = t2.cardcode 
     left outer join inv1 t3 on t2.docentry = t3.docentry 
     left outer join oslp t4 on t2.slpcode = t4.slpcode 

    where t0.[cardtype] = 'C' and 
      t1.adrestype = 'S' 

    group by t0.cardcode, t0.cardname, t1.city, t1.state, t1.country, t4.slpname, t2.discsum, t2.docdate 

    union all 

    select t0.cardcode as cardcode, 
     t0.cardname as customer, 
     t1.city as city, 
     t1.state as state, 
     t1.country as country, 
     t4.slpname as rep, 
     -1*(sum(t3.linetotal) - t2.discsum) as total, 
     t2.docdate 

    from ocrd t0 
     inner join crd1 t1 on (t0.cardcode = t1.cardcode and t0.shiptodef = t1.address) 
     left outer join orin t2 on t0.cardcode = t2.cardcode 
     left outer join rin1 t3 on t2.docentry = t3.docentry 
     left outer join oslp t4 on t2.slpcode = t4.slpcode 

    where t0.[cardtype] = 'C' and 
      t1.adrestype = 'S' 

    group by t0.cardcode, 
      t0.cardname, 
      t1.city, 
      t1.state, 
      t1.country, 
      t4.slpname, 
      t2.discsum, 
      t2.docdate) x 

where (x.docdate between '2008/01/01' and dateadd(day, -1, '2008/' + cast(month(getdate()) as varchar(2)) + '/01') 
     or x.docdate between '2009/01/01' and dateadd(day, -1, '2009/' + cast(month(getdate()) as varchar(2)) + '/01') 
      or x.docdate between '2010/01/01' and dateadd(day, -1, '2010/' + cast(month(getdate()) as varchar(2)) + '/01')) 

group by x.cardcode, x.customer, x.rep, x.city, x.state, x.country, x.total, x.docdate 

現在,我想修改查詢返回的前n,說20,客戶的總淨值每年的百分比。這是我遇到麻煩的地方。我首先使用SQL Server,所以我想我會嘗試使用row_number()結束(分區....但我沒有得到它很正確(我知道它不正確,因爲我可以檢查它反對報告,我是反向工程。)這是我第一次嘗試。

select m.Cardcode, m.Customer, m.Rep, m.City, m.State, m.Country, m.Nettotal as 'Net Total', m.docdate as 'Posting Date' 
from (
    select t.cardcode, t.customer, t.rep, t.city, t.state, t.country, t.nettotal, t.docdate, row_number() over(partition by t.docdate order by t.nettotal desc) as rownum 
    from (
     select x.cardcode, 
      x.customer, 
      case 
       when x.rep is null then (select slpname from ocrd inner join oslp on ocrd.slpcode = oslp.slpcode where ocrd.cardcode = x.cardcode) 
       else 
       x.rep 
      end as rep, 
      x.city, 
      x.state, 
      x.country, 
      case 
       when isnumeric(x.total) = 0 then 0 
       else x.total 
      end as nettotal, 
      x.docdate 

     from (
     select t0.cardcode as cardcode, 
       t0.[cardname] as customer, 
       t1.city as city, 
       t1.state as state, 
       t1.country as country, 
       t4.slpname as rep, 
       sum(t3.linetotal) - t2.discsum as total, 
       t2.docdate as docdate 

     from ocrd t0 
      inner join crd1 t1 on (t0.cardcode = t1.cardcode and t0.shiptodef = t1.address) 
      left outer join oinv t2 on t0.cardcode = t2.cardcode 
      left outer join inv1 t3 on t2.docentry = t3.docentry 
      left outer join oslp t4 on t2.slpcode = t4.slpcode 

     where t0.[cardtype] = 'C' and 
        t1.adrestype = 'S' 

     group by t0.cardcode, 
       t0.cardname, 
       t1.city, 
       t1.state, 
       t1.country, 
       t4.slpname, 
       t2.discsum, 
       t2.docdate 

     union all 

     select t0.cardcode as cardcode, 
       t0.cardname as customer, 
       t1.city as city, 
       t1.country as country, 
       t1.state as state, 
       t4.slpname as rep, 
       -1*(sum(t3.linetotal) - t2.discsum) as total, 
       t2.docdate 

     from ocrd t0 
      inner join crd1 t1 on (t0.cardcode = t1.cardcode and t0.shiptodef = t1.address) 
      left outer join orin t2 on t0.cardcode = t2.cardcode 
      left outer join rin1 t3 on t2.docentry = t3.docentry 
      left outer join oslp t4 on t2.slpcode = t4.slpcode 

     where t0.[cardtype] = 'C' and 
        t1.adrestype = 'S' 

     group by t0.cardcode, 
       t0.cardname, 
       t1.city, 
       t1.state, 
       t1.country, 
       t4.slpname, 
       t2.discsum, 
       t2.docdate) x 

    where (x.docdate between '2008/01/01' and dateadd(day, -1, '2008/' + cast(month(getdate()) as varchar(2)) + '/01') 
      or x.docdate between '2009/01/01' and dateadd(day, -1, '2009/' + cast(month(getdate()) as varchar(2)) + '/01') 
       or x.docdate between '2010/01/01' and dateadd(day, -1, '2010/' + cast(month(getdate()) as varchar(2)) + '/01')) 

    group by x.cardcode, 
      x.customer, 
      x.rep, 
      x.city, 
      x.state, 
      x.country, 
      x.total, 
      x.docdate) as t 
) as m 

where rownum <= 20 

走這條路是麻煩的,即使我這樣做是正確,因爲它不會讓我獲得前百分之n,剛剛與前n

我還沒有嘗試使用交叉應用或子選擇來實現我想要的結果。

有人可以幫助我解決這個問題嗎?另外,它是如何寫入的可能效率不高nd硬編碼的日期範圍選擇不是一個好的解決方案。我想有很多要改進:)

您的幫助表示讚賞。

+0

什麼版本的SQL Server您使用的是?你可以使用2008年嗎? – Gabe 2010-12-07 05:16:00

回答

1

您可以使用SQL Server中的一個排名功能 - 但不是ROW_NUMBER(),而是使用NTILE()

NTILE()會將結果集分解爲您指定的許多數據塊 - 因爲您希望排在前20%,您可能會使用NTILE(5)

所以你應該CTE看起來是這樣的:

WITH CustomerPerYear AS 
(
    SELECT 
     c.Name, s.Sales, 
     NTILE(5) OVER (PARTITION BY c.CustomerID ORDER BY s.Amount DESC) as 'NTile' 
    FROM 
     dbo.Customer c 
    INNER JOIN 
     dbo.Sales s ON s.CustomerID = c.CustomerID 
) 
SELECT * 
FROM CustomerPerYear 
WHERE NTile = 1 

所以基本上你被客戶partioning您的數據,然後將你的排名每個客戶的銷售成銷售金額排序5個NTILE組。對於每個客戶,NTILE = 1是您銷售額的前20%。

查看MSDN docs on NTILE瞭解更多詳情,如果需要的話。

+0

感謝您的回覆。我明天會檢查你的建議。 – m7d 2010-12-07 06:37:26

2

如果你需要一個更獨立的百分比(比方說17%),可以使用ROW_NUMBER和計數:

with cSalesPerYear as (
    select s.Year, c.Customer, 
      RankNo = rank() over (partition by s.Year order by S.Amount desc), 
      RowNo = row_number() over (partition by s.Year order by S.Amount desc), 
      CountOrders = count() over (partition by s.Year) 
    from dbo.Customers c 
    inner join dbo.Sales s 
     on s.CustomerID = c.CustomerID 
) 
select * 
from cSalesPerYear 
where RowNo <= @Percentage * CountOrders 
    -- RankNo <= @Percentage * CountOrders --<-- "with ties" version 
+0

偉大的想法Manfred。我試圖做類似的事情(不包括上面),但完全錯過了你的方法。欣賞它。 – m7d 2010-12-07 21:43:59