2016-01-13 10 views
15

लिखने योग्य सीटीई 9.5 से पहले Upsert के लिए एक समाधान Insert, on duplicate update in PostgreSQL?पोस्टग्रेएसक्यूएल 9.5+ यूपीएसईआरटी के साथ एक अपरेंट अपडेट होने पर कैसे पता लगाना है?

यह जानकारी है कि क्या यह एक अद्यतन या निम्न लिखने योग्य सीटीई मुहावरा के साथ एक सम्मिलित रूप में समाप्त हुआ के साथ एक Upsert प्रदर्शन करने के लिए संभव है में वर्णित के रूप में माना गया:

WITH 
    update_cte AS (
     UPDATE t SET v = $1 WHERE id = $2 RETURNING 'updated'::text status 
    ), 
    insert_cte AS (
     INSERT INTO t(id, v) SELECT $2, $1 WHERE NOT EXISTS 
      (SELECT 1 FROM update_cte) RETURNING 'inserted'::text status 
    ) 
(SELECT status FROM update_cte) UNION (SELECT status FROM insert_cte) 

इस क्वेरी होगा लौटने या तो "अद्यतन" या "डाला", या (कभी-कभी) की कोई समस्या उल्लंघन के रूप में https://dba.stackexchange.com/questions/78510/why-is-cte-open-to-lost-updates

में वर्णित में कुछ इसी तरह PostgreSQL का प्रयोग कर प्राप्त किया जा सकता है 9.5+ नया "Upsert" के साथ विफल हो सकता है वाक्यविन्यास, लाभकारी अपने अनुकूलन से टिंग और संभावित बाधा उल्लंघन से परहेज?

उत्तर

6

@lad2025's answer से रेखाचित्र, परिणाम प्राप्त किया जा सकता settings और customized options वाई का दुरुपयोग करके वें related functions जहां एक आवश्यक साइड-इफेक्ट प्राप्त करने के लिए कहता है।

CREATE TABLE t(id INT PRIMARY KEY, v TEXT); 

INSERT INTO t (id, v) 
    SELECT $1, $2 
    WHERE 'inserted' = set_config('upsert.action', 'inserted', true) 
    ON CONFLICT (id) DO UPDATE 
     SET v = EXCLUDED.v 
     WHERE 'updated' = set_config('upsert.action', 'updated', true) 
RETURNING current_setting('upsert.action') AS "upsert.action"; 

set_config के तीसरे पैरामीटर is_local है: true का मतलब सेटिंग लेनदेन के अंत में बंद हो जाएगा। अधिक सटीक, current_setting('upsert.action') सत्र के अंत तक न्यूल (और त्रुटि नहीं फेंक देगा) वापस कर देगा।

+0

अच्छी चाल **** – lad2025

+5

ओह, यह बदसूरत है! यह मेरी दृष्टि धुंधला बनाता है। मैं निश्चित रूप से इसे याद नहीं करना चाहता हूं, इसे अकेले लागू करने दें! ~ – Patrick

+0

आप इसे 'xmax :: text :: int> 0' के साथ कर सकते हैं - बिना लेनदेन स्तर सेटिंग (जो वास्तव में मूल हैक) –

4

SQL ServerMERGE कथन में $action है जो स्ट्रिंग 'INSERT', 'UPDATE', or 'DELETE' देता है।

Postgresql के लिए मुझे फ़ंक्शन/वेरिएबल नहीं मिल रहा है जो RETURNING के लिए समान चीज़ करता है।

एक तरह से यह वैकल्पिक हल करने के लिए अपने तालिका में कॉलम is_updated जोड़ने के लिए है:

DROP TABLE IF EXISTS tab; 

CREATE TABLE tab(id INT PRIMARY KEY, col VARCHAR(100), 
       is_updated BOOLEAN DEFAULT false); 
INSERT INTO tab(id, col) VALUES (1,'a'), (2, 'b'); 


-- main query 
INSERT INTO tab(id, col) 
VALUES (3, 'c'), (4, 'd'), (1,'aaaa') 
ON CONFLICT (id) DO UPDATE SET col = EXCLUDED.col, is_updated = true 
RETURNING id,col, 
      CASE WHEN is_updated THEN 'UPDATED' ELSE 'INSERTED' END AS action; 

Rextester Demo

आउटपुट:

╔════╦══════╦══════════╗ 
║ id ║ col ║ action ║ 
╠════╬══════╬══════════╣ 
║ 3 ║ c ║ INSERTED ║ 
║ 4 ║ d ║ INSERTED ║ 
║ 1 ║ aaaa ║ UPDATED ║ 
╚════╩══════╩══════════╝ 
+0

जहां मूल्यों है (2, 'बी') उत्पादन में? – kometen

+0

@kometen इसके लिए कोई आवश्यकता नहीं है। यह पहले मौजूद है। आप पूरी टेबल क्यों वापस करना चाहते हैं? केवल रिकॉर्ड जो – lad2025

+0

डाले गए/अपडेट किए गए हैं लेकिन (2, 'बी') भी डाला गया है। और गलत_शुस्त डिफ़ॉल्ट है। तो मैंने सोचा कि यह शामिल के रूप में दिखाई देगा। – kometen

1

मेरा मानना ​​है कि xmax::text::int > 0 सबसे आसान चाल होगा:

so=# DROP TABLE IF EXISTS tab; 
NOTICE: table "tab" does not exist, skipping 
DROP TABLE 
so=# CREATE TABLE tab(id INT PRIMARY KEY, col text); 
CREATE TABLE 
so=# INSERT INTO tab(id, col) VALUES (1,'a'), (2, 'b'); 
INSERT 0 2 
so=# INSERT INTO tab(id, col) 
VALUES (3, 'c'), (4, 'd'), (1,'aaaa') 
ON CONFLICT (id) DO UPDATE SET col = EXCLUDED.col 
returning *,case when xmax::text::int > 0 then 'updated' else 'inserted' end,ctid; 
id | col | case | ctid 
----+------+----------+------- 
    3 | c | inserted | (0,3) 
    4 | d | inserted | (0,4) 
    1 | aaaa | updated | (0,5) 
(3 rows) 

INSERT 0 3 
so=# INSERT INTO tab(id, col) 
VALUES (3, 'c'), (4, 'd'), (1,'aaaa') 
ON CONFLICT (id) DO UPDATE SET col = EXCLUDED.col 
returning *,case when xmax::text::int > 0 then 'updated' else 'inserted' end,ctid; 
id | col | case | ctid 
----+------+---------+------- 
    3 | c | updated | (0,6) 
    4 | d | updated | (0,7) 
    1 | aaaa | updated | (0,8) 
(3 rows) 

INSERT 0 3 
+0

आप इसका उपयोग कर सकते हैं: '' रिटर्निंग (xmax = 0) AS '' ' –

संबंधित मुद्दे