如何编写一个查询来计算表中不存在的记录的值?

时间:2014-04-23 15:54:26

标签: sql netezza

我有一个看起来像这样的表:

MONTH | WIDGET | VALUE
------+--------+------
 Dec  |   A    |   3
 Jan  |   B    |   5
 Feb  |   B    |   6
 Mar  |   B    |   7

我希望编写一个查询,为每个MONTHWIDGET生成当前月份和上个月之间VALUE的差异。所以我想要一个像这样的输出表:

MONTH | WIDGET | VALUE
------+--------+------
 Dec  |   A    |   3
 Jan  |   A    |  -3
 Feb  |   A    |   0
 Mar  |   A    |   0
 Dec  |   B    |   0
 Jan  |   B    |   5
 Feb  |   B    |   1
 Mar  |   B    |   1

如果给定窗口小部件的上个月没有记录值,我想假设上个月的值为零。相反,如果当前月份没有记录值,我想假设当前月份的值为零。

我相信在月份和小部件的所有组合上交叉加入可能会有效,给我一个“脊椎”,我可以将其加入我的数据然后使用coalesce - 但是有更好的方法吗? / p>

编辑:我们可以假设MONTH列实际上有一个数字表示,以便更容易识别前一个。

3 个答案:

答案 0 :(得分:1)

我会使用滞后函数。 IBM Reference我刚刚默认为0,因为先前值不存在但您可以通过多种不同的方式处理这些值。

   create temp table test (
    mth date
    ,widget char(1)
    ,value integer
    )
    distribute on random;

    insert into test values('2013-12-01','A',3);
    insert into test values('2014-01-01','A',-3);
    insert into test values('2014-02-01','A',0);
    insert into test values('2014-03-01','A',0);
    insert into test values('2013-12-01','B',0);
    insert into test values('2014-01-01','B',5);
    insert into test values('2014-02-01','B',1);
    insert into test values('2014-03-01','B',1);


    select *
    ,lag(value,1) over(partition by widget order by mth) as prior_row
    ,value - nvl(lag(value,1) over(partition by widget order by mth),0) as diff
    from test

答案 1 :(得分:0)

行。我已经在MS SQL中解决了这个问题,但它应该可以转移到PostgresQL。我有SQLFiddled the answer

CREATE TABLE WidgetMonths (Month tinyint, Widget varchar(1), Value int)

CREATE TABLE Months (Month tinyint, MonthOrder tinyint)

insert into WidgetMonths Values 
 (12, 'A', 3), 
 (1,'B', 5),
 (2,'B', 6),
 (3,'B', 7);


insert into Months Values
 (12, 1), (1, 2), (2, 3), (3, 4)


Select 
AllWidgetMonths.Widget, 
AllWidgetMonths.Month,
IsNull(wm.Value,0) - IsNull(wmn.Value,0) as Value
from (
      select Distinct Widget, Months.Month, Months.MonthOrder
      from WidgetMonths
      Cross Join months 
      ) AllWidgetMonths
left join WidgetMonths wm on wm.Widget = AllWidgetMonths.Widget
                          AND wm.Month =  AllWidgetMonths.Month

left join WidgetMonths wmn on wmn.Widget = AllWidgetMonths.Widget
                          AND Case When wmn.Month = 12 Then 1 Else wmn.Month + 1 End =  AllWidgetMonths.Month
Order by AllWidgetMonths.Widget, AllWidgetMonths.MonthOrder

我从你的例子中开始使用一个WidgetMonths表,唯一的区别是我将月份转换为代表性整数。

然后我从你的例子中创建了我们感兴趣的所有月份的月份表。如果你想要全年的月份,你可以简单地添加到这个表或找到另一种生成1-12行结果集的方法。 MonthOrder是可选的,只是帮助我完成了你的答案。

正如你所提到的,AllwidgetMonths拥有Cross join,它为我们提供了Widgets和Months的所有组合。通过“小部件”之间的交叉连接可以更好地实现这一点。表和月表。但我不确定这是否存在,所以就这样做了。

我们将WidgetMonths加入到我们的所有小部件月份的主表中,以向我们展示我们有哪个月值。

套管上的技巧然后再次加入同一个表,但这次在连接中添加1到月份数。这会将行向下移动一行。注意我有一个Case语句(在PostgresSql中不确定)来处理第12个月到第1个月的翻转。这有效地为我提供了AllwidgetMonths每行的每个月及其前一个的值。

最后一位是从另一个取一个值。

嘿,先生。我可以尝试将此更新到PostgresSQL,但您可能有更多的知识,可以比我更快地解决它。

答案 2 :(得分:0)

这是获取所需数据的另一种方法。使用两个CTE,包括一个包含月份数的CTE。

可以访问SQL小提琴here

WITH month_order as 
(
  SELECT 'Jan' as month, 1 as month_no, 12 as prev_month_no
  UNION ALL
  SELECT 'Feb' as month, 2 as month_no, 1 as prev_month_no
  UNION ALL
  SELECT 'Mar' as month, 3 as month_no, 2 as prev_month_no
  UNION ALL
  SELECT 'Apr' as month, 4 as month_no, 3 as prev_month_no
  UNION ALL
  SELECT 'May' as month, 5 as month_no, 4 as prev_month_no
  UNION ALL
  SELECT 'Jun' as month, 6 as month_no, 5 as prev_month_no
  UNION ALL
  SELECT 'Jul' as month, 7 as month_no, 6 as prev_month_no
  UNION ALL
  SELECT 'Aug' as month, 8 as month_no, 7 as prev_month_no
  UNION ALL
  SELECT 'Sep' as month, 9 as month_no, 8 as prev_month_no
  UNION ALL
  SELECT 'Oct' as month, 10 as month_no, 9 as prev_month_no
  UNION ALL
  SELECT 'Nov' as month, 11 as month_no, 10 as prev_month_no
  UNION ALL
  SELECT 'Dec' as month, 12 as month_no, 11 as prev_month_no
)  

, values_all_months as 
(
SELECT
    month_order.prev_month_no as prev_month_no
    , month_order.month_no as month_no
    , w4.month as month
    , w4.widget as widget
    , COALESCE(w3.value, 0) as value
FROM widgets w3
RIGHT OUTER JOIN
(
  SELECT
  w1.widget as widget
  ,w2.month as month
FROM
(SELECT
  DISTINCT
  widget
FROM widgets) w1,
(SELECT 
  DISTINCT
  month
FROM widgets) w2
) w4
ON w3.month = w4.month and w3.widget = w4.widget
INNER JOIN month_order
ON w4.month = month_order.month
)

SELECT mo.month, vam1.widget, vam1.value - COALESCE(vam2.value, 0) VALUE
FROM values_all_months vam1
LEFT OUTER JOIN values_all_months vam2
ON vam1.widget = vam2.widget AND vam1.prev_month_no = vam2.month_no
INNER JOIN month_order mo
ON vam1.month_no = mo.month_no
ORDER BY vam1.widget, (SELECT CASE vam1.month_no WHEN 12 THEN 0 ELSE vam1.month_no END);