PostgreSQL 9.3:动态数据透视 table

PostgreSQL 9.3: Dynamic pivot table

我有一个称为矩阵的 table,它包含两列,即 colacolb 作为 如下所示:

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 = y1,这可以更便宜:

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 那里解决它。