具有多个 "row name" 列的 Postgresql 交叉表查询
Postgresql crosstab query with multiple "row name" columns
我有一个 table 是 "tall skinny" 事实 table:
CREATE TABLE facts(
eff_date timestamp NOT NULL,
update_date timestamp NOT NULL,
symbol_id int4 NOT NULL,
data_type_id int4 NOT NULL,
source_id char(3) NOT NULL,
fact decimal
/* Keys */
CONSTRAINT fact_pk
PRIMARY KEY (source_id, symbol_id, data_type_id, eff_date),
)
我想 "pivot" 这个作为报告,所以 header 看起来像这样:
eff_date, symbol_id, source_id, datatypeValue1, ... DatatypeValueN
即,我想要 eff_date、symbol_id 和 source_id 的每个唯一组合对应一行。
但是,postgresql crosstab() 函数只允许键列。
有什么想法吗?
crosstab()
需要其输入查询(第一个参数)中的以下列,顺序为:
- 一个
row_name
- (可选)
extra
列
- a
category
(第二个交叉表参数中的匹配值)
- 一个
value
您没有row_name
。使用 window 函数 dense_rank()
.
添加代理项 row_name
你的问题有解释的余地。让我们添加 示例行以进行演示:
INSERT INTO facts (eff_date, update_date, symbol_id, data_type_id, source_id)
VALUES
(now(), now(), 1, 5, 'foo')
, (now(), now(), 1, 6, 'foo')
, (now(), now(), 1, 7, 'foo')
, (now(), now(), 1, 6, 'bar')
, (now(), now(), 1, 7, 'bar')
, (now(), now(), 1, 23, 'bar')
, (now(), now(), 1, 5, 'baz')
, (now(), now(), 1, 23, 'baz'); -- only two rows for 'baz'
解释 #1:第一个 N 个值
您想为每个不同的 (source_id, symbol_id, eff_date)
列出 data_type_id
的前 N 个值(最小值,如果有更多)。
为此,你还需要合成category
,可以用row_number()
合成。生成 crosstab()
:
输入的基本查询
SELECT dense_rank() OVER (ORDER BY eff_date, symbol_id, source_id)::int AS row_name
, eff_date, symbol_id, source_id -- extra columns
, row_number() OVER (PARTITION BY eff_date, symbol_id, source_id
ORDER BY data_type_id)::int AS category
, data_type_id AS value
FROM facts
ORDER BY row_name, category;
交叉表查询:
SELECT *
FROM crosstab(
'SELECT dense_rank() OVER (ORDER BY eff_date, symbol_id, source_id)::int AS row_name
, eff_date, symbol_id, source_id -- extra columns
, row_number() OVER (PARTITION BY eff_date, symbol_id, source_id
ORDER BY data_type_id)::int AS category
, data_type_id AS value
FROM facts
ORDER BY row_name, category'
, 'VALUES (1), (2), (3)'
) AS (row_name int, eff_date timestamp, symbol_id int, source_id char(3)
, datatype_1 int, datatype_2 int, datatype_3 int);
结果:
row_name | eff_date | symbol_id | source_id | datatype_1 | datatype_2 | datatype_3
-------: | :--------------| --------: | :-------- | ---------: | ---------: | ---------:
1 | 2017-04-10 ... | 1 | bar | 6 | 7 | 23
2 | 2017-04-10 ... | 1 | baz | 5 | 23 | null
3 | 2017-04-10 ... | 1 | foo | 5 | 6 | 7
解释 #2:列名中的实际值
您想将 data_type_id
的实际值附加到列名称 datatypeValue1, ... DatatypeValueN
。其中一个或多个:
SELECT DISTINCT data_type_id FROM facts ORDER BY 1;
5, 6, 7, 23
在示例中。然后实际显示值可以只是 boolean
(或冗余值?)。基本查询:
SELECT dense_rank() OVER (ORDER BY eff_date, symbol_id, source_id)::int AS row_name
, eff_date, symbol_id, source_id -- extra columns
, data_type_id AS category
, TRUE AS value
FROM facts
ORDER BY row_name, category;
交叉表查询:
SELECT *
FROM crosstab(
'SELECT dense_rank() OVER (ORDER BY eff_date, symbol_id, source_id)::int AS row_name
, eff_date, symbol_id, source_id -- extra columns
, data_type_id AS category
, TRUE AS value
FROM facts
ORDER BY row_name, category'
, 'VALUES (5), (6), (7), (23)' -- actual values
) AS (row_name int, eff_date timestamp, symbol_id int, source_id char(3)
, datatype_5 bool, datatype_6 bool, datatype_7 bool, datatype_23 bool);
结果:
eff_date | symbol_id | source_id | datatype_5 | datatype_6 | datatype_7 | datatype_23
:--------------| --------: | :-------- | :--------- | :--------- | :--------- | :----------
2017-04-10 ... | 1 | bar | null | t | t | t
2017-04-10 ... | 1 | baz | t | null | null | t
2017-04-10 ... | 1 | foo | t | t | t | null
dbfiddle here
相关:
- Dynamic alternative to pivot with CASE and GROUP BY
- Postgres - Transpose Rows to Columns
我有一个 table 是 "tall skinny" 事实 table:
CREATE TABLE facts(
eff_date timestamp NOT NULL,
update_date timestamp NOT NULL,
symbol_id int4 NOT NULL,
data_type_id int4 NOT NULL,
source_id char(3) NOT NULL,
fact decimal
/* Keys */
CONSTRAINT fact_pk
PRIMARY KEY (source_id, symbol_id, data_type_id, eff_date),
)
我想 "pivot" 这个作为报告,所以 header 看起来像这样:
eff_date, symbol_id, source_id, datatypeValue1, ... DatatypeValueN
即,我想要 eff_date、symbol_id 和 source_id 的每个唯一组合对应一行。
但是,postgresql crosstab() 函数只允许键列。
有什么想法吗?
crosstab()
需要其输入查询(第一个参数)中的以下列,顺序为:
- 一个
row_name
- (可选)
extra
列 - a
category
(第二个交叉表参数中的匹配值) - 一个
value
您没有row_name
。使用 window 函数 dense_rank()
.
row_name
你的问题有解释的余地。让我们添加 示例行以进行演示:
INSERT INTO facts (eff_date, update_date, symbol_id, data_type_id, source_id)
VALUES
(now(), now(), 1, 5, 'foo')
, (now(), now(), 1, 6, 'foo')
, (now(), now(), 1, 7, 'foo')
, (now(), now(), 1, 6, 'bar')
, (now(), now(), 1, 7, 'bar')
, (now(), now(), 1, 23, 'bar')
, (now(), now(), 1, 5, 'baz')
, (now(), now(), 1, 23, 'baz'); -- only two rows for 'baz'
解释 #1:第一个 N 个值
您想为每个不同的 (source_id, symbol_id, eff_date)
列出 data_type_id
的前 N 个值(最小值,如果有更多)。
为此,你还需要合成category
,可以用row_number()
合成。生成 crosstab()
:
SELECT dense_rank() OVER (ORDER BY eff_date, symbol_id, source_id)::int AS row_name
, eff_date, symbol_id, source_id -- extra columns
, row_number() OVER (PARTITION BY eff_date, symbol_id, source_id
ORDER BY data_type_id)::int AS category
, data_type_id AS value
FROM facts
ORDER BY row_name, category;
交叉表查询:
SELECT *
FROM crosstab(
'SELECT dense_rank() OVER (ORDER BY eff_date, symbol_id, source_id)::int AS row_name
, eff_date, symbol_id, source_id -- extra columns
, row_number() OVER (PARTITION BY eff_date, symbol_id, source_id
ORDER BY data_type_id)::int AS category
, data_type_id AS value
FROM facts
ORDER BY row_name, category'
, 'VALUES (1), (2), (3)'
) AS (row_name int, eff_date timestamp, symbol_id int, source_id char(3)
, datatype_1 int, datatype_2 int, datatype_3 int);
结果:
row_name | eff_date | symbol_id | source_id | datatype_1 | datatype_2 | datatype_3 -------: | :--------------| --------: | :-------- | ---------: | ---------: | ---------: 1 | 2017-04-10 ... | 1 | bar | 6 | 7 | 23 2 | 2017-04-10 ... | 1 | baz | 5 | 23 | null 3 | 2017-04-10 ... | 1 | foo | 5 | 6 | 7
解释 #2:列名中的实际值
您想将 data_type_id
的实际值附加到列名称 datatypeValue1, ... DatatypeValueN
。其中一个或多个:
SELECT DISTINCT data_type_id FROM facts ORDER BY 1;
5, 6, 7, 23
在示例中。然后实际显示值可以只是 boolean
(或冗余值?)。基本查询:
SELECT dense_rank() OVER (ORDER BY eff_date, symbol_id, source_id)::int AS row_name
, eff_date, symbol_id, source_id -- extra columns
, data_type_id AS category
, TRUE AS value
FROM facts
ORDER BY row_name, category;
交叉表查询:
SELECT *
FROM crosstab(
'SELECT dense_rank() OVER (ORDER BY eff_date, symbol_id, source_id)::int AS row_name
, eff_date, symbol_id, source_id -- extra columns
, data_type_id AS category
, TRUE AS value
FROM facts
ORDER BY row_name, category'
, 'VALUES (5), (6), (7), (23)' -- actual values
) AS (row_name int, eff_date timestamp, symbol_id int, source_id char(3)
, datatype_5 bool, datatype_6 bool, datatype_7 bool, datatype_23 bool);
结果:
eff_date | symbol_id | source_id | datatype_5 | datatype_6 | datatype_7 | datatype_23 :--------------| --------: | :-------- | :--------- | :--------- | :--------- | :---------- 2017-04-10 ... | 1 | bar | null | t | t | t 2017-04-10 ... | 1 | baz | t | null | null | t 2017-04-10 ... | 1 | foo | t | t | t | null
dbfiddle here
相关:
- Dynamic alternative to pivot with CASE and GROUP BY
- Postgres - Transpose Rows to Columns