【问题标题】:Using crosstab on a query with a composite key (multiple columns)在具有复合键(多列)的查询上使用交叉表
【发布时间】:2017-04-14 19:17:23
【问题描述】:

我最近从 SQL Server 切换到 PostgreSQL,并试图找到等效的数据透视函数。我无法使用交叉表获得所需的输出,而我可以使用 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 中的这个限制。

【问题讨论】:

  • 这是我第一次发问题,请不要介意格式。
  • 你应该标记这个并要求管理员迁移到Database Administrators

标签: postgresql crosstab composite-key


【解决方案1】:

使用ARRAY解决复合键问题

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

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

所以本质上,组成名称的复合键必须由用户序列化。您仍然可以使用ARRAY[location, sub_location]::text[] 将这项工作序列化为text[] 类型的SQL ARRAY

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 的复杂性。

为您的用例简化

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

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; 当然,这是否比非交叉表版本更容易完全是主观的。

【讨论】:

  • 非常感谢@evan caroll Evan。这是有道理的,也是一个很好的学习。我了解您在 Sub_location 上想说的内容。这只是为了理解这个概念而编造的数据。我是一名 MS Sql 开发人员,刚刚进入 postgresql 的世界。将来你可能会期待我提出更多问题。 :)
猜你喜欢
  • 2015-12-28
  • 2017-09-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-25
  • 2019-05-25
  • 2014-03-14
相关资源
最近更新 更多