如何计算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/value2100/205,明年的值是 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 获取每个 IDVALUE1/VALUE2 的百分比变化,您可以这样做:

lead(val) OVER(PARTITION BY ID ORDER BY ID, YEAR)

其中 VALVALUE1/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:返回特定范围内的矩阵值,范围以元组形式给出(从,到)

如何根据元组的索引值从列表中删除重复的元组,同时保持元组的顺序? [复制]

SQL ALCHEMY - 如何返回 query(model).all() 作为值的元组而不是模型实例

如何更新循环的倍数里面的名单?

Swift flatMap:如何从数组中仅删除元组中特定元素为零的元组?