在具有复合键的查询上使用交叉表(多列)

时间:2017-04-14 19:17:24

标签: postgresql crosstab composite-key

我最近从SQL Server切换到PostgreSQL并试图找到相应的pivot功能。我无法使用使用SQL Server实现的交叉表获得所需的输出。

示例数据。

CREATE TABLE loc
AS
  SELECT location, sub_location, step, amount
  FROM ( VALUES
    ( 100 , '100_A', 'step_1', 2 ),
    ( 100 , '100_A', 'step_2', 7 ),
    ( 100 , '100_A', 'step_3', 6 ),
    ( 100 , '100_B', 'step_1', 5 ),
    ( 100 , '100_B', 'step_2', 8 ),
    ( 100 , '100_B', 'step_3', 9 )
  ) AS t(location, sub_location, step, amount);

我正在尝试实现以下结果集。

Location    Sub_location    Step_1  Step_2  Step_3
--------    ------------    ------  ------  ------
100         100_A           2       7       6
100         100_B           5       8       9

我很容易实现这是MS SQL。我的交叉表查询,

Select * from crosstab
    (
     'select location, sub_location, step, amount from loc',
     'select distinct step from loc'
    )
    as final_result(location varchar,sub_location varchar, step_1 int, step_2 int, step_3 int);

我只看到一行而不是两行。无论如何要克服postgres中的这种限制。

1 个答案:

答案 0 :(得分:0)

使用ARRAY解决复合键问题

我认为您遇到的真正问题是,为了交叉,您的sub_location是您的主要标识符(名称)的一部分。而且,不是crosstab calls an extra column.

  

"额外"对于具有相同row_name值的所有行,列应该是相同的。

因此,实质上,形成名称的复合键必须由用户序列化。您仍然可以使用ARRAY将此工作序列化为text[]类型的ARRAY[location, sub_location]::text[]

SELECT *
FROM crosstab(
  $$ SELECT ARRAY[location, sub_location]::text[], step, amount FROM loc ORDER BY 1, 2, 3; $$,
  $$ SELECT DISTINCT step FROM loc ORDER BY 1; $$
) AS t(location text[], step_1 int, step_2 int, step_3 int );

  location   | step_1 | step_2 | step_3 
-------------+--------+--------+--------
 {100,100_A} |      2 |      7 |      6
 {100,100_B} |      5 |      8 |      9
(2 rows)

利用具有实际位置的子位置

现在,因为在您的特定情况下的子位置具有位置数据,我们可以通过切换顺序使其更短。我不会在100_的表格中存储子位置,但我们可以在此处使用它。要明确的是,如果location: 100, sublocation: 'A'是我存储它的方式,这将无法工作。

SELECT *
FROM crosstab(
  $$ SELECT sub_location, location, step, amount FROM loc ORDER BY 1, 2, 3; $$,
  $$ SELECT DISTINCT step FROM loc ORDER BY 1; $$
) AS t(sub_location text, location int, step_1 int, step_2 int, step_3 int );
 sub_location | location | step_1 | step_2 | step_3 
--------------+----------+--------+--------+--------
 100_A        |      100 |      2 |      7 |      6
 100_B        |      100 |      5 |      8 |      9
(2 rows)

这消除了调用ARRAY的复杂性。

简化您的用例

我们也可以在此时删除`位置或在父查询中切换顺序。

SELECT *
FROM crosstab(
  $$ SELECT sub_location, step, amount FROM loc ORDER BY 1, 2, 3; $$,
  $$ SELECT DISTINCT step FROM loc ORDER BY 1; $$
) AS t(location_full text, step_1 int, step_2 int, step_3 int );

 location_full | step_1 | step_2 | step_3 
---------------+--------+--------+--------
 100_A         |      2 |      7 |      6
 100_B         |      5 |      8 |      9
(2 rows)

不确定上述哪种方法最适合您。不要忘记CREATE EXTENSION tablefunc;当然,这是否完全主观,这是否比非交叉带版本更容易。