在具有复合键(多列)的查询上使用交叉表
Using crosstab on a query with a composite key (multiple columns)
我最近从 SQL 服务器切换到 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 中的这个限制。
使用ARRAY解决组合键问题
我认为您遇到的真正问题是您的 sub_location
是您的主要标识符(名称)的一部分,用于交叉目的。而且,不是 crosstab calls an extra column.
The "extra" columns are expected to be the same for all rows with the same row_name value.
所以本质上,构成名称的组合键必须由用户序列化。您仍然可以使用 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)
利用其中包含实际位置的子位置
现在,因为您的特定情况下的子位置包含位置数据,我们可以通过切换顺序来缩短它。我不会在 table 和 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;
当然,这是否比非交叉表版本更容易是完全主观的。
我最近从 SQL 服务器切换到 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 中的这个限制。
使用ARRAY解决组合键问题
我认为您遇到的真正问题是您的 sub_location
是您的主要标识符(名称)的一部分,用于交叉目的。而且,不是 crosstab calls an extra column.
The "extra" columns are expected to be the same for all rows with the same row_name value.
所以本质上,构成名称的组合键必须由用户序列化。您仍然可以使用 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)
利用其中包含实际位置的子位置
现在,因为您的特定情况下的子位置包含位置数据,我们可以通过切换顺序来缩短它。我不会在 table 和 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;
当然,这是否比非交叉表版本更容易是完全主观的。