2010-12-07 78 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