PostgreSQL
 sql >> डेटाबेस >  >> RDS >> PostgreSQL

PostgreSQL 9.3 . का उपयोग करके CTE UPSERT में DEFAULT मान उत्पन्न करें

पोस्टग्रेज 9.5 कार्यान्वित UPSERT . नीचे देखें।

9.4 या पुराने को पोस्ट करें

यह एक विकट समस्या है। आप इस प्रतिबंध (प्रति दस्तावेज़) में चल रहे हैं:

<ब्लॉकक्वॉट>

एक VALUES . में INSERT . के शीर्ष स्तर पर प्रदर्शित होने वाली सूची , एक अभिव्यक्ति को DEFAULT . द्वारा प्रतिस्थापित किया जा सकता है यह इंगित करने के लिए कि गंतव्य स्तंभ का डिफ़ॉल्ट मान डाला जाना चाहिए। DEFAULT VALUES . होने पर उपयोग नहीं किया जा सकता अन्य संदर्भों में प्रकट होता है।

बोल्ड जोर मेरा। सम्मिलित करने के लिए तालिका के बिना डिफ़ॉल्ट मानों को परिभाषित नहीं किया जाता है। तो कोई प्रत्यक्ष नहीं है आपके प्रश्न का समाधान है, लेकिन सटीक आवश्यकताओं के आधार पर कई संभावित वैकल्पिक मार्ग हैं

सिस्टम कैटलॉग से डिफ़ॉल्ट मान प्राप्त करें?

आप कर सकते थे सिस्टम कैटलॉग से उन्हें प्राप्त करें pg_attrdef जैसे @Patrick ने टिप्पणी की या information_schema.columns . से . यहां पूर्ण निर्देश:

  • पोस्टग्रेज में टेबल कॉलम के डिफ़ॉल्ट मान प्राप्त करें?

लेकिन फिर भी आप अभी भी केवल पंक्तियों . की एक सूची है डिफ़ॉल्ट मान को पकाने के लिए अभिव्यक्ति के पाठ प्रतिनिधित्व के साथ। मूल्यों के साथ काम करने के लिए आपको गतिशील रूप से बयान बनाना और निष्पादित करना होगा। थकाऊ और गन्दा। इसके बजाय, हम अंतर्निहित Postgres कार्यक्षमता को हमारे लिए ऐसा करने दे सकते हैं :

साधारण शॉर्टकट

एक डमी पंक्ति डालें और इसे उत्पन्न डिफ़ॉल्ट का उपयोग करने के लिए वापस कर दें:

INSERT INTO playlist_items DEFAULT VALUES RETURNING *;

समस्याएं / समाधान का दायरा

  • यह केवल STABLE . के लिए काम करने की गारंटी है या IMMUTABLE डिफ़ॉल्ट भाव . अधिकांश VOLATILE कार्य ठीक वैसे ही काम करेंगे, लेकिन इसकी कोई गारंटी नहीं है। current_timestamp कार्यों का परिवार स्थिर के रूप में योग्य होता है, क्योंकि उनके मूल्य लेन-देन में नहीं बदलते हैं।
    विशेष रूप से, इसका serial पर दुष्प्रभाव पड़ता है। कॉलम (या अनुक्रम से कोई अन्य डिफ़ॉल्ट आरेखण)। लेकिन यह कोई समस्या नहीं होनी चाहिए, क्योंकि आप आमतौर पर serial . पर नहीं लिखते हैं सीधे कॉलम। उन्हें INSERT . में सूचीबद्ध नहीं किया जाना चाहिए कथन बिल्कुल।
    serial . के लिए शेष दोष कॉलम:क्रमांकन में एक अंतर पैदा करते हुए, डिफ़ॉल्ट पंक्ति प्राप्त करने के लिए अनुक्रम अभी भी एकल कॉल द्वारा उन्नत है। फिर, यह कोई समस्या नहीं होनी चाहिए, क्योंकि अंतराल आम तौर पर अपेक्षित होते हैं serial . में कॉलम।

दो और समस्याओं का समाधान किया जा सकता है:

  • यदि आपके पास परिभाषित कॉलम हैं NOT NULL , आपको डमी मान डालने होंगे और NULL . से प्रतिस्थापित करना होगा परिणाम में।

  • हम वास्तव में डमी पंक्ति को सम्मिलित नहीं करना चाहते हैं . हम बाद में (उसी लेन-देन में) हटा सकते हैं, लेकिन इसके अधिक दुष्प्रभाव हो सकते हैं, जैसे ट्रिगर ON DELETE . एक बेहतर तरीका है:

डमी पंक्ति से बचें

एक अस्थायी तालिका का क्लोन बनाएं कॉलम डिफ़ॉल्ट सहित और कि . में डालें :

कमिट ड्रॉप पर
BEGIN;
CREATE TEMP TABLE tmp_playlist_items (LIKE playlist_items INCLUDING DEFAULTS)
   ON COMMIT DROP;  -- drop at end of transaction

INSERT INTO tmp_playlist_items DEFAULT VALUES RETURNING *;
...

वही परिणाम, कम दुष्प्रभाव। चूंकि डिफ़ॉल्ट अभिव्यक्तियों को शब्दशः कॉपी किया जाता है, क्लोन उसी क्रम से खींचा जाता है यदि कोई हो। लेकिन अवांछित पंक्ति या ट्रिगर से होने वाले अन्य दुष्प्रभावों से पूरी तरह बचा जाता है।

इस विचार के लिए इगोर को श्रेय:

  • Postgresql, एक "नकली" पंक्ति चुनें

निकालें NOT NULL बाधाएं

आपको NOT NULL . के लिए डमी मान प्रदान करने होंगे कॉलम, क्योंकि (प्रति दस्तावेज़):

<ब्लॉकक्वॉट>

नॉट-नल बाधाओं को हमेशा नई तालिका में कॉपी किया जाता है।

या तो उन लोगों के लिए समायोजित करें जो INSERT . में हैं बयान या (बेहतर) बाधाओं को खत्म करें:

ALTER TABLE tmp_playlist_items
   ALTER COLUMN foo DROP NOT NULL
 , ALTER COLUMN bar DROP NOT NULL;

एक त्वरित और गंदा तरीका है सुपरयुसर विशेषाधिकारों के साथ:

UPDATE pg_attribute
SET    attnotnull = FALSE
WHERE  attrelid = 'tmp_playlist_items'::regclass
AND    attnotnull
AND    attnum > 0;

यह केवल एक अस्थायी तालिका है जिसमें कोई डेटा नहीं है और कोई अन्य उद्देश्य नहीं है, और इसे लेनदेन के अंत में छोड़ दिया जाता है। तो शॉर्टकट आकर्षक है। फिर भी, मूल नियम यह है:कभी भी सीधे सिस्टम कैटलॉग से छेड़छाड़ न करें।

तो, आइए एक साफ तरीके पर नजर डालते हैं :डायनेमिक SQL के साथ DO . में स्वचालित करें बयान। आपको बस नियमित विशेषाधिकारों . की आवश्यकता है आपको गारंटी है कि चूंकि उसी भूमिका ने अस्थायी तालिका बनाई है।

DO $$BEGIN
EXECUTE (
   SELECT 'ALTER TABLE tmp_playlist_items ALTER '
       || string_agg(quote_ident(attname), ' DROP NOT NULL, ALTER ')
       || ' DROP NOT NULL'
   FROM   pg_catalog.pg_attribute
   WHERE  attrelid = 'tmp_playlist_items'::regclass
   AND    attnotnull
   AND    attnum > 0
   );
END$$

बहुत साफ और अभी भी बहुत तेज। डायनेमिक कमांड के साथ देखभाल करें और SQL इंजेक्शन से सावधान रहें। यह कथन सुरक्षित है। मैंने अधिक स्पष्टीकरण के साथ कई संबंधित उत्तर पोस्ट किए हैं।

सामान्य समाधान (9.4 और पुराने)

BEGIN;

CREATE TEMP TABLE tmp_playlist_items
   (LIKE playlist_items INCLUDING DEFAULTS) ON COMMIT DROP;

DO $$BEGIN
EXECUTE (
   SELECT 'ALTER TABLE tmp_playlist_items ALTER '
       || string_agg(quote_ident(attname), ' DROP NOT NULL, ALTER ')
       || ' DROP NOT NULL'
   FROM   pg_catalog.pg_attribute
   WHERE  attrelid = 'tmp_playlist_items'::regclass
   AND    attnotnull
   AND    attnum > 0
   );
END$$;

LOCK TABLE playlist_items IN EXCLUSIVE MODE;  -- forbid concurrent writes

WITH default_row AS (
   INSERT INTO tmp_playlist_items DEFAULT VALUES RETURNING *
   )
, new_values (id, playlist, item, group_name, duration, sort, legacy) AS (
   VALUES
      (651, 21, 30012, 'a', 30, 1, FALSE)
    , (NULL, 21, 1, 'b', 34, 2, NULL)
    , (668, 21, 30012, 'c', 30, 3, FALSE)
    , (7428, 21, 23068, 'd', 0, 4, FALSE)
   )
, upsert AS (  -- *not* replacing existing values in UPDATE (?)
   UPDATE playlist_items m
   SET   (  playlist,   item,   group_name,   duration,   sort,   legacy)
       = (n.playlist, n.item, n.group_name, n.duration, n.sort, n.legacy)
   --                                   ..., COALESCE(n.legacy, m.legacy)  -- see below
   FROM   new_values n
   WHERE  n.id = m.id
   RETURNING m.id
   )
INSERT INTO playlist_items
        (playlist,   item,   group_name,   duration,   sort, legacy)
SELECT n.playlist, n.item, n.group_name, n.duration, n.sort
                                   , COALESCE(n.legacy, d.legacy)
FROM   new_values n, default_row d   -- single row can be cross-joined
WHERE  NOT EXISTS (SELECT 1 FROM upsert u WHERE u.id = n.id)
RETURNING id;

COMMIT;

आपको केवल LOCK . की आवश्यकता है यदि आपके पास समवर्ती लेन-देन एक ही तालिका में लिखने का प्रयास कर रहे हैं।

अनुरोध के अनुसार, यह केवल legacy . कॉलम में NULL मानों को प्रतिस्थापित करता है INSERT . के लिए इनपुट पंक्तियों में मामला। अन्य स्तंभों के लिए या UPDATE . में काम करने के लिए आसानी से बढ़ाया जा सकता है मामला भी। उदाहरण के लिए, आप UPDATE . कर सकते हैं सशर्त रूप से भी:केवल अगर इनपुट मान NOT NULL . मैंने UPDATE में एक टिप्पणी पंक्ति जोड़ी है ऊपर।

इसके अलावा:आपको कास्ट करने . की आवश्यकता नहीं है किसी भी पंक्ति में मान लेकिन VALUES . में सबसे पहले अभिव्यक्ति, चूंकि प्रकार पहले . से व्युत्पन्न होते हैं पंक्ति।

पोस्टग्रेज 9.5

यूपीएसईआरटी लागू करता है INSERT .. ON CONFLICT .. DO NOTHING | UPDATE . यह काफी हद तक ऑपरेशन को सरल करता है:

INSERT INTO playlist_items AS m (id, playlist, item, group_name, duration, sort, legacy)
VALUES (651, 21, 30012, 'a', 30, 1, FALSE)
,      (DEFAULT, 21, 1, 'b', 34, 2, DEFAULT)  -- !
,      (668, 21, 30012, 'c', 30, 3, FALSE)
,      (7428, 21, 23068, 'd', 0, 4, FALSE)
ON CONFLICT (id) DO UPDATE
SET (playlist, item, group_name, duration, sort, legacy)
 = (EXCLUDED.playlist, EXCLUDED.item, EXCLUDED.group_name
  , EXCLUDED.duration, EXCLUDED.sort, EXCLUDED.legacy)
-- (...,  COALESCE(l.legacy, EXCLUDED.legacy))  -- see below
RETURNING m.id;

हम VALUES संलग्न कर सकते हैं INSERT . का खंड सीधे, जो DEFAULT . की अनुमति देता है खोजशब्द। (id) . पर अद्वितीय उल्लंघनों के मामले में , इसके बजाय अपडेट पोस्ट करता है। हम UPDATE . में अपवर्जित पंक्तियों का उपयोग कर सकते हैं . मैनुअल:

<ब्लॉकक्वॉट>

SET और WHERE ON CONFLICT DO UPDATE में क्लॉज तालिका के नाम (या उपनाम) का उपयोग करके मौजूदा पंक्ति तक पहुंच प्राप्त करें, और विशेष excluded का उपयोग करके प्रविष्टि के लिए प्रस्तावित पंक्तियों तक पहुंच प्राप्त करें टेबल।

और:

<ब्लॉकक्वॉट>

ध्यान दें कि सभी प्रति-पंक्ति के प्रभाव BEFORE INSERT ट्रिगर बहिष्कृत मानों में प्रतिबिंबित होते हैं, क्योंकि उन प्रभावों ने पंक्ति को सम्मिलन से बाहर किए जाने में योगदान दिया हो सकता है।

शेष कोने का मामला

आपके पास UPDATE . के लिए कई विकल्प हैं :आप कर सकते हैं...

  • ... बिल्कुल भी अपडेट नहीं:एक WHEREजोड़ें UPDATE का खंड केवल चयनित पंक्तियों में लिखने के लिए।
  • ... केवल चयनित कॉलम अपडेट करें।
  • ... केवल तभी अपडेट करें जब कॉलम वर्तमान में खाली हो:COALESCE(l.legacy, EXCLUDED.legacy)
  • ... केवल तभी अपडेट करें जब नया मान NOT NULL हो :COALESCE(EXCLUDED.legacy, l.legacy)

लेकिन DEFAULT को समझने का कोई तरीका नहीं है वास्तव में INSERT . में प्रदान किए गए मान और मान . केवल परिणामी excluded पंक्तियाँ दिखाई देती हैं। यदि आपको अंतर की आवश्यकता है, तो पिछले समाधान पर वापस आएं, जहां आपके पास हमारे निपटान में दोनों हैं।




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. sqlalchemy.exc.NoSuchModuleError:प्लगइन लोड नहीं कर सकता:sqlalchemy.dialects:postgres

  2. [वीडियो] PostgreSQL में अनुक्रमण की शक्ति

  3. त्रुटि:डोकर में अल्पाइन पर psycopg2 स्थापित करते समय pg_config निष्पादन योग्य नहीं मिला

  4. सी # विंडोज मोबाइल 6.5 ऐप को पोस्टग्रेस डेटाबेस से कैसे कनेक्ट करें?

  5. विधि org.postgresql.jdbc4.Jdbc4Connection.isValid(int) अभी तक लागू नहीं किया गया है