【问题标题】:How to count the amount of tuples who don't change in a specific value range in SQL如何计算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% 的范围内,应该计算在内。
  • 好的,看我的回答。

标签: sql oracle join rows percentage


【解决方案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
      

      【讨论】:

        猜你喜欢
        • 2023-02-22
        • 1970-01-01
        • 2013-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-19
        • 1970-01-01
        相关资源
        最近更新 更多