क्यों करता है रोवन की हैक काम (ज्यादातर)?
SELECT id, title
, CASE WHEN extra_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN (
SELECT EXISTS (
SELECT FROM information_schema.columns
WHERE table_name = 'tbl'
AND column_name = 'extra')
) AS extra(extra_exists)
सामान्य तौर पर, यह बिल्कुल भी काम नहीं करेगा। पोस्टग्रेज़ SQL कथन को पार्स करता है और एक अपवाद फेंकता है यदि कोई शामिल स्तंभों में से मौजूद नहीं है।
चाल एक तालिका नाम (या उपनाम) को उसी नाम से पेश करना है जो प्रश्न में कॉलम नाम के रूप में है। extra
इस मामले में। प्रत्येक तालिका नाम को समग्र रूप से संदर्भित किया जा सकता है, जिसके परिणामस्वरूप पूरी पंक्ति को record
प्रकार के रूप में वापस किया जा सकता है . और चूंकि हर प्रकार को text
. में डाला जा सकता है , हम इस पूरे रिकॉर्ड को text
. में डाल सकते हैं . इस तरह, Postgres क्वेरी को मान्य मान लेता है।
चूंकि स्तंभ नामों को तालिका नामों पर प्राथमिकता दी जाती है, extra::text
कॉलम के रूप में व्याख्या की जाती है tbl.extra
यदि कॉलम मौजूद है। अन्यथा, यह तालिका की पूरी पंक्ति extra
को वापस करने के लिए डिफ़ॉल्ट होगा - जो कभी नहीं होता।
extra
. के लिए कोई भिन्न तालिका उपनाम चुनने का प्रयास करें खुद देखने के लिए।
यह एक गैर-दस्तावेज हैक है और टूट सकता है अगर पोस्टग्रेज भविष्य के संस्करणों में एसक्यूएल स्ट्रिंग्स को पार्स किए जाने के तरीके को बदलने का फैसला करता है - भले ही यह असंभव लगता है।
स्पष्ट
यदि आप इसका उपयोग करने का निर्णय लेते हैं, कम से कम इसे स्पष्ट करें ।
अकेले एक टेबल नाम अद्वितीय नहीं है। "tbl" नाम की एक तालिका एक ही डेटाबेस के कई स्कीमा में कई बार मौजूद हो सकती है, जिससे बहुत भ्रमित और पूरी तरह से गलत परिणाम हो सकते हैं। आपको जरूरत अतिरिक्त रूप से स्कीमा नाम प्रदान करने के लिए:
SELECT id, title
, CASE WHEN col_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN (
SELECT EXISTS (
SELECT FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'tbl'
AND column_name = 'extra'
) AS col_exists
) extra;
तेज़
चूंकि यह प्रश्न अन्य आरडीबीएमएस के लिए शायद ही पोर्टेबल है, मैं का उपयोग करने का सुझाव देता हूं। सूची तालिका pg_attribute
जानकारी स्कीमा के बजाय information_schema.columns
. देखें . लगभग 10 गुना तेज।
SELECT id, title
, CASE WHEN col_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN (
SELECT EXISTS (
SELECT FROM pg_catalog.pg_attribute
WHERE attrelid = 'myschema.tbl'::regclass -- schema-qualified!
AND attname = 'extra'
AND NOT attisdropped -- no dropped (dead) columns
AND attnum > 0 -- no system columns
)
) extra(col_exists);
साथ ही regclass
के लिए अधिक सुविधाजनक और सुरक्षित कास्ट का उपयोग करना . देखें:
आप Postgres को किसी . को मूर्ख बनाने के लिए आवश्यक उपनाम संलग्न कर सकते हैं तालिका, प्राथमिक तालिका सहित। आपको किसी अन्य संबंध में शामिल होने की बिल्कुल भी आवश्यकता नहीं है, जो सबसे तेज़ होना चाहिए:
SELECT id, title
, CASE WHEN EXISTS (SELECT FROM pg_catalog.pg_attribute
WHERE attrelid = 'tbl'::regclass
AND attname = 'extra'
AND NOT attisdropped
AND attnum > 0)
THEN extra::text
ELSE 'default' END AS extra
FROM tbl AS extra;
सुविधा
You could encapsulate the test for existence in a simple SQL function (once), arriving (almost) at the function you have been asking for:
CREATE OR REPLACE FUNCTION col_exists(_tbl regclass, _col text)
RETURNS bool
LANGUAGE sql STABLE AS
$func$
SELECT EXISTS (
SELECT FROM pg_catalog.pg_attribute
WHERE attrelid = $1
AND attname = $2
AND NOT attisdropped
AND attnum > 0
)
$func$;
COMMENT ON FUNCTION col_exists(regclass, text) IS
'Test for existence of a column. Returns TRUE / FALSE.
$1 .. exact table name (case sensitive!), optionally schema-qualified
$2 .. exact column name (case sensitive!)';
क्वेरी को सरल करता है:
SELECT id, title
, CASE WHEN col_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN col_exists('tbl', 'extra') AS extra(col_exists);
यहां अतिरिक्त संबंध के साथ फ़ॉर्म का उपयोग करना, क्योंकि यह फ़ंक्शन के साथ तेज़ हो गया है।
फिर भी, आपको केवल पाठ्य प्रस्तुति . मिलता है इनमें से किसी भी प्रश्न के साथ कॉलम का। वास्तविक प्रकार . प्राप्त करना इतना आसान नहीं है ।
बेंचमार्क
मैंने इन्हें सबसे तेज़ खोजने के लिए पृष्ठ 9.1 और 9.2 पर 100k पंक्तियों के साथ एक त्वरित बेंचमार्क चलाया:
सबसे तेज़:
SELECT id, title
, CASE WHEN EXISTS (SELECT FROM pg_catalog.pg_attribute
WHERE attrelid = 'tbl'::regclass
AND attname = 'extra'
AND NOT attisdropped
AND attnum > 0)
THEN extra::text
ELSE 'default' END AS extra
FROM tbl AS extra;
दूसरा सबसे तेज़:
SELECT id, title
, CASE WHEN col_exists THEN extra::text ELSE 'default' END AS extra
FROM tbl
CROSS JOIN col_exists('tbl', 'extra') AS extra(col_exists);
db<>fiddle यहां
पुराना sqlfiddle
उप>