【问题标题】:crosstab in PostgreSQLPostgreSQL 中的交叉表
【发布时间】:2022-01-21 15:11:03
【问题描述】:

我有以下表格:

CREATE TABLE sensor (
    id int PRIMARY KEY,
    abbrv varchar(255) NOT NULL UNIQUE
);

CREATE TABLE readings (
    time timestamp without time zone NOT NULL,
    device_id int NOT NULL,
    sensor_id int REFERENCES sensor (id) NOT NULL,
    value float4 NOT NULL
);

如何查询数据库以返回表

time | device_id | abbrv $1 | ... | abbrv $n

其中 n 范围跨越表格传感器的行,'abbrv $i' 被表格传感器中的相应值替换?

以下查询

SELECT * FROM crosstab(
    'SELECT time, device_id, sensor_id, value FROM readings ORDER BY 1, 2',
    'SELECT id FROM sensor'
    ) AS (time timestamp without time zone, device_id int, "sensor_1" float4, "sensor_2" float4, "sensor_3" float4);

在一定程度上起作用:我需要知道传感器表中有多少行,我必须手动设置列的名称。

【问题讨论】:

    标签: sql postgresql crosstab


    【解决方案1】:

    我没有比您更好的基本答案,尤其是当传感器/值的数量因设备而异时。但是,您可以执行以下操作:

    首先,您可以创建一个复合TYPE,它对应于sensor.abbrv 表中sensor 的总列表按sensor.id 排序的列列表:

    CREATE OR REPLACE PROCEDURE create_composite_type() LANGUAGE plpgsql AS $$
    DECLARE
      column_list text ;
    BEGIN
      SELECT string_agg(quote_ident(abbrv) || ' float4', ',' ORDER BY id ASC)
        INTO column_list
        FROM sensors ;
      
      EXECUTE 'DROP TYPE IF EXISTS composite_type ;'
      EXECUTE '
      CREATE OR REPLACE TYPE composite_type (time timestamp without time zone, device_id int, ' || column_list || ')' ;
    END ;
    $$ ;
    
    CALL create_composite_type() ;
    

    然后您可以根据timedevice_id 将不同数量的sensor.abbrv / reading.value 对聚合为单个json 对象中的json 键/值对:

    SELECT time
         , device_id
         , jsonb_object_agg(jsonb_build_object(quote_ident(s.abbrv), quote_nullable(r.value)) ORDER BY s.id ASC) AS json_columns
      FROM readings AS r
     RIGHT JOIN sensor AS s
        ON s.id = r.sensor_id
     GROUP BY time, device_id
    

    RIGHT JOIN 是必需的,以便系统地创建 json 键/值对,即使 json 键 s.abbrv 不对应于 readings 表中的任何值。

    最后你可以使用jsonb_populate_record函数将json对象显示为一行:

    SELECT time
         , device_id
         , jsonb_populate_record(NULL :: composite_type, json_columns)
      FROM 
         ( SELECT time
                , device_id
                , jsonb_object_agg(jsonb_build_object(quote_ident(s.abbrv), quote_nullable(r.value)) ORDER BY s.id ASC) AS json_columns
             FROM readings AS r
            RIGHT JOIN sensor AS s
               ON s.id = r.sensor_id
            GROUP BY time, device_id
         ) AS q 
    

    【讨论】:

      猜你喜欢
      • 2016-09-29
      • 1970-01-01
      • 2011-03-01
      • 2021-07-23
      • 2013-09-16
      相关资源
      最近更新 更多