如何计算在SQL中未在特定值范围内更改的元组数量

时间:2015-03-25 08:41:45

标签: sql oracle join rows percentage

我使用以下表格: [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%。
  • 应计算金额,此国家/地区所有公司的百分比(百分比1)以及此国家/地区的所有公司(百分比2)。

结果应该是:

| COUNTRY | CLASS | AMOUNT | PERCENTAGE1 | PERCENTAGE2 |
|---------|-------|--------|-------------|-------------|
|  Russia |     A |      3 |        0.75 |       0.428 |
|  Russia |     B |      0 |           0 |       0.428 |

有人能给我一个方法吗?

3 个答案:

答案 0 :(得分:0)

LAG()或LEAD()分析函数可以为您提供上一年/明年的值,以便直接比较"今年与去年",或者您也可以这样做基于" a.year = b.year - 1"的联接。我会对两者进行基准测试,以确定哪种方法最适合您的数据量和分发。

然后,您将允许应用CASE语句将值变化范围分类为小于或大于2%。

然后,您可以根据该分类聚合数据以获取所需的值 - Ratio_to_Report分析函数可能会有所帮助。

答案 1 :(得分:0)

您可以使用分析 LEAD 功能来完成此操作。其余的是简单的数学和计算。

要根据年份获取每个 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>

答案 2 :(得分:0)

从你的话“2006年到2007年间是1.23%(2006年价值1 /价值2 = 5和2007年价值1 /价值2 = 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