如何压扁PostgreSQL结果

时间:2013-08-27 11:02:51

标签: sql postgresql pivot crosstab

我有实验,功能和feature_values。特征在不同的实验中具有价值。所以我有类似的东西:

Experiments:
experiment_id, experiment_name

Features:
feature_id, feature_name

Feature_values:
experiment_id, feature_id, value

让我们说,我有三个实验(exp1,exp2,exp3)和三个特征(feat1,feat2,feat3)。 我想有一个看起来像这样的SQL结果:

feature_name | exp1 | exp2 | exp3
-------------+------+------+-----
feat1        | 100  | 150  | 110
feat2        | 200  | 250  | 210
feat3        | 300  | 350  | 310

我该怎么做? 此外,一个特征可能在一个实验中没有值。

feature_name | exp1 | exp2 | exp3
-------------+------+------+-----
feat1        | 100  | 150  | 110
feat2        | 200  |      | 210
feat3        |      | 350  | 310

SQL-Query应具有良好的性能。将来,feature_values表中可能有数千万个条目。 或者有更好的方法来处理数据吗?

3 个答案:

答案 0 :(得分:4)

这是一个常见的请求。它被称为枢轴或交叉表查询。 PostgreSQL没有任何漂亮的内置语法,但您可以使用the crosstab function from the tablefunc module to do what you want

有关详情,请搜索[postgresql] [pivot][postgresql] [crosstab]

的Stack Overflow

一些关系数据库系统提供了一种使用内置查询执行此操作的好方法,但到目前为止PostgreSQL却没有。

答案 1 :(得分:3)

我在此假设feature_id, experiment_idFeature_values的唯一键。

执行此操作的标准SQL方法是进行n连接

select
    F.feature_name,
    FV1.value as exp1,
    FV2.value as exp2,
    FV3.value as exp3
from Features as F
    left outer join Feature_values as FV1 on FV1.feature_id = F.feature_id and FV1.experiment_id = 1
    left outer join Feature_values as FV2 on FV2.feature_id = F.feature_id and FV2.experiment_id = 2
    left outer join Feature_values as FV3 on FV3.feature_id = F.feature_id and FV3.experiment_id = 3

或像这样的数据透视数据(聚合max实际上并没有聚合任何东西):

select
    F.feature_name,
    max(case when E.experiment_name = 'exp1' then FV.value end) as exp1,
    max(case when E.experiment_name = 'exp2' then FV.value end) as exp2,
    max(case when E.experiment_name = 'exp3' then FV.value end) as exp3
from Features as F
    left outer join Feature_values as FV on FV.feature_id = F.feature_id
    left outer join Experiments as E on E.experiment_id = FV.experiment_id
group by F.feature_name
order by F.feature_name

sql fiddle demo

您还可以考虑使用json(在9.3版本中)或hstore将所有实验值合并到一列中。

答案 2 :(得分:2)

你尝试的是有点困难,因为你试图将一组表作为单个表提供,显然,这涉及一些转换和一些假设。

假设您事先知道只有3个实验且只有3个功能,您可以执行以下操作

SELECT
    feature_id,
    SUM(CASE WHEN experiment_id = 1 THEN value ELSE 0 END) AS Exp1Total,
    SUM(CASE WHEN experiment_id = 2 THEN value ELSE 0 END) AS Exp2Total,
    SUM(CASE WHEN experiment_id = 3 THEN value ELSE 0 END) AS Exp3Total,
FROM
    Feature_values
GROUP BY
    feature_id
ORDER BY
    feature_id

在这种情况下,您的表格将包含实验ID和功能,而不是其名称。要获取他们的名字,您需要加入Features表以及Experiments表。为清楚起见,我省略了这一点,因为我认为最困难的部分是案例逻辑。