数千列的动态数据透视

时间:2015-07-16 14:13:36

标签: sql postgresql pivot

我正在使用pgAdmin III / PostgreSQL 9.4来存储和处理我的数据。我当前数据的样本:

x | y
--+--
0 | 1
1 | 1
2 | 1
5 | 2
5 | 2
2 | 2
4 | 3
6 | 3
2 | 3

我希望如何格式化:

1, 2, 3 - 列名称是唯一的y
0, 5, 4 - 第一个相应的x
1, 5, 6 - 第二个x值{ 2, 2, 2 - 等等

它需要是动态的,因为我有y的数百万行和数千个唯一值。

使用动态数据透视方法是否正确?我无法成功实现这一点:

DECLARE @columns VARCHAR(8000)

SELECT @columns = COALESCE(@columns + ',[' + cast(y as varchar) + ']',
'[' + cast(y as varchar)+ ']')
FROM tableName
GROUP BY y

DECLARE @query VARCHAR(8000)

SET @query = '
SELECT x
FROM tableName
PIVOT
(
MAX(x)
FOR [y]
IN (' + @columns + ')
)
AS p'

EXECUTE(@query)

它在第一行停止并给出错误:

syntax error at or near "@"

我见过的所有动态透视示例都使用了这个,所以我不确定我做错了什么。任何帮助表示赞赏。谢谢你的时间。

**注意:由于序列很重要,x值以正确的顺序存储非常重要。如有必要,我可以添加另一列来指示顺序。

1 个答案:

答案 0 :(得分:1)

首先 ,当您说"第一行"时,您假设行的自然顺序,这在数据库表中不存在。所以,是的,你需要add another column to indicate sequential order像你已经怀疑的那样。我为此目的假设了一列 tbl_id 。 - 除非你想默认这个穷人的最后(不可靠)度假胜地:ctid

下一步 ,您提供的代码看起来像MS SQL Server代码,对于Postgres完全无效。

最后 ,对于millions of rows and thousands of unique values for Y,尝试返回单个列甚至没有意义。 Postgres有慷慨的限制,但并不是那么慷慨。引用Postgres "About"

  

每个表250 - 1600的最大列数取决于列类型

所以我们甚至没有机会讨论SQL的限制性特征,这要求在执行时知道列及其数据类型,而不是在执行期间动态调整。所以你需要两个单独的电话,就像我们在这个相关问题下非常详细地讨论过一样。

您还会在同一问题下找到alternative returning arrays by @Clodoaldo。实际上,这可能是 完全动态 。这也是我在这里建议的。 查询实际上相当简单:

WITH cte AS (
   SELECT *, row_number() OVER (PARTITION BY y ORDER BY tbl_id) AS rn
   FROM   tbl
   ORDER  BY y, tbl_id
   )
SELECT text 'y' AS col, array_agg (y) AS values
FROM   cte
WHERE  rn = 1

UNION ALL
(  -- parentheses required
SELECT text 'x' || rn, array_agg (x)
FROM   cte
GROUP  BY rn
ORDER  BY rn
);

结果:

col | values
----+--------
y   | {1,2,3}
x1  | {0,5,4}
x2  | {1,5,6}
x3  | {2,2,2}

SQL Fiddle.

解释

  • CTE为每组rn的每一行(每个x)计算一个row_number y。我们将使用它两次,因此是CTE。

  • 外部查询中的第一个SELECT生成y值数组。

  • 外部查询中的第二个SELECT按顺序生成所有x值数组。数组可以有不同的长度。

为什么需要UNION ALL的括号?