具有动态列和联接计数的PostgreSQL查询
我可能想得太多了,因为我真的不知道从哪里开始。。。但这里有: 我有以下几点 桌子 学生 评估 学生评估 预期产量 “尝试”的第一个查询 “已通过”的第二个查询 让我大吃一惊的部分不是为每个评估进行联接,甚至不是手动定义列,而是为存在的评估“生成”每个列 我觉得这很简单,但我现在实在是太累了,无法弄清楚:)提前感谢您的帮助,下面是一组数据作为示例SELECT*FROM crosstab( '选择studentid、assessmentname、count(studentassessmentid)作为ct(studentid、Assessment1、assessment2、Assessent3、assessment4);简单查询“尝试” 在crosstab()函数中,还需要明确定义列名称,如“Assessment 1”、“Assessment 2”等 或者编写自定义函数来创建动态查询,并使用execute语句执行具有动态列和联接计数的PostgreSQL查询,postgresql,pivot,crosstab,Postgresql,Pivot,Crosstab,我可能想得太多了,因为我真的不知道从哪里开始。。。但这里有: 我有以下几点 桌子 学生 评估 学生评估 预期产量 “尝试”的第一个查询 “已通过”的第二个查询 让我大吃一惊的部分不是为每个评估进行联接,甚至不是手动定义列,而是为存在的评估“生成”每个列 我觉得这很简单,但我现在实在是太累了,无法弄清楚:)提前感谢您的帮助,下面是一组数据作为示例SELECT*FROM crosstab( '选择studentid、assessmentname、count(studentassessmentid)作
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();
“已通过”的第二个查询 它的功能看起来像
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();
我意识到它不再工作了。我正在使用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;
如果因为缺乏结构而让人困惑,那么这里的所有内容都可以找到这并不是特别简单,因为PostgreSQL不擅长数据透视。请查看tablefunc模块中的交叉表函数。您的结果是否必须是您定义的数据透视形状,或者您可以使用3列studentid、评估、尝试/通过?其作用是什么技术上正确-它将我需要的查询生成一个字符串-我想有一种方法可以在一个查询步骤(生成查询/使用查询)中完成,但这肯定让我走上了正确的轨道。
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();
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;