我有一个我建立的查询,它将净销售年初至今恢复到最近完成的月份。查询联合总计来自发票和贷记凭证。它很棒。我在另一个工具中使用查询,该工具通过cardcode进行求和,并允许我做有趣的枢轴等等。这是查询:
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()over(partition ....但我没有把它弄得很对(我知道它不对,因为我可以检查它反对报告我是逆向工程这是我的第一次尝试:
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个。
我还没有尝试使用交叉应用或子选择来实现我想要的结果。
有人可以帮助我做到这一点吗?此外,如何编写它可能效率不高,硬编码日期范围选择不是一个好的解决方案。我想还有很多需要改进的地方:)
感谢您的帮助。
答案 0 :(得分: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
答案 1 :(得分: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
所以基本上你是按客户分配你的数据,然后你将每个客户的销售额排在按销售额订购的5个NTile组中。对于每个客户,NTILE = 1是您销售额的前20%。
如果需要,请参阅MSDN docs on NTILE了解更多详情。