2010-01-20 12 views
36

मेरे पास एक दिलचस्प conundrum है जो मुझे विश्वास है कि पूरी तरह से एसक्यूएल में हल किया जा सकता है। मैं करने के लिए इसी तरह की टेबल है निम्नलिखित:एसक्यूएल कॉलम के रूप में पंक्तियों को ट्रांसफर करें

responses: 

user_id | question_id | body 
---------------------------- 
1  | 1   | Yes 
2  | 1   | Yes 
1  | 2   | Yes 
2  | 2   | No 
1  | 3   | No 
2  | 3   | No 


questions: 

id | body 
------------------------- 
1 | Do you like apples? 
2 | Do you like oranges? 
3 | Do you like carrots? 

और मैं निम्नलिखित उत्पादन

user_id | Do you like apples? | Do you like oranges? | Do you like carrots? 
--------------------------------------------------------------------------- 
1  | Yes     | Yes     | No 
2  | Yes     | No     | No 

मैं कितने सवाल नहीं होगा पता नहीं है प्राप्त करना चाहते हैं, और वे इतना गतिशील हो जाएगा, मैं सिर्फ हर प्रश्न के लिए कोड नहीं कर सकता। मैं PostgreSQL का उपयोग कर रहा हूं और मुझे विश्वास है कि इसे पारदर्शिता कहा जाता है, लेकिन मुझे ऐसा कुछ भी नहीं लगता है जो SQL में ऐसा करने का मानक तरीका कहता है। मुझे कॉलेज में वापस अपने डेटाबेस वर्ग में ऐसा करना याद है, लेकिन यह MySQL में था और मैं ईमानदारी से याद नहीं करता कि हमने यह कैसे किया।

मुझे लगता है कि यह जुड़ने और GROUP BY कथन का संयोजन होगा, लेकिन मैं यह भी नहीं समझ सकता कि कैसे शुरू किया जाए।

कोई भी यह कैसे जानता है? बहुत बहुत धन्यवाद!

संपादित करें 1: मुझे crosstab का उपयोग करने के बारे में कुछ जानकारी मिली जो मुझे लगता है कि मुझे लगता है, लेकिन मुझे इसकी समझ में परेशानी हो रही है। बेहतर लेखों के लिए लिंक की सराहना की जाएगी!

उत्तर

46

उपयोग:

SELECT r.user_id, 
     MAX(CASE WHEN r.question_id = 1 THEN r.body ELSE NULL END) AS "Do you like apples?", 
     MAX(CASE WHEN r.question_id = 2 THEN r.body ELSE NULL END) AS "Do you like oranges?", 
     MAX(CASE WHEN r.question_id = 3 THEN r.body ELSE NULL END) AS "Do you like carrots?" 
    FROM RESPONSES r 
    JOIN QUESTIONS q ON q.id = r.question_id 
GROUP BY r.user_id 

यह एक मानक धुरी क्वेरी है, क्योंकि आप स्तंभ डेटा के लिए पंक्तियों से डेटा "पिवट" कर रहे हैं।

+0

तो आप कह रहे हैं कि मेरे पास मेरे प्रश्नों की संख्या के आधार पर गतिशील क्वेरी बनाना है? मुझे लगता है कि मैं ऐसा कर सकता था, लेकिन मैं एक और अधिक सरल समाधान की उम्मीद कर रहा था। –

+0

@ टोफर: ओरेकल और एसक्यूएल सर्वर में 'PIVOT' और' UNPIVOT' है, लेकिन यदि आप पिवोट टैग की जांच करते हैं तो आप देखेंगे कि गतिशील क्वेरी फ़ंक्शन के साथ भी आम हैं। –

+1

उत्तर के लिए धन्यवाद। ऐसा लगता है कि अगर मुझे रनटाइम पर क्वेरी जेनरेट करना है तो भी इसे लागू करना सबसे आसान होगा। –

0

contrib/tablefunc/ में इसका एक उदाहरण है।

+1

उम्म, जहां 'योगदान/tablefunc'? क्या आप डॉक्टर सर्वर पर निर्देशिका के बारे में बात कर रहे हैं? –

+0

यह स्रोत पेड़ में उस निर्देशिका में है, या आपको एक 'postgresql-contrib' बाइनरी पैकेज मिल सकता है जिसे आपको इंस्टॉल करने की आवश्यकता है जिसमें यह शामिल है। खराब गुणवत्ता वाले उत्तर के लिए –

+1

डाउनवॉटेड। आपने कोई संदर्भ नहीं दिया है, स्रोत सामग्री का एक वर्ग प्रदान नहीं किया है (यदि यह बदलता है) और इस सवाल से संबंधित करने के लिए कोई प्रयास नहीं किया है। बेहतर उत्तरों के लिए इसका संदर्भ लें http://stackoverflow.com/help/how-to-answer –

6

आप इस तरह से

drop table if exists responses; 
create table responses (
user_id integer, 
question_id integer, 
body text 
); 

drop table if exists questions; 
create table questions (
id integer, 
body text 
); 

insert into responses values (1,1,'Yes'), (2,1,'Yes'), (1,2,'Yes'), (2,2,'No'), (1,3,'No'), (2,3,'No'); 
insert into questions values (1, 'Do you like apples?'), (2, 'Do you like oranges?'), (3, 'Do you like carrots?'); 

select * from crosstab('select responses.user_id, questions.body, responses.body from responses, questions where questions.id = responses.question_id order by user_id') as ct(userid integer, "Do you like apples?" text, "Do you like oranges?" text, "Do you like carrots?" text); 

प्रथम में crosstab समारोह के साथ इस उदाहरण हल कर सकते हैं, तो आप tablefunc विस्तार स्थापित करना होगा। 9.1 संस्करण के बाद से आप इसे एक्सटेंशन एक्सटेंशन का उपयोग करके कर सकते हैं:

CREATE EXTENSION tablefunc; 
2

मैंने गतिशील क्वेरी उत्पन्न करने के लिए एक फ़ंक्शन लिखा था। यह क्रॉसस्टैब के लिए एसक्यूएल उत्पन्न करता है और एक दृश्य बनाता है (यदि यह मौजूद है तो इसे पहले छोड़ देता है)। आप अपने परिणाम प्राप्त करने के लिए दृश्य से चयन कर सकते हैं। यहाँ

CREATE OR REPLACE FUNCTION public.c_crosstab (
    eavsql_inarg varchar, 
    resview varchar, 
    rowid varchar, 
    colid varchar, 
    val varchar, 
    agr varchar 
) 
RETURNS void AS 
$body$ 
DECLARE 
    casesql varchar; 
    dynsql varchar;  
    r record; 
BEGIN 
dynsql=''; 

for r in 
     select * from pg_views where lower(viewname) = lower(resview) 
    loop 
     execute 'DROP VIEW ' || resview; 
    end loop; 

casesql='SELECT DISTINCT ' || colid || ' AS v from (' || eavsql_inarg || ') eav ORDER BY ' || colid; 
FOR r IN EXECUTE casesql Loop 
    dynsql = dynsql || ', ' || agr || '(CASE WHEN ' || colid || '=''' || r.v || ''' THEN ' || val || ' ELSE NULL END) AS ' || agr || '_' || r.v; 
END LOOP; 
dynsql = 'CREATE VIEW ' || resview || ' AS SELECT ' || rowid || dynsql || ' from (' || eavsql_inarg || ') eav GROUP BY ' || rowid; 
RAISE NOTICE 'dynsql %1', dynsql; 
EXECUTE dynsql; 
END 

$body$ 
LANGUAGE 'plpgsql' 
VOLATILE 
CALLED ON NULL INPUT 
SECURITY INVOKER 
COST 100; 

और मैं इसे कैसे उपयोग करते हैं::

यहाँ समारोह है

SELECT c_crosstab('query_txt', 'view_name', 'entity_column_name', 'attribute_column_name', 'value_column_name', 'first'); 

उदाहरण: फिस्ट आप चलाएँ:

SELECT c_crosstab('Select * from table', 'ct_view', 'usr_id', 'question_id', 'response_value', 'first'); 

से:

Select * from ct_view; 
10

मैंने किसी भी विशिष्ट श्रेणी के उत्तरों को हार्ड कोड या बाहरी मॉड्यूल/एक्सटेंशन का उपयोग किए बिना इस समस्या को संभालने के लिए वास्तव में गतिशील फ़ंक्शन लागू किया। यह कॉलम ऑर्डरिंग पर पूर्ण नियंत्रण भी देता है और एकाधिक कुंजी और वर्ग/विशेषता कॉलम का समर्थन करता है।

आप इसे यहाँ पा सकते हैं: https://github.com/jumpstarter-io/colpivot

उदाहरण है कि इस विशेष समस्या का हल:

begin; 

create temporary table responses (
    user_id integer, 
    question_id integer, 
    body text 
) on commit drop; 

create temporary table questions (
    id integer, 
    body text 
) on commit drop; 

insert into responses values (1,1,'Yes'), (2,1,'Yes'), (1,2,'Yes'), (2,2,'No'), (1,3,'No'), (2,3,'No'); 
insert into questions values (1, 'Do you like apples?'), (2, 'Do you like oranges?'), (3, 'Do you like carrots?'); 

select colpivot('_output', $$ 
    select r.user_id, q.body q, r.body a from responses r 
     join questions q on q.id = r.question_id 
$$, array['user_id'], array['q'], '#.a', null); 

select * from _output; 

rollback; 

यह आउटपुट:

user_id | 'Do you like apples?' | 'Do you like carrots?' | 'Do you like oranges?' 
---------+-----------------------+------------------------+------------------------ 
     1 | Yes     | No      | Yes 
     2 | Yes     | No      | No 
+0

बहुत अच्छा! साझा करने के लिए धन्यवाद और इसे खोलने के स्रोत के लिए धन्यवाद! हालांकि इसके प्रदर्शन के बारे में कुछ बेंचमार्क देखना पसंद करेंगे (विशेष रूप से यहां SO पर जैसे नए खोजकर्ता इसकी क्षमताओं पर भरोसा करना चाहते हैं)। –

+0

मैं उद्धरण से कैसे छुटकारा पा सकता हूं? कॉलम नामों में – Diego

+0

ग्रेट!, धन्यवाद। +1 – John

संबंधित मुद्दे