从表的行创建两列

时间:2012-05-29 09:27:57

标签: sql postgresql

我有这个表以及其他几个列:

months    amount
-----------------
 Jan       100
 Feb       200
 Jan       400 

我想用这个表创建一个视图,如下所示:

Jan    Feb
--------------------
100    200
400    0

我搜索过它并且PL / SQL解决方案只能用SQL查询完成吗? 谢谢

2 个答案:

答案 0 :(得分:1)

首先确保您拥有Crosstab

CREATE EXTENSION tablefunc;

要获得交叉,你需要至少三列

例如:

name    months    amount
-------------------------
Helga    Jan       100
Helga    Feb       200
Bernd    Jan       400 

然后你就像这样创建你的Crosstable:

SELECT * FROM 
   crosstab('SELECT name, month, amount from temp')
 AS ct(name varchar, jan int, feb int);

name       jan       feb
--------------------------
Helga     100        200
Bernd     400

如果您打算每个月都有列,请坚持manual

中所述的示例
create table sales(year int, month int, qty int);
insert into sales values(2007, 1, 1000);
insert into sales values(2007, 2, 1500);
insert into sales values(2007, 7, 500);
insert into sales values(2007, 11, 1500);
insert into sales values(2007, 12, 2000);
insert into sales values(2008, 1, 1000);

select * from crosstab(
  'select year, month, qty from sales order by 1',
  'select m from generate_series(1,12) m'
) as (
  year int,
  "Jan" int,
  "Feb" int,
  "Mar" int,
  "Apr" int,
  "May" int,
  "Jun" int,
  "Jul" int,
  "Aug" int,
  "Sep" int,
  "Oct" int,
  "Nov" int,
  "Dec" int
);
 year | Jan  | Feb  | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov  | Dec
------+------+------+-----+-----+-----+-----+-----+-----+-----+-----+------+------
 2007 | 1000 | 1500 |     |     |     |     | 500 |     |     |     | 1500 | 2000
 2008 | 1000 |      |     |     |     |     |     |     |     |     |      |
(2 rows)

答案 1 :(得分:1)

您的逻辑存在问题,SQL如何知道它应该

Jan     Feb
100     200
400     0

而不是

Jan     Feb
400     200
100     0

我认为这意味着列应该按升序排列(除非因为数据不足而值为0):

WITH CTE AS
(   SELECT  Month, Amount, ROW_NUMBER() OVER(PARTITION BY Month ORDER BY Amount) AS RowNumber
    FROM    T
)
SELECT  COALESCE(Jan.Amount, 0) AS Jan,
        COALESCE(Feb.Amount, 0) AS Feb,
        COALESCE(Mar.Amount, 0) AS Mar
FROM    (   SELECT  RowNumber, Amount
            FROM    CTE
            WHERE   Month = 'Jan'
        ) jan
        FULL JOIN 
        (   SELECT  RowNumber, Amount
            FROM    CTE
            WHERE   Month = 'Feb'
        ) feb
            ON feb.RowNumber = Jan.RowNumber
        FULL JOIN 
        (   SELECT  RowNumber, Amount
            FROM    CTE
            WHERE   Month = 'Mar'
        ) Mar
            ON Mar.RowNumber = Jan.RowNumber

此逻辑已在SQL Fiddle中进行了测试,但我相当确定您可以将Rownumber用于CROSSTAB以解决您目前使用CROSSTAB遇到的问题。 RowNumber成为你的行值,我从来没有在postgres中使用过Crosstab,但我认为你最终会得到这样的东西:

SELECT  *
FROM    CROSSTAB('SELECT ROW_NUMBER() OVER(PARTITION BY Month ORDER BY Amount) AS RowNumber, Month, Amount FROM T',
                 'SELECT DISTINCT ROW_NUMBER() OVER(PARTITION BY Month ORDER BY Amount) AS RowNumber FROM  T') 
        AS (RowNumber INT, Jan INT, Feb INT, Mar INT);

我不能在SQL Fiddle上使用它,但我认为这是因为CROSSTAB没有启用,因为即使Postgres文档中的示例也不适用于SQL Fiddle。