डायनामिक एसक्यूएल और RETURN
टाइप करें
आप डायनामिक SQL निष्पादित करना चाहते हैं . मूल रूप से, EXECUTE
की सहायता से plpgsql में यह आसान है . आपको ज़रूरत नहीं एक कर्सर। वास्तव में, ज्यादातर समय आप स्पष्ट कर्सर के बिना बेहतर होते हैं।
आप जिस समस्या का सामना कर रहे हैं:आप अभी तक अपरिभाषित प्रकार के रिकॉर्ड वापस करना चाहते हैं . किसी फ़ंक्शन को RETURN
. में अपना रिटर्न प्रकार घोषित करने की आवश्यकता होती है क्लॉज (या OUT
. के साथ) या INOUT
पैरामीटर)। आपके मामले में आपको गुमनाम रिकॉर्ड पर वापस जाना होगा, क्योंकि संख्या , नाम और प्रकार लौटाए गए कॉलम अलग-अलग होते हैं। पसंद:
CREATE FUNCTION data_of(integer)
RETURNS SETOF record AS ...
हालांकि, यह विशेष रूप से उपयोगी नहीं है। आपको प्रत्येक कॉल के साथ कॉलम परिभाषा सूची प्रदान करनी होगी। पसंद:
SELECT * FROM data_of(17)
AS foo (colum_name1 integer
, colum_name2 text
, colum_name3 real);
लेकिन आप यह भी कैसे करेंगे, जब आप पहले से कॉलम नहीं जानते हैं?
आप कम संरचित दस्तावेज़ डेटा प्रकारों जैसे json
का उपयोग कर सकते हैं , jsonb
, hstore
या xml
. देखें:
- डेटाबेस में डेटा टेबल कैसे स्टोर करें?
लेकिन, इस प्रश्न के प्रयोजन के लिए, मान लें कि आप यथासंभव व्यक्तिगत, सही ढंग से टाइप किए गए और नामित कॉलम वापस करना चाहते हैं।
निश्चित रिटर्न प्रकार के साथ सरल समाधान
कॉलम datahora
एक दिया हुआ प्रतीत होता है, मैं मान लूंगा डेटा प्रकार timestamp
और यह कि अलग-अलग नाम और डेटा प्रकार वाले हमेशा दो और कॉलम होते हैं।
नाम हम रिटर्न प्रकार में सामान्य नामों के पक्ष में छोड़ देंगे।
प्रकार हम भी छोड़ देंगे, और सभी को text
पर डाल देंगे चूंकि हर डेटा प्रकार को text
में डाला जा सकता है ।
CREATE OR REPLACE FUNCTION data_of(_id integer)
RETURNS TABLE (datahora timestamp, col2 text, col3 text)
LANGUAGE plpgsql AS
$func$
DECLARE
_sensors text := 'col1::text, col2::text'; -- cast each col to text
_type text := 'foo';
BEGIN
RETURN QUERY EXECUTE '
SELECT datahora, ' || _sensors || '
FROM ' || quote_ident(_type) || '
WHERE id = $1
ORDER BY datahora'
USING _id;
END
$func$;
चर _sensors
और _type
इसके बजाय इनपुट पैरामीटर हो सकते हैं।
RETURNS TABLE
पर ध्यान दें खंड।
RETURN QUERY EXECUTE
. के उपयोग पर ध्यान दें . डायनामिक क्वेरी से पंक्तियों को वापस करने के लिए यह अधिक शानदार तरीकों में से एक है।
मैं फ़ंक्शन पैरामीटर के लिए एक नाम का उपयोग करता हूं, बस USING
. बनाने के लिए RETURN QUERY EXECUTE
. का क्लॉज कम भ्रमित। $1
SQL-स्ट्रिंग में फ़ंक्शन पैरामीटर को संदर्भित नहीं करता है बल्कि USING
. के साथ पास किए गए मान को संदर्भित करता है खंड। (दोनों $1
. होते हैं इस सरल उदाहरण में उनके संबंधित क्षेत्र में।)
_sensors
. के लिए उदाहरण मान नोट करें :प्रत्येक कॉलम को text
type टाइप करने के लिए कास्ट किया जाता है ।
इस प्रकार का कोड SQL इंजेक्शन के लिए बहुत संवेदनशील है . मैं quote_ident()
use का इस्तेमाल करता हूं इससे बचाव के लिए। वेरिएबल _sensors
. में कुछ कॉलम नामों को एक साथ लंपिंग करना quote_ident()
. के इस्तेमाल को रोकता है (और आमतौर पर एक बुरा विचार है!) सुनिश्चित करें कि किसी अन्य तरीके से कोई खराब सामग्री नहीं हो सकती है, उदाहरण के लिए अलग-अलग कॉलम नामों को quote_ident()
के माध्यम से चलाकर बजाय। एक VARIADIC
पैरामीटर दिमाग में आता है ...
PostgreSQL 9.1 के बाद से आसान
संस्करण 9.1 या बाद के संस्करण के साथ आप format()
. का उपयोग कर सकते हैं और सरल बनाने के लिए:
RETURN QUERY EXECUTE format('
SELECT datahora, %s -- identifier passed as unescaped string
FROM %I -- assuming the name is provided by user
WHERE id = $1
ORDER BY datahora'
,_sensors, _type)
USING _id;
फिर से, अलग-अलग कॉलम नाम ठीक से बच सकते हैं और यह साफ तरीका होगा।
समान प्रकार साझा करने वाले स्तंभों की परिवर्तनीय संख्या
आपके प्रश्न अपडेट के बाद ऐसा लगता है कि आपके रिटर्न प्रकार में
. है- एक चर संख्या स्तंभों की
- लेकिन सभी कॉलम एक जैसे टाइप
double precision
(उपनामfloat8
)
ARRAY
का उपयोग करें इस मामले में मानों की एक चर संख्या को नेस्ट करने के लिए टाइप करें। इसके अतिरिक्त, मैं कॉलम नामों के साथ एक सरणी लौटाता हूं:
CREATE OR REPLACE FUNCTION data_of(_id integer)
RETURNS TABLE (datahora timestamp, names text[], values float8[])
LANGUAGE plpgsql AS
$func$
DECLARE
_sensors text := 'col1, col2, col3'; -- plain list of column names
_type text := 'foo';
BEGIN
RETURN QUERY EXECUTE format('
SELECT datahora
, string_to_array($1) -- AS names
, ARRAY[%s] -- AS values
FROM %s
WHERE id = $2
ORDER BY datahora'
, _sensors, _type)
USING _sensors, _id;
END
$func$;
विभिन्न पूर्ण तालिका प्रकार
वास्तव में तालिका के सभी कॉलम को वापस करने के लिए , बहुरूपी प्रकार . का उपयोग करके एक सरल, शक्तिशाली समाधान है :
CREATE OR REPLACE FUNCTION data_of(_tbl_type anyelement, _id int)
RETURNS SETOF anyelement
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE format('
SELECT *
FROM %s -- pg_typeof returns regtype, quoted automatically
WHERE id = $1
ORDER BY datahora'
, pg_typeof(_tbl_type))
USING _id;
END
$func$;
कॉल करें (महत्वपूर्ण!):
SELECT * FROM data_of(NULL::pcdmet, 17);
pcdmet
बदलें किसी अन्य तालिका नाम के साथ कॉल में।
यह कैसे काम करता है?
anyelement
एक छद्म डेटा प्रकार, एक बहुरूपी प्रकार, किसी भी गैर-सरणी डेटा प्रकार के लिए प्लेसहोल्डर है। anyelement
. की सभी घटनाएं फ़ंक्शन में रन टाइम पर प्रदान किए गए उसी प्रकार का मूल्यांकन करें। फ़ंक्शन के तर्क के रूप में परिभाषित प्रकार के मान की आपूर्ति करके, हम परोक्ष रूप से रिटर्न प्रकार को परिभाषित करते हैं।
PostgreSQL स्वचालित रूप से बनाई गई प्रत्येक तालिका के लिए एक पंक्ति प्रकार (एक समग्र डेटा प्रकार) को परिभाषित करता है, इसलिए प्रत्येक तालिका के लिए एक अच्छी तरह से परिभाषित प्रकार होता है। इसमें अस्थायी टेबल शामिल हैं, जो तदर्थ उपयोग के लिए सुविधाजनक है।
कोई भी प्रकार NULL
हो सकता है . एक NULL
में हाथ डालें मान, तालिका के प्रकार पर डाला गया:NULL::pcdmet
।
अब फ़ंक्शन एक अच्छी तरह से परिभाषित पंक्ति प्रकार देता है और हम SELECT * FROM data_of()
का उपयोग कर सकते हैं पंक्ति को विघटित करने और अलग-अलग कॉलम प्राप्त करने के लिए।
pg_typeof(_tbl_type)
तालिका का नाम वस्तु पहचानकर्ता प्रकार regtype
. के रूप में देता है . जब स्वचालित रूप से text
में परिवर्तित हो जाता है , पहचानकर्ता स्वचालित रूप से दोहरे-उद्धृत और स्कीमा-योग्य हैं यदि आवश्यक हो, तो स्वचालित रूप से SQL इंजेक्शन से बचाव करें। यह स्कीमा-योग्य तालिका-नामों से भी निपट सकता है जहां quote_ident()
विफल हो जाएगा। देखें:
- तालिका नाम PostgreSQL फ़ंक्शन पैरामीटर के रूप में