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
我有以下 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