2011-12-13 28 views
5

Tôi đang cố gắng lấy dữ liệu ra khỏi bảng cho một cuộc khảo sát theo một định dạng cụ thể. Tuy nhiên tất cả các nỗ lực của tôi dường như bàn giao DB vì quá nhiều tham gia/quá nặng trên DB.Chuyển một kết quả sql để một cột đi vào nhiều cột

Dữ liệu của tôi trông như thế này:

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 

Có khoảng 250.000 hàng và mỗi người dùng có khoảng 30 dòng. Tôi muốn kết quả như sau:

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

Vì vậy mà mỗi người dùng có một dòng trong kết quả, mỗi một cột riêng biệt cho mỗi câu trả lời.

Tôi đang sử dụng Postgres nhưng câu trả lời bằng bất kỳ ngôn ngữ SQL nào sẽ được đánh giá cao như tôi có thể dịch sang Postgres.

EDIT: Tôi cũng cần có khả năng để đối phó với những người dùng không trả lời câu hỏi, ví dụ trong ví dụ trên q2 cho người dùng 1.

+1

tìm kiếm một từ khóa crosstab đây ví dụ http: //stackoverflow.com/questions/3002499/postgresql-crosstab-query –

Trả lời

6

Hãy xem xét các bản demo sau:

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

Kết quả:

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

userreserved word. Không sử dụng nó làm tên cột! Tôi đã đổi tên nó thành usr.

Bạn cần cài đặt mô-đun bổ sung tablefunc cung cấp chức năng crosstab(). Lưu ý rằng hoạt động này đúng là trên mỗi cơ sở dữ liệu. Trong PostgreSQL 9.1 bạn có thể chỉ đơn giản là:

CREATE EXTENSION tablefunc; 

Đối với phiên bản cũ hơn, bạn sẽ thực hiện một vỏ kịch bản được cung cấp trong thư mục contrib của bạn. Trong Debian, cho PostgreSQL 8.4, đó sẽ là:

psql mydb -f /usr/share/postgresql/8.4/contrib/tablefunc.sql 
+0

Đây là nơi tôi đã đi ban đầu, nhưng tôi đang đấu tranh để đối phó với dữ liệu bị thiếu, câu hỏi đã chỉnh sửa. Vì vậy, nếu người dùng không có hàng cho câu hỏi 2, nó vẫn sẽ xuất ra một null (hoặc 0). Có một upvote cho nỗ lực anyway :) – Yule

+0

Crosstab cú pháp có thể là một chút khó khăn ... bạn sẽ cần một hàng trả lại cho mỗi câu trả lời cho mỗi người dùng để cho nó cư trú chính xác. Bên trong khu vực được trích dẫn trong báo cáo tab chéo, xây dựng một bảng (truy vấn phụ có thể xử lý nó ... một cái gì đó giống như từ câu hỏi bên trong của người dùng tham gia trên 1 = 1. Hãy nhớ thứ tự bằng!). Sau đó, hãy tham gia lại dữ liệu của bạn để điền các giá trị. Nếu không, câu trả lời thiếu cho người dùng sẽ bù đắp các trường không chính xác. – Twelfth

+1

@Twelfth: Có biến thể với hai tham số ['crosstab (văn bản, văn bản)'] (http://www.postgresql.org/docs/current/interactive/tablefunc.html#AEN138286) sẽ xử lý các danh mục bị thiếu bằng cách cung cấp danh sách rõ ràng các danh mục trong tham số thứ hai. Chi tiết khác trong [câu trả lời gần đây này] (http://stackoverflow.com/questions/11074489/postgres-buckets-always-filled-from-left-in-crosstab-query/11075727#11075727). –

3

Erwins Câu trả lời là tốt, cho đến khi mất tích câu trả lời cho một người sử dụng xuất hiện. Tôi sẽ đưa ra một giả định về bạn .... bạn có một bảng người dùng có một hàng cho mỗi người dùng và bạn có một bảng câu hỏi có một hàng cho mỗi câu hỏi.

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

Tuyên bố này sẽ tạo một hàng cho mỗi người dùng/câu hỏi và theo cùng thứ tự. Biến nó thành một subquery và để lại đưa nó vào dữ liệu của bạn ...

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 

cắm đó vào tuyên bố crosstab Erwins và cho anh ta tín dụng đối với câu trả lời: P

+0

Chỉ cần thêm ... bạn sẽ cần xác định kết quả crosstab cho từng trường được trả về. Nếu bạn có 3 câu trả lời, AS ct ( văn bản usr , q1 int , q2 int , q3 int); công trinh. Nếu bạn có 30, hãy chuẩn bị để xác định từng cái như thế này. Nếu bạn có câu hỏi mới được thêm vào, câu lệnh chọn chéo bảng sẽ chọn nó, nhưng bạn sẽ cần đảm bảo rằng bạn thêm trường vào danh sách ct() này – Twelfth

1

tôi thực hiện một chức năng thực sự năng động để xử lý này vấn đề mà không cần phải mã cứng bất kỳ số lượng câu hỏi cụ thể hoặc sử dụng các mô-đun/phần mở rộng bên ngoài. Nó cũng đơn giản hơn nhiều để sử dụng hơn crosstab().

Bạn có thể tìm thấy nó ở đây: https://github.com/jumpstarter-io/colpivot

Ví dụ rằng giải quyết vấn đề này cụ thể:

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; 

Kết quả:

usr | 'q1' | 'q2' | 'q3' 
-----+------+------+------ 
    1 | 1 | 9 | 15 
    2 | 2 | 12 | 20 
(2 rows) 
Các vấn đề liên quan