具有动态列名和多个输入列的PostgreSQL交叉表 问题
我有一个PostgreSQL 9.6数据库,其中的表是根据EAV模型设计的,具有不同类型的值。示例摘录如下所示:具有动态列名和多个输入列的PostgreSQL交叉表 问题,postgresql,crosstab,entity-attribute-value,Postgresql,Crosstab,Entity Attribute Value,我有一个PostgreSQL 9.6数据库,其中的表是根据EAV模型设计的,具有不同类型的值。示例摘录如下所示: name |arrivalTime | boolValue | intValue | floatValue | stringValue ------+------------+-----------+----------+------------+------------ a1 | 10:00:00 | true | |
name |arrivalTime | boolValue | intValue | floatValue | stringValue
------+------------+-----------+----------+------------+------------
a1 | 10:00:00 | true | | |
c3 | 10:00:00 | | 12 | |
d4 | 10:00:00 | | | | hello
e5 | 15:00:00 | | | 45.67 |
c3 | 15:00:00 | | 45 | |
b2 | 20:00:00 | | | 4.567 |
a1 | 20:00:00 | false | | |
d4 | 22:00:00 | | | | bye
b2 | 22:00:00 | | | 12.34 |
arrivalTime | a1 | b2 | c3 | d4 | e5
------------+-------+-------+-------+-------+-------
10:00:00 | true | | 12 | hello |
15:00:00 | | | 45 | | 45.67
20:00:00 | false | 4.567 | | |
22:00:00 | | 12.34 | | bye |
空单元格表示数据库中的null
值
现在我想得到一个透视表,新列是arrivalTime
和name
的内容。对于上面的示例,它应该如下所示:
name |arrivalTime | boolValue | intValue | floatValue | stringValue
------+------------+-----------+----------+------------+------------
a1 | 10:00:00 | true | | |
c3 | 10:00:00 | | 12 | |
d4 | 10:00:00 | | | | hello
e5 | 15:00:00 | | | 45.67 |
c3 | 15:00:00 | | 45 | |
b2 | 20:00:00 | | | 4.567 |
a1 | 20:00:00 | false | | |
d4 | 22:00:00 | | | | bye
b2 | 22:00:00 | | | 12.34 |
arrivalTime | a1 | b2 | c3 | d4 | e5
------------+-------+-------+-------+-------+-------
10:00:00 | true | | 12 | hello |
15:00:00 | | | 45 | | 45.67
20:00:00 | false | 4.567 | | |
22:00:00 | | 12.34 | | bye |
作为检索此结果的查询输入,我得到一个与名称
匹配的模式,以及指定到达时间范围的开始和结束时间
原始表的属性:
- “名称”列中的条目不稳定,即输入新名称
老名字也会定期消失
名称
和到达时间
的每个组合都是唯一的
- 每个
名称
和到达时间
组合在其中一个值列中正好有一个条目
思想
我已经给了它一些考虑:
- 我想得太多了,应该使用
交叉表功能
- 由于列是动态的,因此需要两个查询,如或中所述
- 使用
format()
函数生成第一个查询可能是个好主意
示例表
以下是创建示例表的SQL代码:
CREATE TABLE IF NOT EXISTS playTable (
name TEXT NOT NULL,
arrivalTime TIME NOT NULL,
floatValue REAL NULL,
intValue INT NULL,
boolValue BOOLEAN NULL,
stringValue TEXT NULL,
PRIMARY KEY (name, arrivalTime),
CONSTRAINT single_value CHECK(
(boolValue IS NOT NULL)::INT +
(intValue IS NOT NULL)::INT +
(floatValue IS NOT NULL)::INT +
(stringValue IS NOT NULL)::INT = 1
)
);
并插入以下值:
INSERT INTO playTable ( name, arrivalTime, boolValue ) VALUES ( 'a1', '10:00:00', true );
INSERT INTO playTable ( name, arrivalTime, intValue ) VALUES ( 'c3', '10:00:00', 12 );
INSERT INTO playTable ( name, arrivalTime, stringValue ) VALUES ( 'd4', '10:00:00', 'hello' );
INSERT INTO playTable ( name, arrivalTime, floatValue ) VALUES ( 'e5', '15:00:00', 45.67 );
INSERT INTO playTable ( name, arrivalTime, intValue ) VALUES ( 'c3', '15:00:00', 45 );
INSERT INTO playTable ( name, arrivalTime, floatValue ) VALUES ( 'b2', '20:00:00', 4.567 );
INSERT INTO playTable ( name, arrivalTime, boolValue ) VALUES ( 'a1', '20:00:00', false );
INSERT INTO playTable ( name, arrivalTime, stringValue ) VALUES ( 'd4', '22:00:00', 'bye' );
INSERT INTO playTable ( name, arrivalTime, floatValue ) VALUES ( 'b2', '22:00:00', 12.34 );
透视表,非动态
如果提供解决方案的起点,我想:
SELECT *
FROM crosstab(
$ct$
SELECT
arrivalTime, name, concat(boolValue, intValue, floatValue, stringValue)
FROM playTable
ORDER BY 1, 2
$ct$,
$ct$
SELECT DISTINCT name
FROM playTable
ORDER BY 1
$ct$)
AS ct("arrivalTime" time, "a1" BOOLEAN, "b2" REAL, "c3" INT, "d4" TEXT, "e5" REAL);
这个解决方案缺少的是动态方面。作为输入,提供了名称的模式和到达时间的范围(即最小值和最大值)。这使得作为ct(…)
的参数是动态的。对最后四列使用coalesce()
。要执行此操作,必须将列强制转换为text
:
select *
from crosstab(
$ct$
select
arrivaltime, name,
coalesce(boolvalue::text, intvalue::text, floatvalue::text, stringvalue)
from my_table
order by 1, 2
$ct$,
$ct$
select distinct name
from my_table
order by 1
$ct$)
as ct("arrivalTime" time, "a1" text, "b2" text, "c3" text, "d4" text, "e5" text);
arrivalTime | a1 | b2 | c3 | d4 | e5
-------------+-------+-------+----+-------+-------
10:00:00 | true | | 12 | hello |
15:00:00 | | | 45 | | 45.67
20:00:00 | false | 4.567 | | |
22:00:00 | | 12.34 | | bye |
(4 rows)
由于示例数据的格式,我使用了arrivalTime
,将其更改为timestamp
我使用了解决方案,并稍微扩展了我的问题。我将您的代码从使用coalesce
更改为concat
。这项工作很好,有重要的障碍,但我还没有完成:仍然缺少输入指定名称
和到达时间
范围的模式的能力。这使得的参数成为ct(…)
dynamic.user711270您需要任何类型的编程语言,这些语言将为您生成SQL查询,并考虑输入模式。如果您使用的是SQL,则仅使用plpgsql创建此类动态查询。