मैं एक उत्पन्न क्रोस जॉइन कैसे उत्पन्न करता हूं जहां परिणामी तालिका परिभाषा अज्ञात है?


18

एक नाम और मूल्य के साथ अपरिभाषित पंक्ति गणना के साथ दो तालिकाओं को देखते हुए , मैं CROSS JOINउनके मूल्यों पर एक फ़ंक्शन का पिवोट कैसे प्रदर्शित करूंगा ।

CREATE TEMP TABLE foo AS
SELECT x::text AS name, x::int
FROM generate_series(1,10) AS t(x);

CREATE TEMP TABLE bar AS
SELECT x::text AS name, x::int
FROM generate_series(1,5) AS t(x);

उदाहरण के लिए, यदि वह फ़ंक्शन गुणन था, तो मैं नीचे की तरह एक (गुणा) तालिका कैसे उत्पन्न करूंगा,

1..12 की सामान्य गुणन तालिका

उन सभी (arg1,arg2,result)पंक्तियों के साथ उत्पन्न किया जा सकता है

SELECT foo.name AS arg1, bar.name AS arg2, foo.x*bar.x AS result
FROM foo
CROSS JOIN bar; 

तो यह केवल प्रस्तुतिकरण का सवाल है, मैं चाहूंगा कि यह भी एक कस्टम नाम के साथ काम करे - एक ऐसा नाम जो केवल CASTटेक्स्ट के लिए तर्क एड नहीं है, लेकिन तालिका में सेट किया गया है;

CREATE TEMP TABLE foo AS
SELECT chr(x+64) AS name, x::int
FROM generate_series(1,10) AS t(x);

CREATE TEMP TABLE bar AS
SELECT chr(x+72) AS name, x::int
FROM generate_series(1,5) AS t(x);

मुझे लगता है कि यह आसानी से एक गतिशील रिटर्न-प्रकार में सक्षम CROSSTAB के साथ सक्षम होगा।

SELECT * FROM crosstab(
  '
    SELECT foo.x AS arg1, bar.x AS arg2, foo.x*bar.x
    FROM foo
    CROSS JOIN bar
  ', 'SELECT DISTINCT name FROM bar'
) AS **MAGIC**

लेकिन, बिना **MAGIC**मुझे मिलता है

ERROR:  a column definition list is required for functions returning "record"
LINE 1: SELECT * FROM crosstab(

संदर्भ के लिए, नाम के साथ ऊपर के उदाहरण का उपयोग करते हुए इस तरह क्या कुछ है tablefuncकी crosstab()आवश्यकताओं।

SELECT * FROM crosstab(
  '
    SELECT foo.x AS arg1, bar.x AS arg2, foo.x*bar.x
    FROM foo
    CROSS JOIN bar
  '
) AS t(row int, i int, j int, k int, l int, m int);

लेकिन, अब हम barअपने उदाहरण में तालिका की सामग्री और आकार के बारे में धारणा बनाने के लिए वापस आ गए हैं । तो अगर,

  1. टेबल्स अपरिभाषित लंबाई के हैं,
  2. फिर क्रॉस-ज्वाइन अपरिभाषित आयाम के क्यूब का प्रतिनिधित्व करता है (ऊपर के कारण),
  3. कैटागिरी-नाम (क्रॉस-टैब पार्लेंस) तालिका में हैं

उस तरह की प्रस्तुति को उत्पन्न करने के लिए "कॉलम परिभाषा सूची" के बिना पोस्टग्रेएसक्यूएल में हम सबसे अच्छा क्या कर सकते हैं?


1
क्या JSON परिणाम एक अच्छा दृष्टिकोण होगा? क्या ARRAY एक अच्छा एप्रोच होगा? इस तरह, "आउटपुट टेबल" की परिभाषा पहले से ही ज्ञात (और निश्चित) होगी। आप JSON या ARRAY के भीतर लचीलापन रखें। मुझे लगता है कि यह जानकारी को संसाधित करने के लिए बाद में उपयोग किए जाने वाले बहुत सारे उपकरणों पर निर्भर करेगा।
19

यदि संभव हो तो मैं इसे ऊपर की तरह पसंद करूंगा।
इवान कैरोल 20

जवाबों:


12

सरल मामला, स्थिर एसक्यूएल

गैर-डायनामिक साथ समाधान crosstab()सरल मामले के लिए:

SELECT * FROM crosstab(
  'SELECT b.x, f.name, f.x * b.x AS prod
   FROM   foo f, bar b
   ORDER  BY 1, 2'
   ) AS ct (x int, "A" int, "B" int, "C" int, "D" int, "E" int
                 , "F" int, "G" int, "H" int, "I" int, "J" int);

मैं जिसके परिणामस्वरूप कॉलम का आदेश देता हूं foo.name, नहीं foo.x। दोनों समानांतर में क्रमबद्ध किए जाते हैं, लेकिन यह केवल साधारण सेटअप है। अपने मामले के लिए सही क्रम चुनें। दूसरे कॉलम का वास्तविक मान इस क्वेरी (1-पैरामीटर रूप crosstab()) में अप्रासंगिक है ।

हमें crosstab()2 मापदंडों की भी आवश्यकता नहीं है क्योंकि परिभाषा के अनुसार कोई लापता मान नहीं है। देख:

(आप की जगह सवाल में crosstab क्वेरी तय fooसाथ barएक बाद संपादन में। यह भी क्वेरी को ठीक करता है, लेकिन से नाम के साथ काम कर रखता है foo।)

अज्ञात वापसी प्रकार, गतिशील एसक्यूएल

कॉलम नाम और प्रकार गतिशील नहीं हो सकते। SQL कॉल समय पर संख्या, नाम और परिणामित कॉलम के प्रकार को जानने की मांग करता है। या तो स्पष्ट घोषणा से या सिस्टम कैटलॉग में जानकारी से (यही होता है SELECT * FROM tbl: पोस्टग्रेज पंजीकृत तालिका परिभाषा को देखता है।)

आप पोस्टग्रेज को उपयोगकर्ता तालिका में डेटा से परिणामी कॉलम प्राप्त करना चाहते हैं । ऐसा नहीं होगा।

एक तरह से या दूसरे, आपको सर्वर के लिए दो दौर की यात्राएं चाहिए। या तो आप एक कर्सर बनाएँ और फिर उसके माध्यम से चलें। या आप एक अस्थायी तालिका बनाते हैं और फिर उसमें से चयन करते हैं। या आप एक प्रकार रजिस्टर करते हैं और कॉल में इसका उपयोग करते हैं।

या आप बस एक कदम में क्वेरी उत्पन्न करते हैं और इसे अगले में निष्पादित करते हैं:

SELECT $$SELECT * FROM crosstab(
  'SELECT b.x, f.name, f.x * b.x AS prod
   FROM   foo f, bar b
   ORDER  BY 1, 2'
   ) AS ct (x int, $$
 || string_agg(quote_ident(name), ' int, ' ORDER BY name) || ' int)'
FROM   foo;

यह गतिशील रूप से ऊपर क्वेरी उत्पन्न करता है। अगले चरण में इसे निष्पादित करें।

मैं $$नेस्टेड कोट्स को सरल रखने के लिए डॉलर-कोट्स ( ) का उपयोग कर रहा हूं । देख:

quote_ident() अन्यथा अवैध स्तंभ नामों (और संभवतः SQL इंजेक्शन से बचाव) से बचने के लिए आवश्यक है।

सम्बंधित:


मैंने देखा कि उस क्वेरी को निष्पादित करना जिसे आपने "अज्ञात रिटर्न प्रकार, डायनामिक एसक्यूएल" कहा है, वास्तव में केवल एक स्ट्रिंग देता है जो किसी अन्य क्वेरी का प्रतिनिधित्व करता है, और फिर आप कहते हैं "इसे अगले चरण में निष्पादित करें"। क्या इसका मतलब यह है कि उदाहरण के लिए इस से हटकर भौतिक दृष्टिकोण बनाना मुश्किल होगा?
कॉलिन डी

@ColinD: मुश्किल नहीं है, लेकिन सरल असंभव है। आप ज्ञात रिटर्न प्रकार के साथ उत्पन्न एसक्यूएल से एक एमवी बना सकते हैं। लेकिन आपके पास अज्ञात रिटर्न प्रकार के साथ एक एमवी नहीं हो सकता है।
इरविन ब्रान्डेसटेटर

11

उस प्रकार की प्रस्तुति को उत्पन्न करने के लिए "कॉलम परिभाषा सूची" के बिना पोस्टग्रेक्सेल में हम सबसे अच्छा क्या कर सकते हैं?

यदि आप इसे एक प्रस्तुति समस्या के रूप में फ्रेम करते हैं, तो आप पोस्ट-क्वेरी प्रस्तुति सुविधा पर विचार कर सकते हैं।

psql(9.6) के नए संस्करण \crosstabview, SQL समर्थन के बिना क्रॉसस्टैब प्रतिनिधित्व में परिणाम दिखाते हैं (क्योंकि एसक्यूएल सीधे इसका उत्पादन नहीं कर सकता है, जैसा कि @ इरविन के जवाब में उल्लेख किया गया है: एसक्यूएल कॉल समय पर संख्या, नाम और परिणाम कॉलम के प्रकार जानने की मांग करता है )

उदाहरण के लिए, आपकी पहली क्वेरी देता है:

SELECT foo.name AS arg1, bar.name AS arg2, foo.x*bar.x AS result
FROM foo
CROSS JOIN bar
\crosstabview

 arg1 | 1  | 2  | 3  | 4  | 5  
------+----+----+----+----+----
 1    |  1 |  2 |  3 |  4 |  5
 2    |  2 |  4 |  6 |  8 | 10
 3    |  3 |  6 |  9 | 12 | 15
 4    |  4 |  8 | 12 | 16 | 20
 5    |  5 | 10 | 15 | 20 | 25
 6    |  6 | 12 | 18 | 24 | 30
 7    |  7 | 14 | 21 | 28 | 35
 8    |  8 | 16 | 24 | 32 | 40
 9    |  9 | 18 | 27 | 36 | 45
 10   | 10 | 20 | 30 | 40 | 50
(10 rows)

ASCII कॉलम नामों के साथ दूसरा उदाहरण देता है:

SELECT foo.name AS arg1, bar.name AS arg2, foo.x*bar.x
    FROM foo
    CROSS JOIN bar
  \crosstabview

 arg1 | I  | J  | K  | L  | M  
------+----+----+----+----+----
 A    |  1 |  2 |  3 |  4 |  5
 B    |  2 |  4 |  6 |  8 | 10
 C    |  3 |  6 |  9 | 12 | 15
 D    |  4 |  8 | 12 | 16 | 20
 E    |  5 | 10 | 15 | 20 | 25
 F    |  6 | 12 | 18 | 24 | 30
 G    |  7 | 14 | 21 | 28 | 35
 H    |  8 | 16 | 24 | 32 | 40
 I    |  9 | 18 | 27 | 36 | 45
 J    | 10 | 20 | 30 | 40 | 50
(10 rows)

अधिक के लिए psql मैनुअल और https://wiki.postgresql.org/wiki/Crosstabview देखें ।


1
यह वास्तव में बहुत अच्छा है।
इवान कैरोल

1
सबसे सुरुचिपूर्ण वर्कअराउंड।
एरविन ब्रान्डसेट्टर

1

यह कोई निश्चित समाधान नहीं है

यह मेरा अब तक का सबसे अच्छा तरीका है। अभी भी अंतिम सरणी को कॉलम में बदलने की आवश्यकता है।

पहले मुझे दोनों तालिकाओं का कार्टेशियन उत्पाद मिला है:

select foo.name xname, bar.name yname, (foo.x * bar.x)::text as val,
       ((row_number() over ()) - 1) / (select count(*)::integer from foo) as row
 from bar
     cross join foo
 order by bar.name, foo.name

लेकिन, मैंने पहली तालिका की प्रत्येक पंक्ति को पहचानने के लिए एक पंक्ति संख्या जोड़ी है।

((row_number() over ()) - 1) / (select count(*)::integer from foo)

फिर मैं इस प्रारूप में परिणाम तैयार करूंगा:

[Row name] [Array of values]


select col_name, values
from
(
select '' as col_name, array_agg(name) as values from foo
UNION
select fy.name as col_name,
    (select array_agg(t.val) as values
    from  
        (select foo.name xname, bar.name yname, (foo.x * bar.x)::text as val,
              ((row_number() over ()) - 1) / (select count(*)::integer from foo) as row
        from bar
           cross join foo
        order by bar.name, foo.name) t
    where t.row = fy.row)
from
    (select name, (row_number() over(order by name)) - 1 as row from bar) fy
) a
order by col_name;

+---+---------------------+
|   |      ABCDEFGHIJ     |
+---+---------------------+
| I |     12345678910     |
+---+---------------------+
| J |   2468101214161820  |
+---+---------------------+
| K |  36912151821242730  |
+---+---------------------+
| L |  481216202428323640 |
+---+---------------------+
| M | 5101520253035404550 |
+---+---------------------+ 

इसे कॉमा द्वारा सीमांकित स्ट्रिंग में परिवर्तित करना:

select col_name, values
from
(
select '' as col_name, array_to_string(array_agg(name),',') as values from foo
UNION
select fy.name as col_name,
    (select array_to_string(array_agg(t.val),',') as values
    from  
        (select foo.name xname, bar.name yname, (foo.x * bar.x)::text as val,
              ((row_number() over ()) - 1) / (select count(*)::integer from foo) as row
        from bar
           cross join foo
        order by bar.name, foo.name) t
    where t.row = fy.row)
from
    (select name, (row_number() over(order by name)) - 1 as row from bar) fy
) a
order by col_name;


+---+------------------------------+
|   | A,B,C,D,E,F,G,H,I,J          |
+---+------------------------------+
| I | 1,2,3,4,5,6,7,8,9,10         |
+---+------------------------------+
| J | 2,4,6,8,10,12,14,16,18,20    |
+---+------------------------------+
| K | 3,6,9,12,15,18,21,24,27,30   |
+---+------------------------------+
| L | 4,8,12,16,20,24,28,32,36,40  |
+---+------------------------------+
| M | 5,10,15,20,25,30,35,40,45,50 |
+---+------------------------------+

(बस बाद में इसे आज़माने के लिए: http://rextester.com/NBCYXA2183 )


0

साइड नोट के रूप में, यह एसक्यूएल की तरह लगता है: 2016 इसे पॉलीमॉर्फिक टेबल फ़ंक्शन (आईएसओ / आईईसी टीआर 19075-7: 2017) के साथ समायोजित करेगा।

मुझे SQL: 2016 में लिंक व्हाट्स न्यू मिला लेकिन लेखक ने इस पर विस्तार नहीं किया है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.