Postgres计算元素并按日期列出

时间:2017-06-30 00:55:12

标签: sql postgresql pivot crosstab

好的Postgres天才,我需要一些帮助。我试图绘制大量数据,并希望在发送到前端之前尽可能地在数据库中准备数据。我正在使用Postgres 9.6

我有一组这样的结果:

Source  | Date
--------|------------
Email   | 2017-06-01
Email   | 2017-06-01
Email   | 2017-06-03
Email   | 2017-06-03
Email   | 2017-06-03
Email   | 2017-06-04
Email   | 2017-06-06
Email   | 2017-06-06
Email   | 2017-06-07
Banner  | 2017-06-01
Banner  | 2017-06-02
Banner  | 2017-06-03
....

我需要按天计算所有数据然后进行列化,如果日期为零,我需要进行零填充。

期望的结果:

Source  | 2017-06-01  | 2017-06-02  | 2017-06-03  | ...  | 2017-06-07
--------|-------------|-------------|-------------|------|------------
Email   | 2           | 0           | 3           | ...  | 1
Banner  | 1           | 1           | ...         

我希望这是有道理的,谢谢。

2 个答案:

答案 0 :(得分:0)

您可以使用:

SELECT 
    *
FROM
    crosstab(
    $$
        SELECT "Source", "Date"::text, count("Date") 
        FROM t 
        GROUP BY "Source", "Date"
        ORDER BY "Source"
    $$,
    $$
        SELECT cast(d as date)
        FROM generate_series(date '2017-06-01', date '2017-06-10', interval '1 day') as s(d)
    $$
    ) AS  
    (
        "Source" text, 
        "2017-06-01" bigint,
        "2017-06-02" bigint,
        "2017-06-03" bigint,
        "2017-06-04" bigint,
        "2017-06-05" bigint,
        "2017-06-06" bigint,
        "2017-06-07" bigint,
        "2017-06-08" bigint,
        "2017-06-09" bigint,
        "2017-06-10" bigint
    ) ;

哪个使用crosstab(text source_sql, text category_sql),特定于PostgreSQL并且需要tablefunc扩展名,或者您可以使用"经典SQL数据透视":

SELECT
    "Source"
    ,COUNT(CASE WHEN "Date" = '2017-06-01' THEN 1 END) AS "2017-06-01"
    ,COUNT(CASE WHEN "Date" = '2017-06-02' THEN 1 END) AS "2017-06-02"
    ,COUNT(CASE WHEN "Date" = '2017-06-03' THEN 1 END) AS "2017-06-03"
    ,COUNT(CASE WHEN "Date" = '2017-06-04' THEN 1 END) AS "2017-06-04"
    ,COUNT(CASE WHEN "Date" = '2017-06-05' THEN 1 END) AS "2017-06-05"
    ,COUNT(CASE WHEN "Date" = '2017-06-06' THEN 1 END) AS "2017-06-06"
    ,COUNT(CASE WHEN "Date" = '2017-06-07' THEN 1 END) AS "2017-06-07"
    ,COUNT(CASE WHEN "Date" = '2017-06-08' THEN 1 END) AS "2017-06-08"
    ,COUNT(CASE WHEN "Date" = '2017-06-09' THEN 1 END) AS "2017-06-09"
    ,COUNT(CASE WHEN "Date" = '2017-06-10' THEN 1 END) AS "2017-06-10"
FROM
    t
GROUP BY
    "Source"
ORDER BY
    "Source" ;
Source | 2017-06-01 | 2017-06-02 | 2017-06-03 | 2017-06-04 | 2017-06-05 | 2017-06-06 | 2017-06-07 | 2017-06-08 | 2017-06-09 | 2017-06-10
:----- | ---------: | ---------: | ---------: | ---------: | ---------: | ---------: | ---------: | ---------: | ---------: | ---------:
Banner |          1 |          1 |          1 |          0 |          0 |          0 |          0 |          0 |          0 |          0
Email  |          2 |          0 |          3 |          1 |          0 |          2 |          1 |          0 |          0 |          0

两者都会返回相同的值,差异是第二个会给你' 0' 0第一个会给你' null'当没有可计算的值时。

dbfiddle here

答案 1 :(得分:0)

此查询可用于非周期性日期范围。

SELECT * 
FROM CROSSTAB(
            $$
                SELECT id, 
                    fdate::TEXT,
                    fveri
                FROM   tmp_table 
                
                WHERE fdate = '05.11.2020' 
                OR    fdate = '08.11.2020' 
                OR    fdate = '11.11.2020' 
                OR    fdate = '14.11.2020' 
                OR    fdate = '17.11.2020'

                ORDER BY 1, 2
            $$,
            
            $$
                SELECT CAST(m AS date) FROM generate_series( date '05.11.2020' , date '18.11.2020' , interval '3 day' ) m
            $$
        ) AS tmp ( tmpid integer, 
                    "201105" varchar(50), 
                    "201108" varchar(50), 
                    "201111" varchar(50),
                    "201114" varchar(50), 
                    "201117" varchar(50)
                )


  

https://dbfiddle.uk/?rdbms=postgres_9.6&fiddle=95f56c27456ffc389dfb071387d6bef5

样本取自佩奇