PostgreSQL 9.3:动态数据透视 table
PostgreSQL 9.3: Dynamic pivot table
我有一个称为矩阵的 table,它包含两列,即 cola
和 colb
作为
如下所示:
Table: 矩阵
create table matrix
(
cola varchar(10),
colb varchar(10)
);
插入行数:
insert into matrix values('A1','B1'),('A2','B2'),('A3','B3'),('A4','B4'),
('A5','B5'),('A6','B6'),('A7','B7'),('A8','B8'),
('A9','B9'),('A10','B10'),('A11','B11'),('A12','B12'),
('A13','B13'),('A14','B14'),('A15','B15'),('A16','B16'),
('A17','B17'),('A18','B18'),('A19','B19'),('A20','B20'),
('A21','B21'),('A22','B22'),('A23','B23'),('A24','B24'),
('A25','B25'),('A26','B26'),('A27','B27'),('A28','B28'),
('A29','B29'),('A30','B30');
注意:我想以矩阵的形式显示结果并计算哪些列属于彼此,并为每一列分配矩阵中的值。我刚刚
仅作为示例添加了 30 条记录,但也可能有数千条记录。所以我需要
为此准备一个动态枢轴table。预期结果如下图。
预期结果:
A1 A2 A3 A4 A5 A6 ................ A30
------------------------------------------------------------------
B1 | 1 0 0 0 0 0 0
|
B2 | 0 1 0 0 0 0 0
|
B3 | 0 0 1 0 0 0 0
|
B4 | 0 0 0 1 0 0 0
|
B5 | 0 0 0 0 1 0 0
|
B6 | 0 0 0 0 0 1 0
. |
. |
. |
. |
B30| 0 0 0 0 0 0 1
您可以使用附加模块 tablefunc:
中的 crosstab()
执行此操作
SELECT b
, COALESCE(a1, 0) AS "A1"
, COALESCE(a2, 0) AS "A2"
, COALESCE(a3, 0) AS "A3"
, ... -- all the way up to "A30"
FROM crosstab(
'SELECT colb, cola, 1 AS val FROM matrix
ORDER BY 1,2'
, $$SELECT 'A'::text || g FROM generate_series(1,30) g$$
) AS t (b text
, a1 int, a2 int, a3 int, a4 int, a5 int, a6 int
, a7 int, a8 int, a9 int, a10 int, a11 int, a12 int
, a13 int, a14 int, a15 int, a16 int, a17 int, a18 int
, a19 int, a20 int, a21 int, a22 int, a23 int, a24 int
, a25 int, a26 int, a27 int, a28 int, a29 int, a30 int);
如果 NULL
而不是 0
也有效,它可以只是外部查询中的 SELECT *
。
详细解释:
- PostgreSQL Crosstab Query
这里的特殊"difficulty":没有实际的"value"。所以添加 1 AS val
作为最后一列。
类别数量未知
不可能在单个查询中进行完全动态的查询(结果类型未知)。您需要 两个 查询。首先动态构建一个像上面这样的语句,然后执行它。详情:
Selecting multiple max() values using a single SQL statement
PostgreSQL convert columns to rows? Transpose?
Dynamically generate columns for crosstab in PostgreSQL
Dynamic alternative to pivot with CASE and GROUP BY
类别太多
如果超过 maximum number of columns (1600),经典的交叉表是不可能的,因为结果不能用单独的列来表示。 (另外,人眼几乎看不懂有那么多列的table)
数组或文档类型如 hstore
or jsonb
是备选方案。这是一个数组的解决方案:
SELECT colb, array_agg(cola) AS colas
FROM (
SELECT colb, right(colb, -1)::int AS sortb
, CASE WHEN m.cola IS NULL THEN 0 ELSE 1 END AS cola
FROM (SELECT DISTINCT colb FROM matrix) b
CROSS JOIN (SELECT DISTINCT cola FROM matrix) a
LEFT JOIN matrix m USING (colb, cola)
ORDER BY sortb, right(cola, -1)::int
) sub
GROUP BY 1, sortb
ORDER BY sortb;
构建完整的值网格:
(SELECT DISTINCT colb FROM matrix) b
CROSS JOIN (SELECT DISTINCT cola FROM matrix) a
LEFT JOIN
现有组合,按名称的数字部分排序并聚合成数组。
right(colb, -1)::int
从 'A3' 中删除前导字符并将数字转换为整数,以便我们获得正确的排序顺序。
基本矩阵
如果您只想要 0
的 table 和 x = y
的 1
,这可以更便宜:
SELECT x, array_agg((x = y)::int) AS y_arr
FROM generate_series(1,10) x
, generate_series(1,10) y
GROUP BY 1
ORDER BY 1;
SQL Fiddle 以您在评论中提供的内容为基础。
请注意,sqlfiddle.com 目前有一个错误会终止数组值的显示。所以我转向 text
那里解决它。
我有一个称为矩阵的 table,它包含两列,即 cola
和 colb
作为
如下所示:
Table: 矩阵
create table matrix
(
cola varchar(10),
colb varchar(10)
);
插入行数:
insert into matrix values('A1','B1'),('A2','B2'),('A3','B3'),('A4','B4'),
('A5','B5'),('A6','B6'),('A7','B7'),('A8','B8'),
('A9','B9'),('A10','B10'),('A11','B11'),('A12','B12'),
('A13','B13'),('A14','B14'),('A15','B15'),('A16','B16'),
('A17','B17'),('A18','B18'),('A19','B19'),('A20','B20'),
('A21','B21'),('A22','B22'),('A23','B23'),('A24','B24'),
('A25','B25'),('A26','B26'),('A27','B27'),('A28','B28'),
('A29','B29'),('A30','B30');
注意:我想以矩阵的形式显示结果并计算哪些列属于彼此,并为每一列分配矩阵中的值。我刚刚 仅作为示例添加了 30 条记录,但也可能有数千条记录。所以我需要 为此准备一个动态枢轴table。预期结果如下图。
预期结果:
A1 A2 A3 A4 A5 A6 ................ A30
------------------------------------------------------------------
B1 | 1 0 0 0 0 0 0
|
B2 | 0 1 0 0 0 0 0
|
B3 | 0 0 1 0 0 0 0
|
B4 | 0 0 0 1 0 0 0
|
B5 | 0 0 0 0 1 0 0
|
B6 | 0 0 0 0 0 1 0
. |
. |
. |
. |
B30| 0 0 0 0 0 0 1
您可以使用附加模块 tablefunc:
中的crosstab()
执行此操作
SELECT b
, COALESCE(a1, 0) AS "A1"
, COALESCE(a2, 0) AS "A2"
, COALESCE(a3, 0) AS "A3"
, ... -- all the way up to "A30"
FROM crosstab(
'SELECT colb, cola, 1 AS val FROM matrix
ORDER BY 1,2'
, $$SELECT 'A'::text || g FROM generate_series(1,30) g$$
) AS t (b text
, a1 int, a2 int, a3 int, a4 int, a5 int, a6 int
, a7 int, a8 int, a9 int, a10 int, a11 int, a12 int
, a13 int, a14 int, a15 int, a16 int, a17 int, a18 int
, a19 int, a20 int, a21 int, a22 int, a23 int, a24 int
, a25 int, a26 int, a27 int, a28 int, a29 int, a30 int);
如果 NULL
而不是 0
也有效,它可以只是外部查询中的 SELECT *
。
详细解释:
- PostgreSQL Crosstab Query
这里的特殊"difficulty":没有实际的"value"。所以添加 1 AS val
作为最后一列。
类别数量未知
不可能在单个查询中进行完全动态的查询(结果类型未知)。您需要 两个 查询。首先动态构建一个像上面这样的语句,然后执行它。详情:
Selecting multiple max() values using a single SQL statement
PostgreSQL convert columns to rows? Transpose?
Dynamically generate columns for crosstab in PostgreSQL
Dynamic alternative to pivot with CASE and GROUP BY
类别太多
如果超过 maximum number of columns (1600),经典的交叉表是不可能的,因为结果不能用单独的列来表示。 (另外,人眼几乎看不懂有那么多列的table)
数组或文档类型如 hstore
or jsonb
是备选方案。这是一个数组的解决方案:
SELECT colb, array_agg(cola) AS colas
FROM (
SELECT colb, right(colb, -1)::int AS sortb
, CASE WHEN m.cola IS NULL THEN 0 ELSE 1 END AS cola
FROM (SELECT DISTINCT colb FROM matrix) b
CROSS JOIN (SELECT DISTINCT cola FROM matrix) a
LEFT JOIN matrix m USING (colb, cola)
ORDER BY sortb, right(cola, -1)::int
) sub
GROUP BY 1, sortb
ORDER BY sortb;
构建完整的值网格:
(SELECT DISTINCT colb FROM matrix) b CROSS JOIN (SELECT DISTINCT cola FROM matrix) a
LEFT JOIN
现有组合,按名称的数字部分排序并聚合成数组。right(colb, -1)::int
从 'A3' 中删除前导字符并将数字转换为整数,以便我们获得正确的排序顺序。
基本矩阵
如果您只想要 0
的 table 和 x = y
的 1
,这可以更便宜:
SELECT x, array_agg((x = y)::int) AS y_arr
FROM generate_series(1,10) x
, generate_series(1,10) y
GROUP BY 1
ORDER BY 1;
SQL Fiddle 以您在评论中提供的内容为基础。
请注意,sqlfiddle.com 目前有一个错误会终止数组值的显示。所以我转向 text
那里解决它。