मैं अन्य उत्तरों में कुछ सलाह से असहमत हूं। यह PL/pgSQL के साथ किया जा सकता है और मुझे लगता है कि यह ज्यादातर बहुत बेहतर . है क्लाइंट एप्लिकेशन में प्रश्नों को इकट्ठा करने के लिए। यह तेज़ और साफ-सुथरा है और ऐप अनुरोधों में केवल न्यूनतम वायर भेजता है। SQL कथन डेटाबेस के अंदर सहेजे जाते हैं, जिससे इसे बनाए रखना आसान हो जाता है - जब तक आप क्लाइंट एप्लिकेशन में सभी व्यावसायिक तर्क एकत्र नहीं करना चाहते, यह सामान्य वास्तुकला पर निर्भर करता है।
गतिशील SQL के साथ PL/pgSQL फ़ंक्शन
CREATE OR REPLACE FUNCTION func(
_ad_nr int = NULL
, _ad_nr_extra text = NULL
, _ad_info text = NULL
, _ad_postcode text = NULL
, _sname text = NULL
, _pname text = NULL
, _cname text = NULL)
RETURNS TABLE(id int, match text, score int, nr int, nr_extra text
, info text, postcode text, street text, place text
, country text, the_geom geometry)
LANGUAGE plpgsql AS
$func$
BEGIN
-- RAISE NOTICE '%', -- for debugging
RETURN QUERY EXECUTE concat(
$$SELECT a.id, 'address'::text, 1 AS score, a.ad_nr, a.ad_nr_extra
, a.ad_info, a.ad_postcode$$
, CASE WHEN (_sname, _pname, _cname) IS NULL THEN ', NULL::text' ELSE ', s.name' END -- street
, CASE WHEN (_pname, _cname) IS NULL THEN ', NULL::text' ELSE ', p.name' END -- place
, CASE WHEN _cname IS NULL THEN ', NULL::text' ELSE ', c.name' END -- country
, ', a.wkb_geometry'
, concat_ws('
JOIN '
, '
FROM "Addresses" a'
, CASE WHEN NOT (_sname, _pname, _cname) IS NULL THEN '"Streets" s ON s.id = a.street_id' END
, CASE WHEN NOT (_pname, _cname) IS NULL THEN '"Places" p ON p.id = s.place_id' END
, CASE WHEN _cname IS NOT NULL THEN '"Countries" c ON c.id = p.country_id' END
)
, concat_ws('
AND '
, '
WHERE TRUE'
, CASE WHEN $1 IS NOT NULL THEN 'a.ad_nr = $1' END
, CASE WHEN $2 IS NOT NULL THEN 'a.ad_nr_extra = $2' END
, CASE WHEN $3 IS NOT NULL THEN 'a.ad_info = $3' END
, CASE WHEN $4 IS NOT NULL THEN 'a.ad_postcode = $4' END
, CASE WHEN $5 IS NOT NULL THEN 's.name = $5' END
, CASE WHEN $6 IS NOT NULL THEN 'p.name = $6' END
, CASE WHEN $7 IS NOT NULL THEN 'c.name = $7' END
)
)
USING $1, $2, $3, $4, $5, $6, $7;
END
$func$;
कॉल करें:
SELECT * FROM func(1, '_ad_nr_extra', '_ad_info', '_ad_postcode', '_sname');
SELECT * FROM func(1, _pname := 'foo');
चूंकि सभी फ़ंक्शन पैरामीटर में डिफ़ॉल्ट मान होते हैं, आप स्थितीय . का उपयोग कर सकते हैं संकेतन, नामित संकेतन या मिश्रित फ़ंक्शन कॉल में आपके चयन पर संकेतन। देखें:
- इनपुट पैरामीटर की परिवर्तनीय संख्या के साथ कार्य
डायनेमिक SQL की मूल बातें के लिए अधिक स्पष्टीकरण:
- एक PL/pgSQL फ़ंक्शन को रिफ़ैक्टर करें ताकि विभिन्न SELECT क्वेरीज़ का आउटपुट लौटाया जा सके
concat()
स्ट्रिंग के निर्माण के लिए फ़ंक्शन महत्वपूर्ण है। इसे Postgres 9.1 के साथ पेश किया गया था।
ELSE
CASE
. की शाखा स्टेटमेंट डिफ़ॉल्ट रूप से NULL
. है जब मौजूद नहीं है। कोड को सरल करता है।
USING
EXECUTE
. के लिए क्लॉज SQL इंजेक्शन को असंभव बना देता है क्योंकि मानों को मानों . के रूप में पास किया जाता है और तैयार किए गए कथनों की तरह सीधे पैरामीटर मानों का उपयोग करने की अनुमति देता है।
NULL
यहां पैरामीटर को अनदेखा करने के लिए मानों का उपयोग किया जाता है। वे वास्तव में खोज करने के अभ्यस्त नहीं हैं।
आपको SELECT
. के आस-पास कोष्ठकों की आवश्यकता नहीं है RETURN QUERY
. के साथ ।
साधारण SQL फ़ंक्शन
आप कर सकते थे इसे एक सादे SQL फ़ंक्शन के साथ करें और गतिशील SQL से बचें। कुछ मामलों के लिए यह तेज़ हो सकता है। लेकिन मुझे इस मामले में इसकी उम्मीद नहीं थी . अनावश्यक जुड़ाव और विधेय के बिना क्वेरी की योजना बनाना आमतौर पर सर्वोत्तम परिणाम देता है। इस तरह की एक साधारण क्वेरी के लिए योजना लागत लगभग नगण्य है।
CREATE OR REPLACE FUNCTION func_sql(
_ad_nr int = NULL
, _ad_nr_extra text = NULL
, _ad_info text = NULL
, _ad_postcode text = NULL
, _sname text = NULL
, _pname text = NULL
, _cname text = NULL)
RETURNS TABLE(id int, match text, score int, nr int, nr_extra text
, info text, postcode text, street text, place text
, country text, the_geom geometry)
LANGUAGE sql AS
$func$
SELECT a.id, 'address' AS match, 1 AS score, a.ad_nr, a.ad_nr_extra
, a.ad_info, a.ad_postcode
, s.name AS street, p.name AS place
, c.name AS country, a.wkb_geometry
FROM "Addresses" a
LEFT JOIN "Streets" s ON s.id = a.street_id
LEFT JOIN "Places" p ON p.id = s.place_id
LEFT JOIN "Countries" c ON c.id = p.country_id
WHERE ($1 IS NULL OR a.ad_nr = $1)
AND ($2 IS NULL OR a.ad_nr_extra = $2)
AND ($3 IS NULL OR a.ad_info = $3)
AND ($4 IS NULL OR a.ad_postcode = $4)
AND ($5 IS NULL OR s.name = $5)
AND ($6 IS NULL OR p.name = $6)
AND ($7 IS NULL OR c.name = $7)
$func$;
समान कॉल।
प्रभावी ढंग से NULL
के साथ पैरामीटर को अनदेखा करने के लिए मान :
($1 IS NULL OR a.ad_nr = $1)
वास्तव में नल मानों को पैरामीटर के रूप में उपयोग करने के लिए , इसके बजाय इस निर्माण का उपयोग करें:
($1 IS NULL AND a.ad_nr IS NULL OR a.ad_nr = $1) -- AND binds before OR
यह अनुक्रमणिका . के लिए भी अनुमति देता है उपयोग करने के लिए।
मौजूद मामले के लिए, LEFT JOIN
के सभी उदाहरणों को बदलें JOIN
के साथ ।
db<>फिडल यहाँ - साधारण डेमो . के साथ सभी प्रकार के लिए।
पुराना sqlfiddle
एसाइड्स
-
name
का प्रयोग न करें औरid
कॉलम नामों के रूप में। वे वर्णनात्मक नहीं हैं और जब आप तालिकाओं के समूह में शामिल होते हैं (जैसे आपa lot
. करते हैं एक रिलेशनल डेटाबेस में), आपname
. नाम के कई कॉलम के साथ समाप्त होते हैं याid
, और गड़बड़ी को छांटने के लिए उपनाम संलग्न करना होगा। -
कृपया अपने SQL को ठीक से प्रारूपित करें, कम से कम सार्वजनिक प्रश्न पूछते समय। लेकिन इसे निजी तौर पर भी करें, अपने भले के लिए।