PostgreSQL 中的交叉表

crosstab in PostgreSQL

我有以下 tables:

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
);

我如何查询数据库以便它 returns table

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

其中 n 在 table 传感器的行范围内并且 'abbrv $i' 被 table 传感器中的相应值替换?

以下查询

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);

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

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

首先,您可以创建一个复合 TYPE,它对应于 sensor.abbrv 的总列表 table sensor 的列列表 [=16] =]:

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() ;

然后您可以将不同数量的 sensor.abbrv / reading.value 对聚合为每个 [=19] 的单个 json 对象中的 json key/value 对=] 和 device_id :

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 key/value 对,即使 json 键 s.abbrv 不对应任何值在 readings table.

最后,您可以使用 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