PostgreSQL Crosstab क्वेरी


196

क्या कोई जानता है कि PostgreSQL में क्रॉसस्टैब क्वेरी कैसे बनाई जाती है?
उदाहरण के लिए मेरे पास निम्न तालिका है:

Section    Status    Count
A          Active    1
A          Inactive  2
B          Active    4
B          Inactive  5

मैं निम्नलिखित क्रॉस्टैब को वापस करने के लिए क्वेरी चाहूंगा:

Section    Active    Inactive
A          1         2
B          4         5

क्या यह संभव है?


1
मेरे पास एक अलग संरचना थी और इस उदाहरण को समझना थोड़ा कठिन था, इसलिए मैंने इस स्टैकओवरफ्लो के विचार के अपने तरीके का दस्तावेजीकरण किया ।.com//49051959 / 808723 । शायद किसी के लिए भी यह मददगार हो।
गेमस्क्रिप्ट

जवाबों:


317

डेटाबेस के अनुसार एक बार अतिरिक्त मॉड्यूलtablefunc स्थापित करें , जो फ़ंक्शन प्रदान करता है । चूंकि Postgres 9.1 आप इसके लिए उपयोग कर सकते हैं :crosstab()CREATE EXTENSION

CREATE EXTENSION IF NOT EXISTS tablefunc;

परीक्षण के मामले में सुधार हुआ

CREATE TABLE tbl (
   section   text
 , status    text
 , ct        integer  -- "count" is a reserved word in standard SQL
);

INSERT INTO tbl VALUES 
  ('A', 'Active', 1), ('A', 'Inactive', 2)
, ('B', 'Active', 4), ('B', 'Inactive', 5)
                    , ('C', 'Inactive', 7);  -- ('C', 'Active') is missing

सरल रूप - लापता विशेषताओं के लिए फिट नहीं है

crosstab(text)साथ 1 इनपुट पैरामीटर:

SELECT *
FROM   crosstab(
   'SELECT section, status, ct
    FROM   tbl
    ORDER  BY 1,2'  -- needs to be "ORDER BY 1,2" here
   ) AS ct ("Section" text, "Active" int, "Inactive" int);

यह दिखाता है:

धारा | सक्रिय | निष्क्रिय
--------- + -------- + ----------
 ए | 1 | 2
 B | 4 | 5
 सी |      7 | - मैं !!
  • कास्टिंग और नाम बदलने की कोई आवश्यकता नहीं है।
  • इसके लिए गलत परिणाम पर ध्यान दें C: मान 7पहले कॉलम के लिए भरा गया है। कभी-कभी, यह व्यवहार वांछनीय है, लेकिन इस उपयोग के मामले के लिए नहीं।
  • सरल रूप प्रदान इनपुट क्वेरी में ठीक तीन कॉलम तक सीमित है : row_name , श्रेणी , मानअतिरिक्त कॉलम के लिए कोई जगह नहीं है जैसे नीचे 2-पैरामीटर विकल्प में।

सुरक्षित रूप

crosstab(text, text)साथ 2 इनपुट पैरामीटर:

SELECT *
FROM   crosstab(
   'SELECT section, status, ct
    FROM   tbl
    ORDER  BY 1,2'  -- could also just be "ORDER BY 1" here

  , $$VALUES ('Active'::text), ('Inactive')$$
   ) AS ct ("Section" text, "Active" int, "Inactive" int);

यह दिखाता है:

धारा | सक्रिय | निष्क्रिय
--------- + -------- + ----------
 ए | 1 | 2
 B | 4 | 5
 सी | |        7   - !!
  • के लिए सही परिणाम पर ध्यान दें C

  • दूसरा पैरामीटर है कि रिटर्न एक किसी भी प्रश्न हो सकता है पंक्ति प्रति विशेषता अंत में स्तंभ परिभाषा के आदेश से मेल खाते। अक्सर आप इस तरह से अंतर्निहित तालिका से अलग विशेषताओं को क्वेरी करना चाहेंगे:

    'SELECT DISTINCT attribute FROM tbl ORDER BY 1'

    यह मैनुअल में है।

    चूँकि आपको स्तंभ परिभाषा सूची में सभी कॉलमों को किसी भी तरह से लिखना है (पूर्व-परिभाषित वेरिएंट को छोड़कर ), यह आमतौर पर प्रदर्शन की तरह एक छोटी सूची प्रदान करने के लिए अधिक कुशल है :crosstabN()VALUES

    $$VALUES ('Active'::text), ('Inactive')$$)

    या (मैनुअल में नहीं):

    $$SELECT unnest('{Active,Inactive}'::text[])$$  -- short syntax for long lists
  • मैंने उद्धरण को आसान बनाने के लिए डॉलर का उपयोग किया ।

  • आप विभिन्न डेटा प्रकारों के साथ कॉलम भी आउटपुट कर सकते हैं crosstab(text, text)- जब तक कि वैल्यू कॉलम का टेक्स्ट प्रतिनिधित्व लक्ष्य प्रकार के लिए मान्य इनपुट है। इस तरह से आप हो सकता है अलग तरह का है और उत्पादन की विशेषताओं text, date, numericसंबंधित विशेषताओं के लिए आदि। मैनुअल में अध्यायcrosstab(text, text) के अंत में एक कोड उदाहरण है ।

db <> यहाँ fiddle

उन्नत उदाहरण हैं


\crosstabview psql में

9.6 पोस्टर्स ने इस मेटा-कमांड को अपने डिफ़ॉल्ट इंटरैक्टिव टर्मिनल psll में जोड़ा । आप पहले crosstab()पैरामीटर के रूप में उपयोग की जाने वाली क्वेरी को चला सकते हैं और उसे \crosstabview(तुरंत या अगले चरण में) फीड कर सकते हैं । पसंद:

db=> SELECT section, status, ct FROM tbl \crosstabview

उपरोक्त के समान परिणाम, लेकिन यह विशेष रूप से क्लाइंट की ओर एक प्रतिनिधित्व सुविधा है । इनपुट पंक्तियों को थोड़ा अलग तरीके से व्यवहार किया जाता है, इसलिए ORDER BYआवश्यक नहीं है। \crosstabviewमैनुअल के लिए विवरण उस पृष्ठ के निचले भाग में अधिक कोड उदाहरण हैं।

संबंधित जवाब dba.SE पर Daniel Vérité (psql फीचर के लेखक):



पहले से स्वीकार किए जाते हैं जवाब पुरानी हो चुकी है।

  • फ़ंक्शन crosstab(text, integer)का संस्करण पुराना है। दूसरे integerपैरामीटर को नजरअंदाज किया जाता है। मैं वर्तमान मैनुअल को उद्धृत करता हूं :

    crosstab(text sql, int N) ...

    का अप्रचलित संस्करण crosstab(text)। पैरामीटर Nको अब नजरअंदाज कर दिया गया है, क्योंकि मूल्य कॉलम की संख्या हमेशा कॉलिंग क्वेरी द्वारा निर्धारित की जाती है

  • अनावश्यक कास्टिंग और नामकरण।

  • यह विफल रहता है अगर एक पंक्ति में सभी विशेषताएँ नहीं हैं। लापता विशेषताओं को ठीक से संभालने के लिए ऊपर दो इनपुट मापदंडों के साथ सुरक्षित संस्करण देखें।

  • ORDER BYके एक-पैरामीटर रूप में आवश्यक है crosstab()नियम पुस्तिका:

    व्यवहार में SQL क्वेरी को हमेशा ORDER BY 1,2यह सुनिश्चित करने के लिए निर्दिष्ट करना चाहिए कि इनपुट पंक्तियों को ठीक से आदेश दिया गया है


3
+1, अच्छा राइटअप, ध्यान देने के लिए धन्यवादIn practice the SQL query should always specify ORDER BY 1,2 to ensure that the input rows are properly ordered
क्रिस्टोफ़ीड

मुझे $$ VALUES .. $$ का उपयोग करने में कुछ समस्याएं हैं। मैंने इसके बजाय 'VALUES' ('' <Attr> '' :: <टाइप>), .. 'का प्रयोग किया है
Marco Fantasia

क्या हम क्रॉसस्टैब क्वेरी में पैरामीटर बाइंडिंग निर्दिष्ट कर सकते हैं? मुझे यह त्रुटि मिल रही है =>
आशीष

1
क्या क्रॉसस्टैब क्वेरी में कॉलम के लिए डिफ़ॉल्ट मान सेट करना संभव है?
आशीष

2
@ आशीष: कृपया एक नया प्रश्न शुरू करें। टिप्पणियाँ जगह नहीं हैं। आप संदर्भ के लिए इसे हमेशा लिंक कर सकते हैं।
एरविन ब्रान्डसेट्टर

30

आप अतिरिक्त मॉड्यूल टेबलफंक के crosstab()फ़ंक्शन का उपयोग कर सकते हैं - जिसे आपको प्रति डेटाबेस एक बार स्थापित करना होगा । PostgreSQL 9.1 के बाद से आप इसके लिए उपयोग कर सकते हैं :CREATE EXTENSION

CREATE EXTENSION tablefunc;

आपके मामले में, मेरा मानना ​​है कि यह कुछ इस तरह दिखाई देगा:

CREATE TABLE t (Section CHAR(1), Status VARCHAR(10), Count integer);

INSERT INTO t VALUES ('A', 'Active',   1);
INSERT INTO t VALUES ('A', 'Inactive', 2);
INSERT INTO t VALUES ('B', 'Active',   4);
INSERT INTO t VALUES ('B', 'Inactive', 5);

SELECT row_name AS Section,
       category_1::integer AS Active,
       category_2::integer AS Inactive
FROM crosstab('select section::text, status, count::text from t',2)
            AS ct (row_name text, category_1 text, category_2 text);

यदि आप क्रॉसस्टैब क्वेरी में एक पैरामीटर का उपयोग करते हैं, तो आपको इसे ठीक से बचना होगा। उदाहरण: (ऊपर से) कहते हैं कि आप केवल सक्रिय लोगों को चाहते हैं: चयन करें ... फ्रॉस्ट क्रॉस्टैब ('सेक्शन सेक्शन :: टेक्स्ट, स्टेटस, काउंट :: टेक्स्ट इन टी फ्रॉम स्टेटस =' 'एक्टिव' '', 2) ए.एस. .. (दोहरे उद्धरण नोटिस)। यदि उपयोगकर्ता द्वारा पैरामीटर रनटाइम पर पारित किया जाता है, उदाहरण के लिए (उदाहरण के लिए एक फ़ंक्शन पैरामीटर के रूप में) आप कह सकते हैं: चयन करें ... फ्रॉस्ट क्रॉस्टैब ('सेक्शन :: टेक्स्ट, स्टेटस, काउंट :: टी: फ्रॉम टी स्टेटस =' ' '' || par_active || '' '', 2) AS ... (ट्रिपल कोट्स यहाँ!)। BIRT में यह भी साथ काम करता है? प्लेसहोल्डर।
वीरवर्थ

26
SELECT section,
       SUM(CASE status WHEN 'Active' THEN count ELSE 0 END) AS active, --here you pivot each status value as a separate column explicitly
       SUM(CASE status WHEN 'Inactive' THEN count ELSE 0 END) AS inactive --here you pivot each status  value as a separate column explicitly

FROM t
GROUP BY section

1
क्या कोई यह समझा सकता है कि टेबलफंक मॉड्यूल में क्रॉस्टैब फ़ंक्शन इस उत्तर में क्या जोड़ता है, जो दोनों हाथ में काम करता है, और मेरे दिमाग को समझना आसान है?
जॉन पॉवेल

4
@ JohnBarça: इस तरह के एक साधारण मामले को आसानी से CASE स्टेटमेंट के साथ हल किया जा सकता है। हालाँकि, यह केवल पूर्णांकों की तुलना में अधिक विशेषताओं और / या अन्य डेटा प्रकारों के साथ बहुत जल्दी प्रकट होता है। एक तरफ के रूप में: यह फ़ॉर्म कुल फ़ंक्शन का उपयोग करता है sum(), इसका उपयोग करना बेहतर होगा min()या max()नहीं ELSEजो इसके लिए textभी काम करता है । लेकिन इसका सूक्ष्म रूप से भिन्न प्रभाव है corosstab(), जो प्रति विशेषता "पहले" मूल्य का उपयोग करता है। कोई फर्क नहीं पड़ता जब तक कि केवल एक ही हो सकता है। अंत में, प्रदर्शन भी प्रासंगिक है। crosstab()C में लिखा गया है और कार्य के लिए अनुकूलित है।
एरविन ब्रान्डसेट्टर

यह मेरे लिए काम नहीं करता है, postgresql के लिए। मुझे त्रुटि मिलती हैERROR: 42803: aggregate function calls may not be nested
ऑड्रे

1
@ क्या आप उसी SQL को नहीं चला रहे हैं?

2
कोड के केवल एक खंड बनाम
स्पष्टीकरण को

10

JSON एकत्रीकरण के साथ समाधान:

CREATE TEMP TABLE t (
  section   text
, status    text
, ct        integer  -- don't use "count" as column name.
);

INSERT INTO t VALUES 
  ('A', 'Active', 1), ('A', 'Inactive', 2)
, ('B', 'Active', 4), ('B', 'Inactive', 5)
                   , ('C', 'Inactive', 7); 


SELECT section,
       (obj ->> 'Active')::int AS active,
       (obj ->> 'Inactive')::int AS inactive
FROM (SELECT section, json_object_agg(status,ct) AS obj
      FROM t
      GROUP BY section
     )X

धन्यवाद, इसने मुझे एक संबंधित समस्या के साथ मदद की।
जेफचटर

1

क्षमा करें, यह पूरा नहीं हुआ है क्योंकि मैं इसे यहाँ परीक्षण नहीं कर सकता, लेकिन यह आपको सही दिशा में ले जा सकता है। मैं कुछ ऐसी चीज़ों से अनुवाद कर रहा हूँ जिनका उपयोग मैं एक समान क्वेरी करता हूँ:

select mt.section, mt1.count as Active, mt2.count as Inactive
from mytable mt
left join (select section, count from mytable where status='Active')mt1
on mt.section = mt1.section
left join (select section, count from mytable where status='Inactive')mt2
on mt.section = mt2.section
group by mt.section,
         mt1.count,
         mt2.count
order by mt.section asc;

मैं जिस कोड से काम कर रहा हूं वह है:

select m.typeID, m1.highBid, m2.lowAsk, m1.highBid - m2.lowAsk as diff, 100*(m1.highBid - m2.lowAsk)/m2.lowAsk as diffPercent
from mktTrades m
   left join (select typeID,MAX(price) as highBid from mktTrades where bid=1 group by typeID)m1
   on m.typeID = m1.typeID
   left join (select typeID,MIN(price) as lowAsk  from mktTrades where bid=0 group by typeID)m2
   on m1.typeID = m2.typeID
group by m.typeID, 
         m1.highBid, 
         m2.lowAsk
order by diffPercent desc;

जो टाइपिड लौटेगा, उच्चतम मूल्य बोली और सबसे कम कीमत पूछी गई और दोनों के बीच का अंतर (एक सकारात्मक अंतर का मतलब होगा कि इसे बेचा जा सकता है) से कम के लिए खरीदा जा सकता है।


1
आप एक खंड से गायब हैं, अन्यथा यह सही है। मेरे सिस्टम पर व्याख्या की योजना बेतहाशा अलग है - क्रॉसस्टैब फ़ंक्शन की लागत 22.5 है, जबकि LEFT JOIN दृष्टिकोण 91.38 की लागत के साथ लगभग 4 गुना महंगा है। यह लगभग दो बार भी कई भौतिक रीड्स का उत्पादन करता है और हैश जॉइन करता है - जो कि अन्य जॉइन प्रकारों की तुलना में काफी महंगा हो सकता है।
यिर्मयाह पेश्का

धन्यवाद यिर्मयाह, यह जानना अच्छा है। मैंने अन्य उत्तर को बदल दिया है, लेकिन आपकी टिप्पणी रखने योग्य है, इसलिए मैं इसे हटा नहीं पाऊंगा।
लांसह

-1

Crosstabtablefuncएक्सटेंशन के तहत फ़ंक्शन उपलब्ध है । आपको डेटाबेस के लिए एक बार यह एक्सटेंशन बनाना होगा।

सृजन विस्तार tablefunc;

आप क्रॉस टैब का उपयोग करके धुरी तालिका बनाने के लिए नीचे दिए गए कोड का उपयोग कर सकते हैं:

create table test_Crosstab( section text,
<br/>status text,
<br/>count numeric)

<br/>insert into test_Crosstab values ( 'A','Active',1)
                <br/>,( 'A','Inactive',2)
                <br/>,( 'B','Active',4)
                <br/>,( 'B','Inactive',5)

select * from crosstab(
<br/>'select section
    <br/>,status
    <br/>,count
    <br/>from test_crosstab'
    <br/>)as ctab ("Section" text,"Active" numeric,"Inactive" numeric)

1
यह उत्तर पहले से मौजूद उत्तर पर कुछ नहीं जोड़ता है।
19-21 बजे इरविन ब्रान्डस्टेट्टर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.