我最近从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中的这种限制。
答案 0 :(得分:0)
我认为您遇到的真正问题是,为了交叉,您的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;
当然,这是否完全主观,这是否比非交叉带版本更容易。