2012-10-14 29 views
12

में क्रॉसस्टैब के लिए गतिशील रूप से कॉलम उत्पन्न करें, मैं PostgreSQL में crosstab क्वेरी बनाने की कोशिश कर रहा हूं, जैसे कि यह स्वचालित रूप से हार्डकोडिंग के बजाय crosstab कॉलम उत्पन्न करता है। मैंने एक ऐसा फ़ंक्शन लिखा है जो गतिशील रूप से कॉलम सूची जेनरेट करता है जिसे मुझे अपने crosstab क्वेरी के लिए आवश्यक है। विचार इस कार्य के परिणाम को गतिशील एसक्यूएल का उपयोग करके crosstab क्वेरी में प्रतिस्थापित करना है।PostgreSQL

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


-- Table which has be pivoted 
CREATE TABLE test_db 
(
    kernel_id int, 
    key int, 
    value int 
); 

INSERT INTO test_db VALUES 
(1,1,99), 
(1,2,78), 
(2,1,66), 
(3,1,44), 
(3,2,55), 
(3,3,89); 


-- This function dynamically returns the list of columns for crosstab 
CREATE FUNCTION test() RETURNS TEXT AS ' 
DECLARE 
    key_id int; 
    text_op TEXT = '' kernel_id int, ''; 
BEGIN 
    FOR key_id IN SELECT DISTINCT key FROM test_db ORDER BY key LOOP 
    text_op := text_op || key_id || '' int , '' ; 
    END LOOP; 
    text_op := text_op || '' DUMMY text''; 
    RETURN text_op; 
END; 
' LANGUAGE 'plpgsql'; 

-- This query works. I just need to convert the static list 
-- of crosstab columns to be generated dynamically. 
SELECT * FROM 
crosstab 
(
    'SELECT kernel_id, key, value FROM test_db ORDER BY 1,2', 
    'SELECT DISTINCT key FROM test_db ORDER BY 1' 
) 
AS x (kernel_id int, key1 int, key2 int, key3 int); -- How can I replace .. 
-- .. this static list with a dynamically generated list of columns ? 

उत्तर

6

आप इसके लिए प्रदान किए गए सी फ़ंक्शन crosstab_hash का उपयोग कर सकते हैं।

मैनुअल इस संबंध में बहुत स्पष्ट नहीं है। यह at the end of the chapter on crosstab() with two parameters:

उल्लेख किया गया है आप परिणाम स्तंभ नाम और प्रत्येक प्रश्न में प्रकार को लिखने के लिए होने से बचाने के पूर्वनिर्धारित कार्यों बना सकते हैं। पिछले खंड में उदाहरण देखें। crosstab के इस फ़ॉर्म के लिए अंतर्निहित सी फ़ंक्शन crosstab_hash नाम दिया गया है।

अपने उदाहरण के लिए:

CREATE OR REPLACE FUNCTION f_cross_test_db(text, text) 
    RETURNS TABLE (kernel_id int, key1 int, key2 int, key3 int) 
    AS '$libdir/tablefunc','crosstab_hash' LANGUAGE C STABLE STRICT; 

कॉल:

SELECT * FROM f_cross_test_db(
     'SELECT kernel_id, key, value FROM test_db ORDER BY 1,2' 
    ,'SELECT DISTINCT key FROM test_db ORDER BY 1'); 

ध्यान दें कि आप एक अलग वापसी प्रकार के साथ हर crosstab समारोह के लिए एक अलग crosstab_hash समारोह बनाना होगा।

Here is another closely related answer.


आपका समारोह स्तंभ सूची बल्कि जटिल है उत्पन्न करने के लिए, परिणाम गलत (intkernel_id के बाद लापता) है, यह इस SQL ​​क्वेरी के साथ बदला जा सकता है:

SELECT 'kernel_id int, ' 
     || string_agg(DISTINCT key::text, ' int, ' ORDER BY key::text) 
     || ' int, DUMMY text' 
FROM test_db; 

और इसका उपयोग गतिशील रूप से किसी भी तरह से नहीं किया जा सकता है।

+0

विस्तृत उत्तर @Erwin के लिए धन्यवाद। मैं crosstab क्षेत्रों के एक स्थिर सेट के साथ मूल crosstab काम करने में सक्षम था। मुझे आश्चर्य है कि हमारे पास स्तंभों की सूची को गतिशील रूप से उत्पन्न करने का कोई तरीका नहीं है और इस प्रकार postgresql में गतिशील क्रॉसस्टैब है। क्या पोस्टग्रेस्क्ल में ऐसा करने के बारे में कोई अन्य दौर या हैकी तरीका है जिसे आप जानते हैं? – invinc4u

+1

@ invinc4u: समस्या यह है कि किसी फ़ंक्शन का रिटर्न प्रकार गतिशील रूप से बदला नहीं जा सकता है। आप कार्य को गतिशील रूप से फिर से बना सकते हैं और फिर इसे तुरंत कॉल कर सकते हैं। लेकिन यह मुश्किल व्यवसाय है ... –

+1

हां समझ में आता है। लेकिन मैं वहां उम्मीद कर रहा था कि डायनमिक एसक्यूएल का उपयोग करके कुछ हैकी तरीके से समस्या हल हो गई होगी। गतिशील एसक्यूएल तैयार करने के बाद, हमें केवल क्वेरी निष्पादित करने में सक्षम होना चाहिए और इसे हमारे लिए गतिशील क्रॉसस्टैब उत्पन्न करना चाहिए। उदाहरण के लिए इसे देखें: http://muhammedsalimp.wordpress.com/2010/07/16/dynamic-cross-tab-query-in-sql-server/। मैं पहले से ही अपना एसक्यूएल सर्वर खो रहा हूं :(वैसे भी आपकी मदद और मार्गदर्शन के लिए एक टन धन्यवाद !! – invinc4u

2

@ erwin-brandstetter: फ़ंक्शन का रिटर्न प्रकार कोई समस्या नहीं है यदि आप हमेशा परिवर्तित परिणामों के साथ JSON प्रकार लौट रहे हैं।

यहाँ समारोह मैं के साथ आया है:

CREATE OR REPLACE FUNCTION report.test(
    i_start_date TIMESTAMPTZ, 
    i_end_date TIMESTAMPTZ, 
    i_interval INT 
    ) RETURNS TABLE (
    tab JSON 
    ) AS $ab$ 
DECLARE 
    _key_id TEXT; 
    _text_op TEXT = ''; 
    _ret JSON; 
BEGIN 
    -- SELECT DISTINCT for query results 
    FOR _key_id IN 
    SELECT DISTINCT at_name 
     FROM report.company_data_date cd 
     JOIN report.company_data_amount cda ON cd.id = cda.company_data_date_id 
     JOIN report.amount_types at ON cda.amount_type_id = at.id 
    WHERE date_start BETWEEN i_start_date AND i_end_date 
     AND interval_type_id = i_interval 
    LOOP 
    -- build function_call with datatype of column 
     IF char_length(_text_op) > 1 THEN 
      _text_op := _text_op || ', ' || _key_id || ' NUMERIC(20,2)'; 
     ELSE 
      _text_op := _text_op || _key_id || ' NUMERIC(20,2)'; 
     END IF; 
    END LOOP; 
    -- build query with parameter filters 
    RETURN QUERY 
    EXECUTE ' 
     SELECT array_to_json(array_agg(row_to_json(t))) 
      FROM (
     SELECT * FROM crosstab(''SELECT date_start, at.at_name, cda.amount ct 
      FROM report.company_data_date cd 
      JOIN report.company_data_amount cda ON cd.id = cda.company_data_date_id 
      JOIN report.amount_types at ON cda.amount_type_id = at.id 
     WHERE date_start between $$' || i_start_date::TEXT || '$$ AND $$' || i_end_date::TEXT || '$$ 
      AND interval_type_id = ' || i_interval::TEXT || ' ORDER BY date_start'') 
      AS ct (date_start timestamptz, ' || _text_op || ') 
      ) t;'; 
END; 
$ab$ LANGUAGE 'plpgsql'; 

इसलिए, जब आप इसे चलाने आपको प्रभावी परिणाम JSON में मिलता है, और आप को पता है कि कितने मूल्यों पिवट किए गए थे की जरूरत नहीं है:

select * from report.test(now()- '1 week'::interval, now(), 1); 
                                tab                              
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 
[{"date_start":"2015-07-27T08:40:01.277556-04:00","burn_rate":0.00,"monthly_revenue":5800.00,"cash_balance":0.00},{"date_start":"2015-07-27T08:50:02.458868-04:00","burn_rate":34000.00,"monthly_revenue":15800.00,"cash_balance":24000.00}] 
(1 row) 

संपादित:

: यदि आप अपने crosstab में डेटाटाइप्स मिलाया है, तो आप तर्क कुछ इस तरह के साथ प्रत्येक स्तंभ के लिए उसे ढूंढने की जोड़ सकते हैं
1

यहां वर्णित दृष्टिकोण http://www.cureffi.org/2013/03/19/automatically-creating-pivot-table-column-names-in-postgresql/ मेरे लिए अच्छा काम करता है। सीधे पिवट तालिका को पुनर्प्राप्त करने के बजाय। आसान तरीका यह है कि फ़ंक्शन को SQL क्वेरी स्ट्रिंग उत्पन्न करने दें। मांग पर परिणामस्वरूप SQL क्वेरी स्ट्रिंग को गतिशील रूप से निष्पादित करें।