如何计算SQL中特定值范围内不变的元组数量
Posted
技术标签:
【中文标题】如何计算SQL中特定值范围内不变的元组数量【英文标题】:How to count the amount of tuples who don't change in a specific value range in SQL 【发布时间】:2015-03-25 08:41:45 【问题描述】:我使用下表: [http://sqlfiddle.com/#!4/eb1b79/1]
表公司:
| ID | CNAME | COUNTRY | CLASS |
|----|-------|---------|-------|
| 1 | ABC | Russia | A |
| 2 | DEF | Russia | B |
表格值:
| ID | VALUE1 | VALUE2 | YEAR |
|----|--------|--------|------|
| 1 | 100 | 20 | 2005 |
| 1 | 200 | 40 | 2006 |
| 1 | 400 | 81 | 2007 |
| 1 | 101 | 16 | 2008 |
| 2 | 300 | 22 | 1999 |
| 2 | 900 | 30 | 2001 |
| 2 | 600 | 10 | 2002 |
我想做的是:
计算每个公司和年份的 value1/value2,然后计算每个国家和类别的数量公司,他们不在下一个给定年份之间变化更多超过 2%。 应给出计算出的金额,占该国家/地区所有公司的百分比 (percentage1) 以及该国家/地区/类别中所有公司的百分比 (percentage2)。结果应该是:
| COUNTRY | CLASS | AMOUNT | PERCENTAGE1 | PERCENTAGE2 |
|---------|-------|--------|-------------|-------------|
| Russia | A | 3 | 0.75 | 0.428 |
| Russia | B | 0 | 0 | 0.428 |
谁能给我一个方法?
【问题讨论】:
百分比的导出规则是什么?是每年的value1/value2
值吗?我可以回答你的问题,但百分比部分不清楚。你能解释更多吗。例如,2005 年 ID 1 的 value1/value2
是 100/20
即 5
,明年的值是 200/40
又是 5
,所以根本没有变化。
你是绝对正确的。所以 2005 年和 2006 年之间的变化是 0%,而 2006 年和 2007 年之间的变化是 1.23%(2006 年 value1/value2=5 和 2007 年 value1/value2=4.938),所以这几年之间的变化将在 2% 的范围内,应该计算在内。
好的,看我的回答。
【参考方案1】:
LAG() 或 LEAD() 分析函数将为您提供上一年/下一年的值,以便直接比较“今年与去年”,或者您可以使用基于“a.year = b.年 - 1"。我会对两者进行基准测试,看看哪个最适合您的数据量和分布。
这将允许您应用 CASE 语句将值变化范围分类为小于或大于 2%。
然后您可以根据该分类聚合数据以获得您需要的值——Ratio_to_Report 分析函数可能会有所帮助。
【讨论】:
【参考方案2】:您可以使用 Analytic LEAD 功能来做到这一点。剩下的就是简单的数学和计算。
要根据 YEAR 获取每个 ID 的 VALUE1/VALUE2
的百分比变化,您可以这样做:
lead(val) OVER(PARTITION BY ID ORDER BY ID, YEAR)
其中 VAL 是 VALUE1/VALUE2
。
我们来看一个测试用例:
设置
SQL> SELECT * FROM companies;
ID CNA COUNTR C
---------- --- ------ -
1 ABC Russia A
2 DEF Russia B
SQL>
SQL> SELECT * FROM vals;
ID VALUE1 VALUE2 YEAR
---------- ---------- ---------- ----------
1 100 20 2005
1 200 40 2006
1 400 81 2007
1 101 16 2008
2 300 22 1999
2 900 30 2001
2 600 10 2002
7 rows selected.
SQL>
现在,实现上述逻辑会给我们:
SQL> WITH data1 AS
2 ( SELECT t.*, ROUND(value1/value2, 2) val FROM vals t ORDER BY YEAR
3 ),
4 data2 AS
5 (SELECT t.*,
6 lead(val) OVER(PARTITION BY ID ORDER BY ID, YEAR) prev
7 FROM data1 t
8 )
9 SELECT t.*, ROUND(((val - prev)/val)* 100, 2) percentage FROM data2 t;
ID VALUE1 VALUE2 YEAR VAL PREV PERCENTAGE
---------- ---------- ---------- ---------- ---------- ---------- ----------
1 100 20 2005 5 5 0
1 200 40 2006 5 4.94 1.2
1 400 81 2007 4.94 6.31 -27.73
1 101 16 2008 6.31
2 300 22 1999 13.64 30 -119.94
2 900 30 2001 30 60 -100
2 600 10 2002 60
7 rows selected.
SQL>
【讨论】:
【参考方案3】:从你的话 “2006 年到 2007 年之间是 1.23%(2006 年 value1/value2=5 和 2007 年 value1/value2=4.938)” 我总结道,
每年变化的公式是1 - current (v1/v2)/previous (v1/v2)
。此外,具有未知先前值的行似乎
不应包括在进一步的计算中。如果不是这样,请删除过滤器where lvv is not null
。
应该删除 value1 或 value2 等于 0 的所有行,因为在当前或后续步骤中它们可以生成
除以零,但我没有在这里过滤它们,因为我不确定在这种情况下你想做什么。
“谁在下一个给定年份之间的变化不会超过 2%” 是什么意思还不太清楚。
我为此使用了条件abs(1-vv/lvv) <= .02
,但您可能希望将其更改为1-vv/lvv < .02
。
最终结果与你的不同,我怀疑你只是展示了结果应该是什么样子(例如,百分比 2 中的最后一个值为 0.428,数量为 0 - 这是不一致的)。 如果这不是您想要的,请编辑您的帖子,添加更多示例和与输入匹配的所需输出,这样我们可以验证某些内容:-)
SQLFiddle
with
step1 as (select id, year, cname, country, class, round(value1/value2, 8) vv,
round(lag(value1/value2) over (partition by id order by year), 8) lvv
from vals v join companies c using (id) where value2<>0 ),
step2 as (select step1.*, round(1-vv/lvv, 8) change_value,
case when abs(1-vv/lvv) <= .02 then 1 end as change,
count(1) over (partition by country) cc,
count(1) over (partition by country, class) ccc
from step1 where lvv is not null )
select country, class, count(change) amount,
round(count(change) / max(cc), 4) percentage1,
round(count(change) / max(ccc), 4) percentage2
from step2 group by country, class order by country, class
输出(我添加了一些其他国家的公司):
COUNTRY CLASS AMOUNT PERCENTAGE1 PERCENTAGE2
------- ----- ------ ----------- -----------
Brazil A 1 1 1
Canada A 1 0,25 0,5
Canada B 2 0,5 1
Russia A 2 0,4 0,6667
Russia B 0 0 0
【讨论】:
以上是关于如何计算SQL中特定值范围内不变的元组数量的主要内容,如果未能解决你的问题,请参考以下文章
Python:返回特定范围内的矩阵值,范围以元组形式给出(从,到)
如何根据元组的索引值从列表中删除重复的元组,同时保持元组的顺序? [复制]