postgresql - Postgres colpivot 函数不返回任何内容

标签 postgresql pivot-table crosstab

我尝试遵循本教程 http://www.anhuiyouxi.com/transposing-an-sql-result-so-that-one-column-goes-onto-multiple-columns/ .我通过运行以下代码来做到这一点:

CREATE EXTENSION IF NOT EXISTS tablefunc;

create or replace function colpivot(
    out_table varchar, in_query varchar,
    key_cols varchar[], class_cols varchar[],
    value_e varchar, col_order varchar
) returns void as $$
    declare
        in_table varchar;
        col varchar;
        ali varchar;
        on_e varchar;
        i integer;
        rec record;
        query varchar;
        -- This is actually an array of arrays but postgres does not support an array of arrays type so we flatten it.
        -- We could theoretically use the matrix feature but it's extremly cancerogenous and we would have to involve
        -- custom aggrigates. For most intents and purposes postgres does not have a multi-dimensional array type.
        clsc_cols text[] := array[]::text[];
        n_clsc_cols integer;
        n_class_cols integer;
    begin
        in_table := quote_ident('__' || out_table || '_in');
        execute ('create temp table ' || in_table || ' on commit drop as ' || in_query);
        -- get ordered unique columns (column combinations)
        query := 'select array[';
        i := 0;
        foreach col in array class_cols loop
           if i > 0 then
               query := query || ', ';
           end if;
           query := query || 'quote_literal(' || quote_ident(col) || ')';
           i := i + 1;
        end loop;
        query := query || '] x from ' || in_table;
        for j in 1..2 loop
            if j = 1 then
                query := query || ' group by ';
            else
                query := query || ' order by ';
                if col_order is not null then
                    query := query || col_order || ' ';
                    exit;
                end if;
            end if;
            i := 0;
            foreach col in array class_cols loop
                if i > 0 then
                    query := query || ', ';
                end if;
                query := query || quote_ident(col);
                i := i + 1;
            end loop;
        end loop;
        -- raise notice '%', query;
        for rec in
            execute query
        loop
            clsc_cols := array_cat(clsc_cols, rec.x);
        end loop;
        n_class_cols := array_length(class_cols, 1);
        n_clsc_cols := array_length(clsc_cols, 1) / n_class_cols;
        -- build target query
        query := 'select ';
        i := 0;
        foreach col in array key_cols loop
            if i > 0 then
                 query := query || ', ';
            end if;
            query := query || '_key.' || quote_ident(col) || ' ';
            i := i + 1;
        end loop;
        for j in 1..n_clsc_cols loop
            query := query || ', ';
            col := '';
            for k in 1..n_class_cols loop
                if k > 1 then
                    col := col || ', ';
                end if;
                col := col || clsc_cols[(j - 1) * n_class_cols + k];
            end loop;
            ali := '_clsc_' || j::text;
            query := query || '(' || replace(value_e, '#', ali) || ')' || ' as ' || quote_ident(col) || ' ';
        end loop;
        query := query || ' from (select distinct ';
        i := 0;
        foreach col in array key_cols loop
            if i > 0 then
                query := query || ', ';
            end if;
            query := query || quote_ident(col) || ' ';
            i := i + 1;
        end loop;
        query := query || ' from ' || in_table || ') _key ';
        for j in 1..n_clsc_cols loop
            ali := '_clsc_' || j::text;
            on_e := '';
            i := 0;
            foreach col in array key_cols loop
                if i > 0 then
                    on_e := on_e || ' and ';
                end if;
                on_e := on_e || ali || '.' || quote_ident(col) || ' = _key.' || quote_ident(col) || ' ';
                i := i + 1;
            end loop;
            for k in 1..n_class_cols loop
                on_e := on_e || ' and ';
                on_e := on_e || ali || '.' || quote_ident(class_cols[k]) || ' = ' || clsc_cols[(j - 1) * n_class_cols + k];
            end loop;
            query := query || 'left join ' || in_table || ' as ' || ali || ' on ' || on_e || ' ';
        end loop;
        -- raise notice '%', query;
        execute ('create temp table ' || quote_ident(out_table) || ' on commit drop as ' || query);
        -- cleanup temporary in_table before we return
        execute ('drop table ' || in_table)
       return;
   end;
$$ language plpgsql volatile;

begin;

DROP TABLE IF EXISTS qa;  
create temp table qa (id int, usr int, question_id int, answer_id int);
insert into qa values
 (1,1,1,1)
 ,(2,1,2,9)
 ,(3,1,3,15)
,(4,2,1,2)
,(5,2,2,12)
,(6,2,3,20);
--select * from qa;

select colpivot('_output', $$
    select usr, ('q' || question_id::text) question_id, answer_id from qa
 $$, array['usr'], array['question_id'], '#.answer_id', null);

select * from _output;

rollback;

到达代码的最后一行后,我什么也没有。

我做错了吗?

请指教

最佳答案

我稍微改变了函数,你可以做一个 diff 来查看到底是什么,但看起来函数在最后删除了结果表(我假设它做了一个提交,所以删除它).如果它已经存在,我已经添加了一个临时表。此外,我还删除了围绕表名的 quote_ident() 函数调用,因为在为 out_table(版本 9.6)传递长表名值时会导致问题。

-- Copyright © 2015, Hannes Landeholm <hannes@jumpstarter.io>
-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at http://mozilla.org/MPL/2.0/.

-- See the README.md file distributed with this project for documentation.
create or replace function colpivot(
    out_table varchar, in_query varchar,
    key_cols varchar[], class_cols varchar[],
    value_e varchar, col_order varchar
) returns void as $$
    declare
        in_table varchar;
        col varchar;
        ali varchar;
        on_e varchar;
        i integer;
        rec record;
        query varchar;
        -- This is actually an array of arrays but postgres does not support an array of arrays type so we flatten it.
        -- We could theoretically use the matrix feature but it's extremly cancerogenous and we would have to involve
        -- custom aggrigates. For most intents and purposes postgres does not have a multi-dimensional array type.
        clsc_cols text[] := array[]::text[];
        n_clsc_cols integer;
        n_class_cols integer;
    begin
        in_table := ('__' || out_table || '_in');
        -- if the temp table already exists, drop
        execute ( 'drop TABLE IF EXISTS ' || in_table );
        execute ('create temp table ' || in_table || ' on commit drop as ' || in_query);
        -- get ordered unique columns (column combinations)
        query := 'select array[';
        i := 0;
        foreach col in array class_cols loop
            if i > 0 then
                query := query || ', ';
            end if;
            query := query || 'quote_literal(' || quote_ident(col) || ')';
            i := i + 1;
        end loop;
        query := query || '] x from ' || in_table;
        for j in 1..2 loop
            if j = 1 then
                query := query || ' group by ';
            else
                query := query || ' order by ';
                if col_order is not null then
                    query := query || col_order || ' ';
                    exit;
                end if;
            end if;
            i := 0;
            foreach col in array class_cols loop
                if i > 0 then
                    query := query || ', ';
                end if;
                query := query || quote_ident(col);
                i := i + 1;
            end loop;
        end loop;
        -- raise notice '%', query;
        for rec in
            execute query
        loop
            clsc_cols := array_cat(clsc_cols, rec.x);
        end loop;
        n_class_cols := array_length(class_cols, 1);
        n_clsc_cols := array_length(clsc_cols, 1) / n_class_cols;
        -- build target query
        query := 'select ';
        i := 0;
        foreach col in array key_cols loop
            if i > 0 then
                query := query || ', ';
            end if;
            query := query || '_key.' || quote_ident(col) || ' ';
            i := i + 1;
        end loop;
        for j in 1..n_clsc_cols loop
            query := query || ', ';
            col := '';
            for k in 1..n_class_cols loop
                if k > 1 then
                    col := col || ', ';
                end if;
                col := col || clsc_cols[(j - 1) * n_class_cols + k];
            end loop;
            ali := '_clsc_' || j::text;
            query := query || '(' || replace(value_e, '#', ali) || ')' || ' as ' || quote_ident(col) || ' ';
        end loop;
        query := query || ' from (select distinct ';
        i := 0;
        foreach col in array key_cols loop
            if i > 0 then
                query := query || ', ';
            end if;
            query := query || quote_ident(col) || ' ';
            i := i + 1;
        end loop;
        query := query || ' from ' || in_table || ') _key ';
        for j in 1..n_clsc_cols loop
            ali := '_clsc_' || j::text;
            on_e := '';
            i := 0;
            foreach col in array key_cols loop
                if i > 0 then
                    on_e := on_e || ' and ';
                end if;
                on_e := on_e || ali || '.' || quote_ident(col) || ' = _key.' || quote_ident(col) || ' ';
                i := i + 1;
            end loop;
            for k in 1..n_class_cols loop
                on_e := on_e || ' and ';
                on_e := on_e || ali || '.' || quote_ident(class_cols[k]) || ' = ' || clsc_cols[(j - 1) * n_class_cols + k];
            end loop;
            query := query || 'left join ' || in_table || ' as ' || ali || ' on ' || on_e || ' ';
        end loop;
        -- raise notice '%', query;
        execute ('create temp table ' || out_table || ' as ' || query);
        -- cleanup temporary in_table before we return
        execute ('drop table ' || in_table);
        return;
    end;
$$ language plpgsql volatile;

现在你可以像这样运行它了:

DROP TABLE IF EXISTS qa;  
create temp table qa (id int, usr int, question_id int, answer_id int);
insert into qa values
 (1,1,1,1)
     ,(2,1,2,9)
 ,(3,1,3,15)
,(4,2,1,2)
,(5,2,2,12)
,(6,2,3,20);
--select * from qa;

select colpivot('_output', $$
    select usr, ('q' || question_id::text) question_id, answer_id from qa
 $$, array['usr'], array['question_id'], '#.answer_id', null);

-- then run the select to get the result
select * from _output;

关于postgresql - Postgres colpivot 函数不返回任何内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44643929/

相关文章:

database - PostgreSQL - fatal error : Ident authentication failed for user "myuser"

excel - SSAS 属性层次结构不能用作 Excel 中的行标签

string - 非常奇怪的列字符串 "single-quote-comment-brace-time-char"- postgres 选择中的错误替换 - 可能仅与 JDBC 相关

postgresql - 在 Postgresql 中,有没有办法将列的值限制为枚举?

google-sheets - 如何在同一张工作表中拥有多个数据透视表?

从 Hmisc 包的summary()生成的交叉表中的行百分比

SQL:将行转换为列以实现可变的行数

sql - 将交叉表功能与 DISTINCT ON 相结合

postgresql - 是否可以在不进行两次转换的情况下获取作为 JSON 对象字段的 JSON 对象的字段?

pivot - BigQuery 透视数据行列