【问题标题】:PostgreSQL Crosstab issues / "Return and SQL tuple descriptions are incompatible"PostgreSQL 交叉表问题/“返回和 SQL 元组描述不兼容”
【发布时间】:2019-05-12 20:55:40
【问题描述】:

下午好,我正在使用 POSTGRESql 9.2 版,我正在尝试使用交叉表函数来转置表上的两列,以便以后可以将其加入到不同的 SELECT 查询中。

我已经安装了 tablefunc 扩展。

但是我不断收到这个“返回和 SQL 元组描述不兼容”的错误,这似乎是因为类型转换。
我不需要它们是特定类型。

我原来的 SELECT 查询是这样的

SELECT inventoryid, ttype, tamount

FROM inventorytesting

这给了我以下结果:

inventoryid           ttype    tamount
2451530088940460        7       0.2
2451530088940460        2       0.5
2451530088940460        8       0.1
2451530088940460        1       15.7
8751530077940461        7       0.7
8751530077940461        2       0.2
8751530077940461        8       1.1
8751530077940461        1       19.2

我的目标是:

inventoryid          7      2       8       1
8751530077940461    0.7    0.2     1.1     19.2
2451530088940460    0.2    0.5     0.1     15.7

“ttype”字段有 49 个不同的值,例如“7”、“2”、“8”、“1”,它们是固定的。
'tamount' 字段的值会根据 'inventoryid' 字段的不同而变化,但总会有 49 个,即使它的值为零。它永远不会是“空的”。

我尝试了一些我可以在互联网上找到的变体,总结如下:

SELECT *

FROM    crosstab (

    $$SELECT inventoryid, ttype, tamount
    FROM inventorytesting
    WHERE inventoryid = '2451530088940460'
    ORDER BY inventoryid, ttype$$
) 
AS ct("inventoryid" text,"ttype" smallint,"tamount" numeric) 

inventorytesting 表上的字段类型是

select column_name, data_type from information_schema.columns
where table_name = 'inventorytesting'  

结果:

column_name    data_type
id             bigint
ttype          smallint
tamount        numeric
tunit          text
tlessthan      smallint
plantid        text
sessiontime    bigint
deleted        smallint
inventoryid    text
docdata        text
docname        text
labid          bigint

任何指针都会很棒。

【问题讨论】:

    标签: postgresql


    【解决方案1】:

    demo:db<>fiddle

    生成的表定义必须包含您期望的表结构 - 透视表 - 而不是给定表的结构:

    SELECT *
    FROM crosstab(
        $$SELECT inventoryid, ttype, tamount
        FROM inventorytesting
        WHERE inventoryid = '2451530088940460'
        ORDER BY inventoryid, ttype$$
    ) 
    AS ct("inventoryid" text,"type1" numeric,"type2" numeric,"type7" numeric,"type8" numeric) 
    

    另外,不需要使用crosstab 函数。只需使用标准的CASE 函数即可实现枢轴:

    SELECT
        inventoryid,
        SUM(CASE WHEN ttype = 1 THEN tamount END) AS type1,
        SUM(CASE WHEN ttype = 2 THEN tamount END) AS type2,
        SUM(CASE WHEN ttype = 7 THEN tamount END) AS type7,
        SUM(CASE WHEN ttype = 8 THEN tamount END) AS type8
    FROM
        inventorytesting
    GROUP BY 1
    

    如果您使用的是 9.4 或更高版本,则可以使用 Postgres 特定的 FILTER 子句:

    SELECT
        inventoryid,
        SUM(tamount) FILTER (WHERE ttype = 1) AS type1,
        SUM(tamount) FILTER (WHERE ttype = 2) AS type2,
        SUM(tamount) FILTER (WHERE ttype = 7) AS type7,
        SUM(tamount) FILTER (WHERE ttype = 8) AS type8
    FROM
        inventorytesting
    GROUP BY 1
    

    demo:db<>fiddle

    【讨论】:

    • 感谢您对交叉表最后一部分的澄清,我能够考虑到这一点,但是我喜欢您使用“CASE”功能解决我的问题优雅得多。再次感谢您,先生。
    【解决方案2】:

    使用crosstab,您可以定义实际结果表(基本上是数据透视的结果)。输入查询定义了三列,然后将其处理为:

    1. 在实际行中分组列结果
    2. 数据透视列
    3. 数据透视列的值

    在您的情况下,crosstab 因此需要定义为:

    ct(
        "inventoryid" text,
        "tamount_1" numeric,
        "tamount_2" numeric,
        "tamount_3" numeric,
        ...
    )
    

    然后,列标题将按照内部查询的ORDER BY 定义的顺序与列ttype 的某个值相关联。

    crosstab 的问题是 ttype 的缺失值(例如,4 的某些值返回但 3 没有返回),结果列将是 124 , ... 3 丢失。在这里,您必须确保(如果您需要一致的输出)您的内部查询至少返回一个NULL 行(例如通过LEFT JOIN)。

    【讨论】:

    • 非常感谢您扩展交叉表的预期和要求。我能够通过您的解决方案和“S-man”添加到 CROSSTAB 中获得所需的解决方案,但我最终省略了交叉表并使用 S-MAN 建议的 CASE 函数,因为它以更优雅的方式解决了我的问题。再次感谢您。
    猜你喜欢
    • 1970-01-01
    • 2017-12-14
    • 1970-01-01
    • 2010-09-11
    • 1970-01-01
    • 2011-05-11
    • 1970-01-01
    • 2011-10-30
    相关资源
    最近更新 更多