PostgreSQL
 sql >> डेटाबेस >  >> RDS >> PostgreSQL

CASE और GROUP BY के साथ पिवट का गतिशील विकल्प

यदि आपने अतिरिक्त मॉड्यूल स्थापित नहीं किया है tablefunc , इस कमांड को एक बार run चलाएँ प्रति डेटाबेस:

CREATE EXTENSION tablefunc;

प्रश्न का उत्तर

आपके मामले के लिए एक बहुत ही बुनियादी क्रॉसस्टैब समाधान:

SELECT * FROM crosstab(
  'SELECT bar, 1 AS cat, feh
   FROM   tbl_org
   ORDER  BY bar, feh')
 AS ct (bar text, val1 int, val2 int, val3 int);  -- more columns?

विशेष कठिनाई यहाँ यह है कि कोई श्रेणी नहीं है (cat ) आधार तालिका में। बुनियादी 1-पैरामीटर फ़ॉर्म . के लिए हम श्रेणी के रूप में सेवा करने वाले डमी मान के साथ केवल एक डमी कॉलम प्रदान कर सकते हैं। वैसे भी मान को नज़रअंदाज़ कर दिया जाता है।

यह दुर्लभ मामलों . में से एक है जहां दूसरा पैरामीटर crosstab() . के लिए फ़ंक्शन आवश्यक नहीं है , क्योंकि सभी NULL इस समस्या की परिभाषा के अनुसार मान केवल लटकने वाले स्तंभों में दाईं ओर दिखाई देते हैं। और आदेश मान . द्वारा निर्धारित किया जा सकता है ।

अगर हमारे पास एक वास्तविक श्रेणी होती परिणाम में मानों के क्रम को निर्धारित करने वाले नामों वाला कॉलम, हमें 2-पैरामीटर फॉर्म की आवश्यकता होगी crosstab() . का . यहां मैं विंडो फंक्शन row_number() . की मदद से एक कैटेगरी कॉलम को सिंथेसाइज करता हूं , आधार के लिए crosstab() पर:

SELECT * FROM crosstab(
   $$
   SELECT bar, val, feh
   FROM  (
      SELECT *, 'val' || row_number() OVER (PARTITION BY bar ORDER BY feh) AS val
      FROM tbl_org
      ) x
   ORDER BY 1, 2
   $$
 , $$VALUES ('val1'), ('val2'), ('val3')$$         -- more columns?
) AS ct (bar text, val1 int, val2 int, val3 int);  -- more columns?

बाकी काफी रन-ऑफ-द-मिल है। इन निकट से संबंधित उत्तरों में अधिक स्पष्टीकरण और लिंक खोजें।

मूल बातें:
अगर आप crosstab() से परिचित नहीं हैं तो इसे पहले पढ़ें समारोह!

  • PostgreSQL क्रॉसस्टैब क्वेरी

उन्नत:

  • Tablefunc का उपयोग करके अनेक स्तंभों पर पिवट करें
  • एक तालिका और एक परिवर्तन लॉग को PostgreSQL में एक दृश्य में मर्ज करें

उचित परीक्षण सेटअप

इस तरह से आपको शुरू में एक टेस्ट केस देना चाहिए:

CREATE TEMP TABLE tbl_org (id int, feh int, bar text);
INSERT INTO tbl_org (id, feh, bar) VALUES
   (1, 10, 'A')
 , (2, 20, 'A')
 , (3,  3, 'B')
 , (4,  4, 'B')
 , (5,  5, 'C')
 , (6,  6, 'D')
 , (7,  7, 'D')
 , (8,  8, 'D');

डायनामिक क्रॉसस्टैब?

बहुत गतिशील नहीं , फिर भी, जैसा कि @Clodoaldo ने टिप्पणी की। plpgsql के साथ डायनामिक रिटर्न प्रकार प्राप्त करना कठिन है। लेकिन वहाँ हैं इसके आसपास के तरीके - कुछ सीमाओं के साथ

तो बाकी को और जटिल न करने के लिए, मैं सरल . के साथ प्रदर्शित करता हूं टेस्ट केस:

CREATE TEMP TABLE tbl (row_name text, attrib text, val int);
INSERT INTO tbl (row_name, attrib, val) VALUES
   ('A', 'val1', 10)
 , ('A', 'val2', 20)
 , ('B', 'val1', 3)
 , ('B', 'val2', 4)
 , ('C', 'val1', 5)
 , ('D', 'val3', 8)
 , ('D', 'val1', 6)
 , ('D', 'val2', 7);

कॉल करें:

SELECT * FROM crosstab('SELECT row_name, attrib, val FROM tbl ORDER BY 1,2')
AS ct (row_name text, val1 int, val2 int, val3 int);

रिटर्न:

 row_name | val1 | val2 | val3
----------+------+------+------
 A        | 10   | 20   |
 B        |  3   |  4   |
 C        |  5   |      |
 D        |  6   |  7   |  8

tablefunc of की अंतर्निहित सुविधा मॉड्यूल

टेबलफंक मॉड्यूल जेनेरिक crosstab() . के लिए एक सरल बुनियादी ढांचा प्रदान करता है कॉलम परिभाषा सूची प्रदान किए बिना कॉल करता है। C . में लिखे गए कई फंक्शन (आमतौर पर बहुत तेज़):

crosstabN()

crosstab1() - crosstab4() पूर्व निर्धारित हैं। एक छोटी सी बात:उन्हें सभी text . की आवश्यकता होती है और वे वापस कर देते हैं . इसलिए हमें अपना integer cast डालना होगा मूल्य। लेकिन यह कॉल को आसान बनाता है:

SELECT * FROM crosstab4('SELECT row_name, attrib, val::text  -- cast!
                         FROM tbl ORDER BY 1,2')

परिणाम:

 row_name | category_1 | category_2 | category_3 | category_4
----------+------------+------------+------------+------------
 A        | 10         | 20         |            |
 B        | 3          | 4          |            |
 C        | 5          |            |            |
 D        | 6          | 7          | 8          |

कस्टम crosstab() समारोह

अधिक कॉलम के लिए या अन्य डेटा प्रकार , हम अपना खुद का समग्र प्रकार . बनाते हैं और फ़ंक्शन (एक बार)।
प्रकार:

CREATE TYPE tablefunc_crosstab_int_5 AS (
  row_name text, val1 int, val2 int, val3 int, val4 int, val5 int);

समारोह:

CREATE OR REPLACE FUNCTION crosstab_int_5(text)
  RETURNS SETOF tablefunc_crosstab_int_5
AS '$libdir/tablefunc', 'crosstab' LANGUAGE c STABLE STRICT;

कॉल करें:

SELECT * FROM crosstab_int_5('SELECT row_name, attrib, val   -- no cast!
                              FROM tbl ORDER BY 1,2');

परिणाम:

 row_name | val1 | val2 | val3 | val4 | val5
----------+------+------+------+------+------
 A        |   10 |   20 |      |      |
 B        |    3 |    4 |      |      |
 C        |    5 |      |      |      |
 D        |    6 |    7 |    8 |      |

एक सभी के लिए बहुरूपी, गतिशील कार्य

यह tablefunc द्वारा कवर की गई चीज़ों से भी आगे जाता है मॉड्यूल।
रिटर्न प्रकार को गतिशील बनाने के लिए मैं इस संबंधित उत्तर में विस्तृत तकनीक के साथ एक बहुरूपी प्रकार का उपयोग करता हूं:

  • एक PL/pgSQL फ़ंक्शन को रिफ़ैक्टर करें ताकि विभिन्न SELECT क्वेरीज़ का आउटपुट लौटाया जा सके

1-पैरामीटर फॉर्म:

CREATE OR REPLACE FUNCTION crosstab_n(_qry text, _rowtype anyelement)
  RETURNS SETOF anyelement AS
$func$
BEGIN
   RETURN QUERY EXECUTE 
   (SELECT format('SELECT * FROM crosstab(%L) t(%s)'
                , _qry
                , string_agg(quote_ident(attname) || ' ' || atttypid::regtype
                           , ', ' ORDER BY attnum))
    FROM   pg_attribute
    WHERE  attrelid = pg_typeof(_rowtype)::text::regclass
    AND    attnum > 0
    AND    NOT attisdropped);
END
$func$  LANGUAGE plpgsql;

2-पैरामीटर फॉर्म के लिए इस प्रकार के साथ ओवरलोड करें:

CREATE OR REPLACE FUNCTION crosstab_n(_qry text, _cat_qry text, _rowtype anyelement)
  RETURNS SETOF anyelement AS
$func$
BEGIN
   RETURN QUERY EXECUTE 
   (SELECT format('SELECT * FROM crosstab(%L, %L) t(%s)'
                , _qry, _cat_qry
                , string_agg(quote_ident(attname) || ' ' || atttypid::regtype
                           , ', ' ORDER BY attnum))
    FROM   pg_attribute
    WHERE  attrelid = pg_typeof(_rowtype)::text::regclass
    AND    attnum > 0
    AND    NOT attisdropped);
END
$func$  LANGUAGE plpgsql;

pg_typeof(_rowtype)::text::regclass :प्रत्येक उपयोगकर्ता-परिभाषित मिश्रित प्रकार के लिए एक पंक्ति प्रकार परिभाषित किया गया है, ताकि सिस्टम कैटलॉग pg_attribute में विशेषताओं (कॉलम) को सूचीबद्ध किया जा सके। . इसे प्राप्त करने के लिए तेज़ लेन:पंजीकृत प्रकार कास्ट करें (regtype ) से text और यह text कास्ट करें करने के लिए regclass

एक बार मिश्रित प्रकार बनाएं:

आपके द्वारा उपयोग किए जाने वाले प्रत्येक रिटर्न प्रकार को एक बार परिभाषित करने की आवश्यकता है:

CREATE TYPE tablefunc_crosstab_int_3 AS (
    row_name text, val1 int, val2 int, val3 int);

CREATE TYPE tablefunc_crosstab_int_4 AS (
    row_name text, val1 int, val2 int, val3 int, val4 int);

...

तदर्थ कॉलों के लिए, आप केवल एक अस्थायी तालिका . भी बना सकते हैं उसी (अस्थायी) प्रभाव के लिए:

CREATE TEMP TABLE temp_xtype7 AS (
    row_name text, x1 int, x2 int, x3 int, x4 int, x5 int, x6 int, x7 int);

या मौजूदा तालिका के प्रकार का उपयोग करें, यदि उपलब्ध हो तो देखें या भौतिक दृश्य देखें।

कॉल करें

उपरोक्त पंक्ति प्रकारों का उपयोग करना:

1-पैरामीटर फॉर्म (कोई गुम मान नहीं):

SELECT * FROM crosstab_n(
   'SELECT row_name, attrib, val FROM tbl ORDER BY 1,2'
 , NULL::tablefunc_crosstab_int_3);

2-पैरामीटर फॉर्म (कुछ मान गायब हो सकते हैं):

SELECT * FROM crosstab_n(
   'SELECT row_name, attrib, val FROM tbl ORDER BY 1'
 , $$VALUES ('val1'), ('val2'), ('val3')$$
 , NULL::tablefunc_crosstab_int_3);

यह एक फ़ंक्शन सभी प्रकार के रिटर्न के लिए काम करता है, जबकि crosstabN() tablefunc . द्वारा प्रदान किया गया ढांचा मॉड्यूल को प्रत्येक के लिए एक अलग फ़ंक्शन की आवश्यकता होती है।
यदि आपने ऊपर दिखाए गए क्रम में अपने प्रकारों का नाम दिया है, तो आपको केवल बोल्ड नंबर को बदलना होगा। आधार तालिका में श्रेणियों की अधिकतम संख्या ज्ञात करने के लिए:

SELECT max(count(*)) OVER () FROM tbl  -- returns 3
GROUP  BY row_name
LIMIT  1;

यदि आप व्यक्तिगत कॉलम चाहते हैं तो यह उतना ही गतिशील है जितना कि यह हो जाता है . एरे जैसे @Clocoaldo द्वारा प्रदर्शित या एक साधारण पाठ प्रतिनिधित्व या दस्तावेज़ प्रकार में लिपटे परिणाम जैसे json या hstore गतिशील रूप से किसी भी श्रेणी के लिए काम कर सकता है।

अस्वीकरण:
उपयोगकर्ता इनपुट को कोड में बदलने पर यह हमेशा संभावित रूप से खतरनाक होता है। सुनिश्चित करें कि इसका उपयोग SQL इंजेक्शन के लिए नहीं किया जा सकता है। अविश्वसनीय उपयोगकर्ताओं (सीधे) से इनपुट स्वीकार न करें।

मूल प्रश्न के लिए कॉल करें:

SELECT * FROM crosstab_n('SELECT bar, 1, feh FROM tbl_org ORDER BY 1,2'
                       , NULL::tablefunc_crosstab_int_3);


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. पोस्टग्रेएसक्यूएल - त्रुटि:कॉलम तिथि टाइप करने के लिए डाली नहीं जा सकती तिथि

  2. अंतिम डाली गई पंक्ति से मूल्य कैसे प्राप्त करें?

  3. बहु-पंक्ति क्वेरी के लिए psql की \ copy का उपयोग करें

  4. डुप्लिकेट पंक्तियां हटाएं (सभी डुप्लिकेट हटाएं नहीं)

  5. Postgresql info_schema में सभी तालिकाओं की सूची बनाएं