季度平均销售额与上一季度的平均销售额
Posted
技术标签:
【中文标题】季度平均销售额与上一季度的平均销售额【英文标题】:avg sale of quarter with previous quarter avg sale 【发布时间】:2015-09-02 09:24:44 【问题描述】:我有一张表,其中有各种属性,如区域产品、年份、季度、月份、销售。我必须计算具有相同区域的每个产品的 avg_qtr 销售量并显示它们之前的 avg_qtr 销售量。我已经阅读了有关滞后的信息,但这里无法使用,因为它在重复多少行后并不固定。我的表结构是这样的
Region Product Year Qtr Month Sales
NORTH P1 2015 1 JAN 1000
NORTH P1 2015 1 FEB 2000
NORTH P1 2015 1 MAR 3000
NORTH P1 2015 2 APR 4000
NORTH P1 2015 2 MAY 5000
NORTH P1 2015 2 JUN 6000
NORTH P1 2015 3 JUL 7000
NORTH P1 2015 3 AUG 8000
NORTH P1 2015 3 SEP 9000
NORTH P1 2015 4 OCT 1000
NORTH P1 2015 4 DEC 4000
NORTH P1 2015 4 NOV 2000
NORTH P3 2015 1 FEB 1000
NORTH P3 2015 1 FEB 9000
NORTH P3 2015 2 APR 2000
NORTH P3 2015 3 JUL 8000
NORTH P1 2016 1 MAR 3000
NORTH P1 2016 1 FEB 1000
NORTH P1 2016 1 JAN 2000
SOUTH P1 2015 1 JAN 2000
SOUTH P1 2015 1 FEB 3000
SOUTH P1 2015 1 JAN 4000
SOUTH P2 2015 1 MAR 1000
SOUTH P2 2015 1 JAN 8000
SOUTH P2 2015 1 FEB 9000
SOUTH P2 2015 2 JUN 9000
SOUTH P2 2015 2 MAY 8000
SOUTH P2 2015 2 APR 2000
SOUTH P2 2015 3 SEP 4000
SOUTH P2 2015 3 AUG 2000
SOUTH P2 2015 3 JUL 1000
SOUTH P2 2015 4 NOV 2000
SOUTH P2 2015 4 DEC 1000
SOUTH P2 2015 4 OCT 5000
SOUTH P3 2015 3 AUG 9000
SOUTH P3 2015 4 OCT 1000
SOUTH P3 2015 4 NOV 3000
SOUTH P2 2016 1 JAN 2000
SOUTH P2 2016 1 JAN 4000
我编写了计算当前 qtr 并显示前一个与当前一个平均值的查询
WITH AvgSales
AS (SELECT
region,
product,
year,
qtr,
ROUND(AVG(sales), 2) AS avg_Sale
FROM one
GROUP BY region,
product,
year,qtr
)
SELECT
s.region,
s.product,
s.year,
s.month,
s.sales,
avg.qtr,
avg.avg_Sale AS Qtr_Avg_Sale,
prev.avg_sale AS Prev_Qtr_Avg_Sale
FROM one s
JOIN AvgSales avg
ON s.region = avg.region
AND s.product = avg.product
AND s.QTR = avg.qtr
AND s.year = avg.year
LEFT JOIN AvgSales prev
ON (s.region = prev.region
AND s.product = prev.product
AND s.year - 1 = prev.year
and s.qtr=1
AND prev.qtr = 4) or
(s.region = prev.region
AND s.product = prev.product
AND s.year = prev.year
AND s.qtr - 1 = prev.qtr) ;
我能够获得该产品的当前平均值和之前的平均值,但反之则不行。我不确定如何显示当前季度没有任何销售的该季度的先前平均值。 我想要这样的输出-
Region Product Year qtr month sale avg_Sale prev_avg_sale
NORTH P1 2015 1 JAN 1000 2000
NORTH P1 2015 1 FEB 2000 2000
NORTH P1 2015 1 MAR 3000 2000
NORTH P1 2015 2 APR 4000 5000 2000
NORTH P1 2015 2 MAY 5000 5000 2000
NORTH P1 2015 2 JUN 6000 5000 2000
NORTH P1 2015 3 JUL 7000 8000 5000
NORTH P1 2015 3 AUG 8000 8000 5000
NORTH P1 2015 3 SEP 9000 8000 5000
NORTH P1 2015 4 OCT 1000 2333.33 8000
NORTH P1 2015 4 NOV 2000 2333.33 8000
NORTH P1 2015 4 DEC 4000 2333.33 8000
SOUTH P2 2015 1 JAN 8000 6000
SOUTH P2 2015 1 FEB 9000 6000
SOUTH P2 2015 1 MAR 1000 6000
SOUTH P2 2015 2 APR 2000 6333.33 6000
SOUTH P2 2015 2 MAY 8000 6333.33 6000
SOUTH P2 2015 2 JUN 9000 6333.33 6000
SOUTH P2 2015 3 JUL 1000 2333.33 6333.33
SOUTH P2 2015 3 AUG 2000 2333.33 6333.33
SOUTH P2 2015 3 SEP 4000 2333.33 6333.33
SOUTH P2 2015 4 OCT 5000 2666.67 2333.33
SOUTH P2 2015 4 NOV 2000 2666.67 2333.33
SOUTH P2 2015 4 DEC 1000 2666.67 2333.33
NORTH P3 2015 1 FEB 9000 5000
NORTH P3 2015 1 FEB 1000 5000
NORTH P3 2015 2 APR 2000 2000 5000
NORTH P3 2015 3 JUL 8000 8000 2000
SOUTH P3 2015 3 AUG 9000 9000
SOUTH P3 2015 4 OCT 1000 2000 9000
SOUTH P3 2015 4 NOV 3000 2000 9000
NORTH P1 2016 1 JAN 2000 2000 2333.33
NORTH P1 2016 1 FEB 1000 2000 2333.33
NORTH P1 2016 1 MAR 3000 2000 2333.33
NORTH P2 2016 2 2000
SOUTH P2 2016 1 JAN 2000 3000 2666.67
SOUTH P2 2016 1 JAN 4000 3000 2666.67
SOUTH P2 2016 2 3000
SOUTH P1 2015 1 JAN 4000 3000
SOUTH P1 2015 1 JAN 2000 3000
SOUTH P1 2015 1 FEB 3000 3000
【问题讨论】:
也添加了输出 【参考方案1】:根据最新要求进行了编辑。
您的问题是您试图通过操纵 QTR 和 YEAR 来获取 previous_avg。我正在使用 RANK 函数,按照我希望数据排名的方式进行排序。在联接中,我确保平均区域 = 上一区域,并且不考虑自上一季度以来的年份可能是上一季度的平均年份 Q1;这样更干净。
--Build the test table
IF OBJECT_ID('SALES','U') IS NOT NULL
DROP TABLE SALES
CREATE TABLE SALES
(
Region VARCHAR(255)
, Product VARCHAR(10)
, [Year] INT
, QTR INT
, [Month] VARCHAR(19)
, Sales DECIMAL(19,4)
);
INSERT SALES
VALUES
('NORTH', 'P1', 2015, 1, 'JAN', 1000)
,('NORTH', 'P1', 2015, 1, 'FEB', 2000)
,('NORTH', 'P1', 2015, 1, 'MAR', 3000)
,('NORTH', 'P1', 2015, 2, 'APR', 4000)
,('NORTH', 'P1', 2015, 2, 'MAY', 5000)
,('NORTH', 'P1', 2015, 2, 'JUN', 6000)
,('NORTH', 'P1', 2015, 3, 'JUL', 7000)
,('NORTH', 'P1', 2015, 3, 'AUG', 8000)
,('NORTH', 'P1', 2015, 3, 'SEP', 9000)
,('NORTH', 'P1', 2015, 4, 'OCT', 1000)
,('NORTH', 'P1', 2015, 4, 'DEC', 4000)
,('NORTH', 'P1', 2015, 4, 'NOV', 2000)
,('NORTH', 'P3', 2015, 1, 'FEB', 1000)
,('NORTH', 'P3', 2015, 1, 'FEB', 9000)
,('NORTH', 'P3', 2015, 2, 'APR', 2000)
,('NORTH', 'P3', 2015, 3, 'JUL', 8000)
,('NORTH', 'P1', 2016, 1, 'MAR', 3000)
,('NORTH', 'P1', 2016, 1, 'FEB', 1000)
,('NORTH', 'P1', 2016, 1, 'JAN', 2000)
,('SOUTH', 'P1', 2015, 1, 'JAN', 2000)
,('SOUTH', 'P1', 2015, 1, 'FEB', 3000)
,('SOUTH', 'P1', 2015, 1, 'JAN', 4000)
,('SOUTH', 'P2', 2015, 1, 'MAR', 1000)
,('SOUTH', 'P2', 2015, 1, 'JAN', 8000)
,('SOUTH', 'P2', 2015, 1, 'FEB', 9000)
,('SOUTH', 'P2', 2015, 2, 'JUN', 9000)
,('SOUTH', 'P2', 2015, 2, 'MAY', 8000)
,('SOUTH', 'P2', 2015, 2, 'APR', 2000)
,('SOUTH', 'P2', 2015, 3, 'SEP', 4000)
,('SOUTH', 'P2', 2015, 3, 'AUG', 2000)
,('SOUTH', 'P2', 2015, 3, 'JUL', 1000)
,('SOUTH', 'P2', 2015, 4, 'NOV', 2000)
,('SOUTH', 'P2', 2015, 4, 'DEC', 1000)
,('SOUTH', 'P2', 2015, 4, 'OCT', 5000)
,('SOUTH', 'P3', 2015, 3, 'AUG', 9000)
,('SOUTH', 'P3', 2015, 4, 'OCT', 1000)
,('SOUTH', 'P3', 2015, 4, 'NOV', 3000)
,('SOUTH', 'P2', 2016, 1, 'JAN', 2000)
,('SOUTH', 'P2', 2016, 1, 'JAN', 4000);
--CTE TO CAPTURE AVG SALES BY REGION, PRODCUT, YEAR, QTR; OMIT PRODUCT IF YOU WANT STRAIGHT UP QUARTER AVG, REGARDLESS OF PRODCUCT
WITH cteAvgSales AS
(
SELECT Region, Product, [Year], QTR, AVG(Sales) current_avg
, RANK() OVER(ORDER BY Region, Product, [Year], QTR) AS RNK
FROM SALES
GROUP BY Region, Product, [Year], QTR
)
SELECT s.Region, s.Product, s.[Year] AS [year], s.QTR AS [quarter], s.[Month], s.Sales, a.current_avg, p.current_avg AS previous_avg
FROM SALES s
INNER JOIN cteAvgSales a ON a.Region = s.Region
AND a.Product = s.Product
AND a.[Year] = s.[Year]
AND a.QTR = s.QTR
LEFT JOIN cteAvgSales p ON p.Region = a.Region
AND p.Product = s.Product
AND p.RNK=a.RNK-1
ORDER BY s.Region, s.Product, s.[Year], s.QTR
【讨论】:
对不起,我的要求随着时间的推移而改变,现在我也有一个区域列,现在我必须根据每个区域、每个产品、每年和每个季度进行分区。在此之前它非常不同我现在计算了简单的平均值,它完全不同。因此,我尝试在您的示例的分区中添加区域和产品 qtr,但是对于上一季度没有销售的某些产品,它会在上一销售列中显示其他产品平均销售,如果当前季度没有销售,还有一件事它应该是以前的平均销售,我一直到最后一个条件,但不知道如何 显示当前季度是否没有销售 :( 我已经编写了代码,但不满足最后一个条件,如果当前没有销售,它应该显示 null 并显示以前的销售avg 在上一个 avg 列中。如果您能提供帮助,我也将添加我的代码 更新答案并解释您在回答中遇到困难的原因。我放弃了您使用的方法,因为它太混乱且难以匹配 avg 和 prv_avg quaters。享受吧! 再次感谢朋友!但无论如何你都没有明白我的意思,非常感谢你的回答我已经解决了我的问题。如果当前季度没有销售,你的回答错过了我获得上一季度平均值的最后一点…… 如果我没抓住重点,说明重点不是很清楚;您发布的数据在创建的输出中完全显示。如果您有其他解决方案可以更准确地回答您的问题,请将其发布,以便您提出的问题得到正确答案。【参考方案2】:如果您有一个排序值作为排序依据,您可以使用分析函数的窗口子句,因此首先创建一个年份和 qtr 的 DENSE_RANK
ing,然后在您的分析函数中使用该排名:
with t1 as (
select one.*
, dense_rank() over (order by year, qtr) qord
from one
)
select product
, year
, qtr
, month
, sales
, round(avg(sales) over (partition by qord),2) qtr_avg
, round(avg(sales) over (order by qord
range between 1 preceding
and 1 preceding),2) prev_qtr_avg
from t1
上述解决方案假定样本数据集中提供了密集的季度数据,但是,如果沿季度维度的数据稀疏,您可以首先像此查询中那样对数据进行密集化:
with qtrs as (select level qtr from dual connect by level <=4)
, t1 as (
select product
, year
, qtrs.qtr
, month
, sales
, dense_rank() over (order by year, qtrs.qtr) qord
from qtrs
left outer join one partition by (year)
on one.qtr = qtrs.qtr
)
select product
, year
, qtr
, month
, sales
, round(avg(sales) over (partition by qord),2) qtr_avg
, round(avg(sales) over (order by qord
range between 1 preceding
and 1 preceding),2) prev_qtr_avg
from t1
这可确保对于数据中表示的每一年,每个季度至少存在一行,因此 QORD 将在每个季度进行枚举,数据中的差距将导致计算出的季度平均值出现差距。
您还可以通过利用 YEAR 和 QTR 的数字性质来改变 QORD 的计算方式来实现类似的效果,如下例所示:
with t1 as (select one.*, year*4+qtr qord from one)
select product
, year
, qtr
, month
, sales
, round(avg(sales) over (partition by qord),2) qtr_avg
, round(avg(sales) over (order by qord
range between 1 preceding
and 1 preceding),2) prev_qtr_avg
from t1
这里不需要加密,但它仍然正确地在 prev_qtr_avg 中留下空白,但它确实遗漏了加密数据包括的缺失季度的记录。
结合最后两个示例,并添加您对区域的新要求,如果需要针对每个不同的区域、产品和年份,每个季度将返回或生成至少一行数据。两个平均值均按地区和产品划分,并根据具体情况按当前或上一季度计算:
with qtrs(qtr) as (select level from dual connect by level <= 4)
, t1 as (
select region, product, year, q.qtr, month, sales, year*4+q.qtr qord
from qtrs q
left join one partition by (region, product, year)
on q.qtr = one.qtr
)
select region
, product
, year
, qtr
, month
, sales
, round(avg(sales) over (partition by region, product, qord),2) avg_sale
, round(avg(sales) over (partition by region, product
order by qord
range between 1 preceding
and 1 preceding),2) prev_avg_sale
from t1
order by year, region, qtr, product;
【讨论】:
在您的回答中,您使用了介于之间的范围,在我的问题中,我们可以在该季度进行无销售,因此它将获取上一季度的销售,而不是应该显示 null 像 if我们在 2015 年第 3 季度没有销售,因此 2015 年第 4 季度的平均值应该为零 就像我们在 2012 年上个季度没有销售一样,它在我有问题的输出中显示为 null 您的查询将连接到最后一个平均值,我不希望这样 :( 我添加了第二个示例,展示了如何使用分区外连接来加密数据,以便获得所需的间隙 对不起,我的要求随着时间的推移而改变,现在我也有一个区域列,现在我必须根据每个区域、每个产品、每年和每个季度进行分区。在此之前它非常不同我现在计算了简单的平均值,它完全不同。因此,我尝试在您的示例分区中添加区域和产品 qtr,但对于上一季度没有销售的某些产品,它会在之前的销售列中显示其他产品的平均销售量。 还有一件事,如果当前季度没有销售,它应该是以前的平均销售。我一直计算到最后一个条件,但是如果当前季度没有销售,我没有得到它,如何计算销售。如果你能帮助我,我也会添加我的代码【参考方案3】:您需要加入一些子选择语句,这些语句将获得上一季度的平均值。您还需要合并两个语句,因为对于第 2、3、4 季度,您可以简单地将 join 语句中的一个季度减去前一个 qtr avg,但是当它是第一个季度时,您需要减去一年并设置之前的 qtr = 4。此语句应该适用于您所描述的内容。
--handles when the current quarter being viewed is 2,3,or 4 because those would still be in the same year when looking at the previous quarter
select t1.product,
t1.year,
t1.month,
t1.sales ,
t1.qtr,
round(avg(t1.sales) over (partition by t1.qtr,t1.year),2) as av,
t2.prev_av
from one t1
left join ( select
product,
year,
month,
sales ,
qtr,
round(avg(sales) over (partition by qtr,year),2) as prev_av
from one
) t2
on t1.year = t2.year
and (t1.qtr - 1) = t2.qtr
where t1.qtr in (2,3,4)
union
--handles the 1st quarter of the year when you need to grab the 4th quarter of the previous year for the previous avg
select t3.product,
t3.year,
t3.month,
t3.sales ,
t3.qtr,
round(avg(t3.sales) over (partition by t3.qtr,t3.year),2) as av,
t4.prev_av
from one t3
left join ( select
product,
year,
month,
sales,
qtr,
round(avg(sales) over (partition by qtr,year),2) as prev_av
from one
) t4
on (t3.year - 1) = t4.year
and t4.qtr = 4
where t3.qtr = 1;
【讨论】:
您的查询工作正常,但它很重,所以我们可以让它更优化 您查询的答案是正确的,尽管我的要求后来发生了变化。无论如何,谢谢你....投票给你的答案 你能做到我想要的吗?? 是的,需求的哪一部分发生了变化? 只用了不多的select语句,主要是消除union以上是关于季度平均销售额与上一季度的平均销售额的主要内容,如果未能解决你的问题,请参考以下文章