पोस्टग्रेज मानते हुए 9.1 या बाद में।
मैंने नवीनतम मूल्यों को पुनः प्राप्त करने के लिए आपकी मूल क्वेरी को सरल/अनुकूलित किया है:
SELECT DISTINCT ON (1,2)
c.unique_id, a.attname AS col, c.value
FROM pg_attribute a
LEFT JOIN changes c ON c.column_name = a.attname
AND c.table_name = 'instances'
-- AND c.unique_id = 3 -- uncomment to fetch single row
WHERE a.attrelid = 'instances'::regclass -- schema-qualify to be clear?
AND a.attnum > 0 -- no system columns
AND NOT a.attisdropped -- no deleted columns
ORDER BY 1, 2, c.updated_at DESC;
मैं मानक सूचना स्कीमा के बजाय PostgreSQL कैटलॉग को क्वेरी करता हूं क्योंकि यह तेज़ है। विशेष कलाकारों को ::regclass
. पर ध्यान दें ।
अब, यह आपको एक टेबल . देता है . आप एक unique_id
. के लिए सभी मान चाहते हैं एक पंक्ति . में .
यह हासिल करने के लिए आपके पास मूल रूप से तीन विकल्प हैं:
-
प्रति कॉलम एक उप-चयन (या शामिल हों)। महंगा और बोझिल। लेकिन केवल कुछ स्तंभों के लिए एक मान्य विकल्प।
-
एक बड़ा
CASE
बयान। -
एक पिवट फ़ंक्शन . PostgreSQL
crosstab()
प्रदान करता है अतिरिक्त मॉड्यूल में कार्य करेंtablefunc
उसके लिए।
बुनियादी निर्देश:- PostgreSQL क्रॉसस्टैब क्वेरी
crosstab()
के साथ मूल पिवट टेबल
मैंने फ़ंक्शन को पूरी तरह से फिर से लिखा:
SELECT *
FROM crosstab(
$x$
SELECT DISTINCT ON (1, 2)
unique_id, column_name, value
FROM changes
WHERE table_name = 'instances'
-- AND unique_id = 3 -- un-comment to fetch single row
ORDER BY 1, 2, updated_at DESC;
$x$,
$y$
SELECT attname
FROM pg_catalog.pg_attribute
WHERE attrelid = 'instances'::regclass -- possibly schema-qualify table name
AND attnum > 0
AND NOT attisdropped
AND attname <> 'unique_id'
ORDER BY attnum
$y$
)
AS tbl (
unique_id integer
-- !!! You have to list all columns in order here !!! --
);
मैंने कैटलॉग लुकअप को वैल्यू क्वेरी से crosstab()
. के रूप में अलग किया है दो पैरामीटर के साथ फ़ंक्शन अलग-अलग कॉलम नाम प्रदान करता है। अनुपलब्ध मान (परिवर्तनों में कोई प्रविष्टि नहीं) को NULL
. से प्रतिस्थापित किया जाता है खुद ब खुद। इस उपयोग के मामले के लिए एकदम सही मिलान!
मान लें कि attname
column_name
. से मेल खाता है . unique_id
. को छोड़कर , जो एक विशेष भूमिका निभाता है।
पूर्ण स्वचालन
अपनी टिप्पणी को संबोधित करते हुए:एक तरीका है कॉलम परिभाषा सूची स्वचालित रूप से आपूर्ति करने के लिए। हालांकि यह बेहोश दिल के लिए नहीं है।
मैं यहां कई उन्नत पोस्टग्रेज सुविधाओं का उपयोग करता हूं:crosstab()
, गतिशील SQL के साथ plpgsql फ़ंक्शन, समग्र प्रकार की हैंडलिंग, उन्नत डॉलर उद्धरण, कैटलॉग लुकअप, कुल फ़ंक्शन, विंडो फ़ंक्शन, ऑब्जेक्ट पहचानकर्ता प्रकार, ...
परीक्षण वातावरण:
CREATE TABLE instances (
unique_id int
, col1 text
, col2 text -- two columns are enough for the demo
);
INSERT INTO instances VALUES
(1, 'foo1', 'bar1')
, (2, 'foo2', 'bar2')
, (3, 'foo3', 'bar3')
, (4, 'foo4', 'bar4');
CREATE TABLE changes (
unique_id int
, table_name text
, column_name text
, value text
, updated_at timestamp
);
INSERT INTO changes VALUES
(1, 'instances', 'col1', 'foo11', '2012-04-12 00:01')
, (1, 'instances', 'col1', 'foo12', '2012-04-12 00:02')
, (1, 'instances', 'col1', 'foo1x', '2012-04-12 00:03')
, (1, 'instances', 'col2', 'bar11', '2012-04-12 00:11')
, (1, 'instances', 'col2', 'bar17', '2012-04-12 00:12')
, (1, 'instances', 'col2', 'bar1x', '2012-04-12 00:13')
, (2, 'instances', 'col1', 'foo2x', '2012-04-12 00:01')
, (2, 'instances', 'col2', 'bar2x', '2012-04-12 00:13')
-- NO change for col1 of row 3 - to test NULLs
, (3, 'instances', 'col2', 'bar3x', '2012-04-12 00:13');
-- NO changes at all for row 4 - to test NULLs
एक टेबल के लिए स्वचालित फ़ंक्शन
CREATE OR REPLACE FUNCTION f_curr_instance(int, OUT t public.instances) AS
$func$
BEGIN
EXECUTE $f$
SELECT *
FROM crosstab($x$
SELECT DISTINCT ON (1,2)
unique_id, column_name, value
FROM changes
WHERE table_name = 'instances'
AND unique_id = $f$ || $1 || $f$
ORDER BY 1, 2, updated_at DESC;
$x$
, $y$
SELECT attname
FROM pg_catalog.pg_attribute
WHERE attrelid = 'public.instances'::regclass
AND attnum > 0
AND NOT attisdropped
AND attname <> 'unique_id'
ORDER BY attnum
$y$) AS tbl ($f$
|| (SELECT string_agg(attname || ' ' || atttypid::regtype::text
, ', ' ORDER BY attnum) -- must be in order
FROM pg_catalog.pg_attribute
WHERE attrelid = 'public.instances'::regclass
AND attnum > 0
AND NOT attisdropped)
|| ')'
INTO t;
END
$func$ LANGUAGE plpgsql;
तालिका instances
हार्ड-कोडेड है, स्कीमा स्पष्ट होने के योग्य है। तालिका प्रकार के उपयोग को वापसी प्रकार के रूप में नोट करें। PostgreSQL में प्रत्येक तालिका के लिए स्वचालित रूप से पंजीकृत एक पंक्ति प्रकार है। यह crosstab()
. के रिटर्न प्रकार से मेल खाने के लिए बाध्य है समारोह।
यह फ़ंक्शन को तालिका के प्रकार से बांधता है:
- यदि आप
DROP
का प्रयास करते हैं तो आपको एक त्रुटि संदेश प्राप्त होगा टेबल - आपका फ़ंक्शन
ALTER TABLE
. के बाद विफल हो जाएगा . आपको इसे फिर से बनाना होगा (बिना बदलाव के)। मैं इसे 9.1 में एक बग मानता हूं।ALTER TABLE
फ़ंक्शन को चुपचाप नहीं तोड़ना चाहिए, लेकिन एक त्रुटि उत्पन्न करनी चाहिए।
यह बहुत अच्छा प्रदर्शन करता है।
कॉल करें:
SELECT * FROM f_curr_instance(3);
unique_id | col1 | col2
----------+-------+-----
3 |<NULL> | bar3x
ध्यान दें कि कैसे col1
है NULL
यहां।
किसी उदाहरण को उसके नवीनतम मानों के साथ प्रदर्शित करने के लिए क्वेरी में उपयोग करें:
SELECT i.unique_id
, COALESCE(c.col1, i.col1)
, COALESCE(c.col2, i.col2)
FROM instances i
LEFT JOIN f_curr_instance(3) c USING (unique_id)
WHERE i.unique_id = 3;
किसी भी तालिका के लिए पूर्ण स्वचालन
(2016 को जोड़ा गया। यह डायनामाइट है।)
पोस्टग्रेज की आवश्यकता है 9.1 या बाद में। (पृष्ठ 8.4 के साथ काम करने के लिए तैयार किया जा सकता है, लेकिन मैंने बैकपैच करने की जहमत नहीं उठाई।)
CREATE OR REPLACE FUNCTION f_curr_instance(_id int, INOUT _t ANYELEMENT) AS
$func$
DECLARE
_type text := pg_typeof(_t);
BEGIN
EXECUTE
(
SELECT format
($f$
SELECT *
FROM crosstab(
$x$
SELECT DISTINCT ON (1,2)
unique_id, column_name, value
FROM changes
WHERE table_name = %1$L
AND unique_id = %2$s
ORDER BY 1, 2, updated_at DESC;
$x$
, $y$
SELECT attname
FROM pg_catalog.pg_attribute
WHERE attrelid = %1$L::regclass
AND attnum > 0
AND NOT attisdropped
AND attname <> 'unique_id'
ORDER BY attnum
$y$) AS ct (%3$s)
$f$
, _type, _id
, string_agg(attname || ' ' || atttypid::regtype::text
, ', ' ORDER BY attnum) -- must be in order
)
FROM pg_catalog.pg_attribute
WHERE attrelid = _type::regclass
AND attnum > 0
AND NOT attisdropped
)
INTO _t;
END
$func$ LANGUAGE plpgsql;
कॉल करें (तालिका प्रकार प्रदान करते हुए NULL::public.instances
:
SELECT * FROM f_curr_instance(3, NULL::public.instances);
संबंधित:
- एक PL/pgSQL फ़ंक्शन को रिफ़ैक्टर करें ताकि विभिन्न SELECT क्वेरीज़ का आउटपुट लौटाया जा सके
- डायनेमिक SQL का उपयोग करके कंपोजिट वैरिएबल फ़ील्ड का मान कैसे सेट करें