PostgreSQL查询包含动态列和来自联接的计数

时间:2015-11-04 03:33:01

标签: postgresql pivot crosstab

我可能想过这个,因为我不确定从哪里开始......但是这里有:

我有以下

评估

students_assessments

预期输出

首次查询“尝试”

student_id | Assessment 1 | Assessment 2 | Assessment 3 | Assessment 4
1            3              1              2              0
2            1              0              0              0
3            2              1              1              0
4            5              3              3              0
5            1              5              0              0
6            2              1              2              0

第二次查询“已通过”

student_id | Assessment 1 | Assessment 2 | Assessment 3 | Assessment 4
1            t              t              f              f
2            t              t              f              f
3            t              t              f              f
4            t              t              t              f
5            t              t              f              f
6            t              t              t              f

绊倒我的部分并没有为每个评估进行连接,甚至没有手动定义列,而是“生成”每列用于存在的评估。

我觉得这很简单,而且我现在太过于劳累而无法弄明白:)请事先感谢您的帮助,这里有一个SQL Fiddle数据作为示例

3 个答案:

答案 0 :(得分:1)

简单查询“尝试”,

select student_id,sum(case when assessment_id=1 then 1 else 0 end) as "Assessment 1",
sum(case when assessment_id=2 then 1 else 0 end) as "Assessment 2",
sum(case when assessment_id=3 then 1 else 0 end) as "Assessment 3",
sum(case when assessment_id=4 then 1 else 0 end) as "Assessment 4",
sum(case when assessment_id=5 then 1 else 0 end) as "Assessment 5",
sum(case when assessment_id=6 then 1 else 0 end) as "Assessment 6"
from assessments_students
group by student_id
order by student_id

在crosstab()函数中,还需要明确定义列名称,如“评估1”,“评估2”等。

或编写用于创建动态查询的自定义函数,并使用EXECUTE语句执行。

DROP FUNCTION get_Attempts() ;

CREATE OR REPLACE FUNCTION get_Attempts() RETURNS text AS
$BODY$
DECLARE
        r1 record;
        str_query text := '';
 BEGIN
    str_query :='select student_id,';
    FOR r1 IN SELECT "_id" , "name" FROM Assessments
    LOOP
      str_query:= str_query || 
                  'sum(case when assessment_id=' || r1."_id" || ' then 1 else 0 end) as  "' || r1.name ||'",' ;
    END LOOP;
    str_query:=trim( trailing  ',' from str_query); -- remove last semicolon
    str_query:= str_query || ' from assessments_students group by student_id order by student_id';
    return str_query;
END
$BODY$
LANGUAGE 'plpgsql' ;

SELECT * FROM get_Attempts();

第二次查询“已通过”

select student_id,
max(case when assessment_id=1 and passed='t' then 't' else 'f' end) as  "Assessment 1",
max(case when assessment_id=2 and passed='t' then 't' else 'f' end) as  "Assessment 2",
max(case when assessment_id=3 and passed='t' then 't' else 'f' end) as  "Assessment 3",
max(case when assessment_id=4 and passed='t' then 't' else 'f' end) as  "Assessment 4",
max(case when assessment_id=5 and passed='t' then 't' else 'f' end) as  "Assessment 5",
max(case when assessment_id=6 and passed='t' then 't' else 'f' end) as  "Assessment 6" 
from assessments_students 
group by student_id 
order by student_id

它的功能看起来像,

DROP FUNCTION get_passed() ;

CREATE OR REPLACE FUNCTION get_passed() RETURNS text AS
$BODY$
DECLARE
        r1 record;
        str_query text := '';
 BEGIN
    str_query :='select student_id,';
    FOR r1 IN SELECT "_id" , "name" FROM Assessments
    LOOP
      str_query:= str_query || 
                  'max(case when assessment_id=' || r1."_id" || ' and passed=''t'' then ''t'' else ''f'' end) as  "' || r1.name ||'",' ;
    END LOOP;
    str_query:=trim( trailing  ',' from str_query); -- remove last semicolon
    str_query:= str_query || ' from assessments_students group by student_id order by student_id';

    return str_query;
END
$BODY$
LANGUAGE 'plpgsql' ;

SELECT * FROM get_passed();

答案 1 :(得分:0)

SELECT * FROM crosstab( '选择studentid,assessmentname,count(studentassessmentid)来自.... [你在这里加入] group by studentid,assessmentname order by 1,2' AS ct(studentid,assesment1,assessment2,assesent3,assessment4);

答案 2 :(得分:0)

我意识到它不再起作用了。我正在使用 PostgreSQL 12。 所以在这里你不能返回非预定义的表类型(我的意思是变量列)或者从不同的字符中选择。

使用匿名代码块、交叉表和临时表的示例。 光标是多余的,我正在解决一个需要使用它们的任务。

CREATE EXTENSION IF NOT EXISTS tablefunc;

DO
$$
    DECLARE
        movie_probe CURSOR FOR
            SELECT m.name movie_name, count(c.id) cinema_count
            FROM movies m
                     JOIN sessions s ON m.id = s.movie_id
                     JOIN cinema_halls ch ON s.cinema_hall_id = ch.id
                     JOIN cinemas c ON ch.cinema_id = c.id
            GROUP BY m.name
            HAVING count(c.name) > 1
            ORDER BY count(c.name) DESC;
        movie_rec          RECORD;
        movie_columns      TEXT DEFAULT '';

        cinemas_probe CURSOR (cond_movie_name TEXT) FOR
            SELECT m.name movie_name, c.name cinema_name
            FROM movies m
                     JOIN sessions s ON m.id = s.movie_id
                     JOIN cinema_halls ch ON s.cinema_hall_id = ch.id
                     JOIN cinemas c ON ch.cinema_id = c.id
            WHERE cond_movie_name = m.name
            ORDER BY c.name;

        cinema_rec         RECORD;
        cinema_row_counter INT DEFAULT 0;
    BEGIN
        DROP TABLE IF EXISTS cinema_multiples_aev;
        CREATE TEMP TABLE cinema_multiples_aev (
            row_id      INT,
            movie_name  TEXT,
            cinema_name TEXT
        );

        OPEN movie_probe;
        LOOP
            FETCH movie_probe INTO movie_rec;
            EXIT WHEN NOT FOUND;

            OPEN cinemas_probe(movie_rec.movie_name);

            LOOP
                FETCH cinemas_probe INTO cinema_rec;
                EXIT WHEN NOT FOUND;
                cinema_row_counter := cinema_row_counter + 1;

                INSERT INTO cinema_multiples_aev (row_id, movie_name, cinema_name)
                VALUES (cinema_row_counter, cinema_rec.movie_name, cinema_rec.cinema_name);
            END LOOP;

            CLOSE cinemas_probe;
            cinema_row_counter := 0;

            movie_columns := movie_columns || ', "' || movie_rec.movie_name || '" TEXT';
        END LOOP;
        CLOSE movie_probe;

        movie_columns := substring(movie_columns FROM 2);

        DROP TABLE IF EXISTS movie_multiples;
        EXECUTE format('CREATE TEMP TABLE movie_multiples(row_id INT, %s)', movie_columns);

        EXECUTE format(E'
        INSERT INTO movie_multiples
        SELECT *
        FROM crosstab(\'select row_id, movie_name, cinema_name
                        from cinema_multiples_aev

                        order by 1,2\')
            AS cinema_multiples_aev(row_id INT, %s);
        ', movie_columns, movie_columns);

        ALTER TABLE movie_multiples DROP COLUMN  row_id;
    END
$$ LANGUAGE plpgsql;

SELECT *
FROM movie_multiples;

DROP TABLE IF EXISTS movie_multiples;
DROP TABLE IF EXISTS cinema_multiples_aev;

如果因为缺乏信任而感到困惑,一切都可以在这里找到github