CROSSTAB PostgreSQL - Oracle 中 PIVOT 的替代方案
CROSSTAB PostgreSQL - Alternative for PIVOT in Oracle
我正在将 Oracle 数据透视表的查询迁移到 PostgreSQL 交叉表。
create table(cntry numeric,week numeric,year numeric,days text,day text);
insert into x_c values(1,15,2015,'DAY1','MON');
...
insert into x_c values(1,15,2015,'DAY7','SUN');
insert into x_c values(2,15,2015,'DAY1','MON');
...
values(4,15,2015,'DAY7','SUN');
我有 4 周的时间在 table 中处理 28 行这样的行。我的 Oracle 查询如下所示:
SELECT * FROM(select * from x_c)
PIVOT (MIN(DAY) FOR (DAYS) IN
('DAY1' AS DAY1 ,'DAY2' DAY2,'DAY3' DAY3,'DAY4' DAY4,'DAY5' DAY5,'DAY6' DAY6,'DAY7' DAY7 ));
结果:
cntry|week|year|day1|day2|day3|day4|day4|day6|day7|
---------------------------------------------------
1 | 15 |2015| MON| TUE| WED| THU| FRI| SAT| SUN|
...
4 | 18 |2015| MON| ...
现在我写了一个这样的 Postgres 交叉表查询:
select *
from crosstab('select cntry,week,year,days,min(day) as day
from x_c
group by cntry,week,year,days'
,'select distinct days from x_c order by 1'
) as (cntry numeric,week numeric,year numeric
,day1 text,day2 text,day3 text,day4 text, day5 text,day6 text,day7 text);
我只得到一行作为输出:
1|17|2015|MON|TUE| ... -- only this row is coming
我哪里做错了?
我从 http://www.postgresonline.com/journal/categories/24-tablefunc
中得到了这个
select year_wk_cntry.t[1],year_wk_cntry.t[2],year_wk_cntry.t[3],day1,day2,day3,day4,day5,day6,day7
from crosstab('select ARRAY[country :: numeric,week,year] as t,days,min(day) as day
from x_c group by country,week,year,days order by 1,2
','select distinct days from x_c order by 1')
as year_wk_cntry (t numeric[],day1 text,day2 text,day3 text,
day4 text, day5 text,day6 text,day7 text);
谢谢!!
ORDER BY
在您的原始查询中缺失。手册:
In practice the SQL query should always specify ORDER BY 1,2
to ensure that the input rows are properly ordered, that is, values with the same row_name
are brought together and correctly ordered within the row.
更重要的是(也更棘手),crosstab()
恰好需要 one row_name 列。这个密切相关的答案中的详细解释:
- Crosstab splitting results due to presence of unrelated field
是将多个列嵌套在一个数组中,然后再取消嵌套。这是不必要的昂贵、容易出错和有限的(仅适用于具有相同数据类型的列,否则您需要转换并可能丢失正确的排序顺序)。
相反,使用 rank()
或 dense_rank()
(在我的示例中为 rnk
)生成代理项 row_name 列:
SELECT cntry, week, year, day1, day2, day3, day4, day5, day6, day7
FROM crosstab (
'SELECT dense_rank() OVER (ORDER BY cntry, week, year)::int AS rnk
, cntry, week, year, days, day
FROM x_c
ORDER BY rnk, days'
, $$SELECT unnest('{DAY1,DAY2,DAY3,DAY4,DAY5,DAY6,DAY7}'::text[])$$
) AS ct (rnk int, cntry int, week int, year int
, day1 text, day2 text, day3 text, day4 text, day5 text, day6 text, day7 text)
ORDER BY rnk;
我对输出列 cntry
、week
、year
使用数据类型 integer
,因为这似乎是(更便宜的)合适的类型。您也可以像以前一样使用数字。
这里是交叉表查询的基础知识:
- PostgreSQL Crosstab Query
我正在将 Oracle 数据透视表的查询迁移到 PostgreSQL 交叉表。
create table(cntry numeric,week numeric,year numeric,days text,day text);
insert into x_c values(1,15,2015,'DAY1','MON');
...
insert into x_c values(1,15,2015,'DAY7','SUN');
insert into x_c values(2,15,2015,'DAY1','MON');
...
values(4,15,2015,'DAY7','SUN');
我有 4 周的时间在 table 中处理 28 行这样的行。我的 Oracle 查询如下所示:
SELECT * FROM(select * from x_c)
PIVOT (MIN(DAY) FOR (DAYS) IN
('DAY1' AS DAY1 ,'DAY2' DAY2,'DAY3' DAY3,'DAY4' DAY4,'DAY5' DAY5,'DAY6' DAY6,'DAY7' DAY7 ));
结果:
cntry|week|year|day1|day2|day3|day4|day4|day6|day7|
---------------------------------------------------
1 | 15 |2015| MON| TUE| WED| THU| FRI| SAT| SUN|
...
4 | 18 |2015| MON| ...
现在我写了一个这样的 Postgres 交叉表查询:
select *
from crosstab('select cntry,week,year,days,min(day) as day
from x_c
group by cntry,week,year,days'
,'select distinct days from x_c order by 1'
) as (cntry numeric,week numeric,year numeric
,day1 text,day2 text,day3 text,day4 text, day5 text,day6 text,day7 text);
我只得到一行作为输出:
1|17|2015|MON|TUE| ... -- only this row is coming
我哪里做错了?
我从 http://www.postgresonline.com/journal/categories/24-tablefunc
中得到了这个 select year_wk_cntry.t[1],year_wk_cntry.t[2],year_wk_cntry.t[3],day1,day2,day3,day4,day5,day6,day7
from crosstab('select ARRAY[country :: numeric,week,year] as t,days,min(day) as day
from x_c group by country,week,year,days order by 1,2
','select distinct days from x_c order by 1')
as year_wk_cntry (t numeric[],day1 text,day2 text,day3 text,
day4 text, day5 text,day6 text,day7 text);
谢谢!!
ORDER BY
在您的原始查询中缺失。手册:
In practice the SQL query should always specify
ORDER BY 1,2
to ensure that the input rows are properly ordered, that is, values with the samerow_name
are brought together and correctly ordered within the row.
更重要的是(也更棘手),crosstab()
恰好需要 one row_name 列。这个密切相关的答案中的详细解释:
- Crosstab splitting results due to presence of unrelated field
相反,使用 rank()
或 dense_rank()
(在我的示例中为 rnk
)生成代理项 row_name 列:
SELECT cntry, week, year, day1, day2, day3, day4, day5, day6, day7
FROM crosstab (
'SELECT dense_rank() OVER (ORDER BY cntry, week, year)::int AS rnk
, cntry, week, year, days, day
FROM x_c
ORDER BY rnk, days'
, $$SELECT unnest('{DAY1,DAY2,DAY3,DAY4,DAY5,DAY6,DAY7}'::text[])$$
) AS ct (rnk int, cntry int, week int, year int
, day1 text, day2 text, day3 text, day4 text, day5 text, day6 text, day7 text)
ORDER BY rnk;
我对输出列 cntry
、week
、year
使用数据类型 integer
,因为这似乎是(更便宜的)合适的类型。您也可以像以前一样使用数字。
这里是交叉表查询的基础知识:
- PostgreSQL Crosstab Query