Транспонирование результата SQL, так что один столбец переходит на несколько столбцов - PullRequest
5 голосов
/ 13 декабря 2011

Я пытаюсь получить данные из таблицы для опроса в определенном формате.Однако все мои попытки, похоже, передают БД из-за слишком большого количества соединений / слишком большого количества данных в БД.

Мои данные выглядят так:

id, user, question_id, answer_id, 
1,   1,   1,           1
3,   1,   3,           15
4,   2,   1,           2
5,   2,   2,           12
6,   2,   3,           20

Примерно 250 000 строк и каждый пользовательимеет около 30 рядов.Я хочу, чтобы результат выглядел следующим образом:

user0, q1, q2,   q3 
1,     1,  NULL, 15
2,     2,  12,   20 

Чтобы у каждого пользователя была одна строка в результате, каждая с отдельным столбцом для каждого ответа.

Я использую Postgres, но ответына любом языке SQL будет признателен, поскольку я могу перевести на Postgres.

РЕДАКТИРОВАТЬ: Мне также нужно иметь возможность иметь дело с пользователями, не отвечающими на вопросы, то есть в примере выше q2 для пользователя 1.

Ответы [ 3 ]

6 голосов
/ 13 декабря 2011

Рассмотрим следующую демонстрацию:

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   crosstab('
    SELECT usr::text
          ,question_id
          ,answer_id
    FROM qa
    ORDER BY 1,2')
 AS ct (
     usr text
    ,q1 int
    ,q2 int
    ,q3 int);

Результат:

 usr | q1 | q2 | q3
-----+----+----+----
 1   |  1 |  9 | 15
 2   |  2 | 12 | 20
(2 rows)

user является зарезервированным словом . Не используйте это как имя столбца! Я переименовал его в usr.

Вам необходимо установить дополнительный модуль tablefunc , который обеспечивает функцию crosstab(). Обратите внимание, что эта операция строго для базы данных . В PostgreSQL 9.1 вы можете просто:

CREATE EXTENSION tablefunc;

Для более старой версии вы должны выполнить shell-скрипт, предоставленный в вашем каталоге contrib. В Debian для PostgreSQL 8.4 это будет:

psql mydb -f /usr/share/postgresql/8.4/contrib/tablefunc.sql
3 голосов
/ 13 декабря 2011

Ответ Erwins хорош, пока не появится отсутствующий ответ для пользователя.Я собираюсь сделать предположение о вас .... у вас есть таблица пользователей, которая имеет одну строку на пользователя, и у вас есть таблица вопросов, которая имеет одну строку на вопросы.

select usr, question_id
from users u inner join questions q on 1=1
order by 1,

Это утверждение будетсоздать строку для каждого пользователя / вопроса, и быть в том же порядке.Превратите его в подзапрос и оставьте его присоединенным к своим данным ...

select usr,question_id,qa.answer_id
from
(select usr, question_id
from users u inner join questions q on 1=1
)a
left join qa on qa.usr = a.usr and qa.question_id = a.usr
order by 1,2

Включите это в кросс-таблицу Erwins и дайте ему кредит за ответ: P

1 голос
/ 19 октября 2015

Я реализовал по-настоящему динамическую функцию для решения этой проблемы без необходимости жесткого кодирования какого-либо определенного количества вопросов или использования внешних модулей / расширений. Это также намного проще в использовании, чем crosstab().

Вы можете найти его здесь: https://github.com/jumpstarter-io/colpivot

Пример, который решает эту конкретную проблему:

begin;

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 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;

Результат:

 usr | 'q1' | 'q2' | 'q3' 
-----+------+------+------
   1 |    1 |    9 |   15
   2 |    2 |   12 |   20
(2 rows)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...