Postgres为缺失的行返回0

时间:2015-01-30 18:15:19

标签: sql postgresql

我有3个表格需要报告:

    *dates*         
date_sk | full_date         
1       | 2013-01-01            
2       | 2013-02-01            
3       | 2013-03-01            

    *person*            
person_sk   | person_id  | person_name      
1           |   10       |   John       
2           |   11       |   Bob        
3           |   12       |   Jill       



    *person_portfolio*          
person_portfolio_sk | date_sk | person_sk | res_value | report_month
1                   |   1     |     1     |     15    |  2013-01-01
2                   |   1     |     2     |     10    |  2013-01-01
3                   |   1     |     3     |      1    |  2013-01-01
4                   |   2     |     1     |     30    |  2013-02-01

(想象一下'日期'表填写过去10年和未来10年的每个日期)

我一直在努力找出,为了使用日期范围进行比较报告,如何在该时间范围内没有任何条目替换该人的0值。这是我试过的查询:

SELECT
 p.person_id,
 COALESCE(pp.res_value,0)::NUMERIC(16,2) AS res_value,
 pp.report_month
FROM person p
LEFT JOIN person_portfolio pp
ON p.person_sk = pp.person_sk
LEFT JOIN date d
ON d.date_sk = pp.date_sk
WHERE person_id IN ('10','11','12')
AND pp.report_month >= '2013-01-01' --From Date
AND pp.report_month <= '2013-05-01' -- To Date
AND d.day_number_of_month = 1
ORDER BY p.person_id DESC;

我想要返回的输出最终总共为15行。 3人x 5个月的数据=总共15行。我在日期表中遗漏了day_number_of_month列,但它保留了每月第一个的数字1,第二个的数字等等(每个月的每一天都在此表中)。它应该是这样的:

person_id   | res_value | report_month
10          |   15      |   2013-01-01
10          |   30      |   2013-02-01
10          |   0       |   2013-03-01
10          |   0       |   2013-04-01
10          |   0       |   2013-05-01
11          |   10      |   2013-01-01
11          |   0       |   2013-02-01
11          |   0       |   2013-03-01
11          |   0       |   2013-04-01
11          |   0       |   2013-05-01
12          |   1       |   2013-01-01
12          |   0       |   2013-02-01
12          |   0       |   2013-03-01
12          |   0       |   2013-04-01
12          |   0       |   2013-05-01

但我只得到这些结果:

person_id   | res_value | report_month
10          |   15      |  2013-01-01
10          |   30      |  2013-02-01
11          |   10      |  2013-01-01
12          |    1      |  2013-01-01

所以基本上......目前有一种可行的方法,当没有#report; report_month&#39;的条目时,我可以将0值行注入结果中。对于特定的人?我会感激任何形式的帮助,因为我已经为此工作了两个星期,现在试图完成这份报告。谢谢!

2 个答案:

答案 0 :(得分:1)

您对输出的描述提供了有关如何解决问题的指导。首先使用cross join生成行。然后引入其余的数据。

鉴于您的查询结构,我没有看到日期表的目的。如果我假设每个报告期至少有一份报告,我可以这样做:

SELECT p.person_id,
       COALESCE(pp.res_value,0)::NUMERIC(16,2) AS res_value,
       d.report_month
FROM (SELECT DISTINCT person_id FROM person p WHERE person_id IN ('10', '11', '12')
     ) p CROSS JOIN
     (SELECT DISTINCT pp.report_month
      FROM person_portfolio pp
      WHERE pp.report_month >= '2013-01-01' AND
            pp.report_month <= '2013-05-01' 
     ) d LEFT JOIN
     person_portfolio pp
     ON p.person_sk = pp.person_sk and
        d.report_month = pp.report_month
ORDER BY p.person_id DESC, d.report_month asc;

但是,您的数据并非如此。您可以生成日期。在您的环境中,我不知道使用generate_series()date表是否更好。在任何情况下,这将替换上面的d子查询与具有所有感兴趣日期的子查询。

答案 1 :(得分:0)

查找“OUTER JOIN”..

未经测试,但您可以尝试这样的事情吗? (从您的日期表开始,将日期范围限制在您想要的范围内,然后开始将它们连接到您的其他表格... OUTER JOIN说“即使您在此日期找不到包含数据的人,请保留日期..我想看到它)

SELECT
 p.person_id,
 COALESCE(pp.res_value,0)::NUMERIC(16,2) AS res_value,
 pp.report_month
FROM date d
   LEFT OUTER JOIN person p
   ON d.date_sk = p.date_sk
   LEFT OUTER JOIN person_portfolio pp
   ON p.person_sk = pp.person_sk
WHERE person_id IN ('10','11','12')
AND d.date_sk >= '2013-01-01' --From Date
AND d.date_sk <= '2013-05-01' -- To Date
ORDER BY p.person_id DESC;