পোস্টগ্রিএসকিউএল এ ইউপিএসআরটি বাস্তবায়নের আইডোমেটিক উপায়


40

আমি UPSERTপোস্টগ্রেএসকিউএলে বিভিন্ন বাস্তবায়ন সম্পর্কে পড়েছি , তবে এই সমস্ত সমাধানগুলি তুলনামূলকভাবে পুরানো বা তুলনামূলকভাবে বহিরাগত ( উদাহরণস্বরূপ লিখিতযোগ্য সিটিই ব্যবহার করে )।

এবং তাত্ক্ষণিকভাবে অনুসন্ধান করার জন্য আমি ঠিক কোনও পিএসকিএল বিশেষজ্ঞ নই, এই সমাধানগুলি পুরানো কিনা সেগুলি সুপারিশ করা হয় বা তারা (ভাল, প্রায় সবগুলিই) কেবল খেলনা উদাহরণ উত্পাদন ব্যবহারের জন্য উপযুক্ত নয়।

পোস্টগ্রিজ এসকিউএল-এ ইউপিএসআরটি বাস্তবায়নের সর্বাধিক থ্রেড-নিরাপদ উপায় কী?

উত্তর:


23

PostgreSQL এর এখন ইউপিএসআরটি রয়েছে


অনুরূপ স্ট্যাকওভারফ্লো প্রশ্ন অনুসারে পছন্দের পদ্ধতিটি বর্তমানে নিম্নলিখিত:

CREATE TABLE db (a INT PRIMARY KEY, b TEXT);

CREATE FUNCTION merge_db(key INT, data TEXT) RETURNS VOID AS
$$
BEGIN
    LOOP
        -- first try to update the key
        UPDATE db SET b = data WHERE a = key;
        IF found THEN
            RETURN;
        END IF;
        -- not there, so try to insert the key
        -- if someone else inserts the same key concurrently,
        -- we could get a unique-key failure
        BEGIN
            INSERT INTO db(a,b) VALUES (key, data);
            RETURN;
        EXCEPTION WHEN unique_violation THEN
            -- do nothing, and loop to try the UPDATE again
        END;
    END LOOP;
END;
$$
LANGUAGE plpgsql;

SELECT merge_db(1, 'david');
SELECT merge_db(1, 'dennis');

7
: আমি বরং একটি লেখার যোগ্য কোটে ব্যবহার করতে চাই stackoverflow.com/a/8702291/330315
a_horse_with_no_name

একটি ফাংশন বনাম লিখনযোগ্য সিটিইর সুবিধা কী?
ফ্রাসোয়া Beausoleil

1
@ ফ্রাঞ্জোইস এক জিনিস, গতি জন্য। সিটিই ব্যবহার করে আপনি একবারে ডাটাবেসটি হিট করলেন। এইভাবে এটি করা আপনি সম্ভবত এটি দুটি বা আরও বেশিবার আঘাত করতে পারেন। এছাড়াও, অপ্টিমাইজারটি খাঁটি এসকিউএল কোডের মতো দক্ষতার সাথে pl / pgsql পদ্ধতিগুলি অনুকূল করতে পারে না।
অ্যাডাম ম্যাকলার

1
@ ফ্রানসোইস আরেকটি জিনিসের জন্য, একমত। উপরের উদাহরণটিতে একাধিক এসকিউএল স্টেটমেন্ট রয়েছে তাই আপনাকে রেসের অবস্থার বিষয়ে চিন্তা করতে হবে (ক্লুজে লুপের কারণ)। একটি একক এসকিউএল বিবৃতি পারমাণবিক হবে be এই লিঙ্কটি
অ্যাডাম ম্যাকলার 11

1
@ FrançoisBeausoleil দেখতে এখানে এবং এখানে কেন জন্য। মূলত পুনরায় চেষ্টা করার লুপ ছাড়াই আপনাকে হয় সিরিয়ালাইজ করতে হবে অথবা সহজাত জাতি অবস্থার কারণে আপনার ব্যর্থতার সম্ভাবনা রয়েছে।
জ্যাক ডগলাস

27

আপডেট (2015-08-20):

ON CONFLICT DO UPDATE(অফিশিয়াল ডকুমেন্টেশন) ব্যবহারের মাধ্যমে আপসেটগুলি পরিচালনা করার জন্য একটি অফিসিয়াল বাস্তবায়ন রয়েছে । এই লেখার সময়, এই বৈশিষ্ট্যটি বর্তমানে পোস্টগ্রিজএসকিউএল 9.5 আলফা 2 এ থাকে, যা এখানে ডাউনলোডের জন্য পাওয়া যায়: পোস্টগ্রিস উত্স ডিরেক্টরিগুলি

এখানে একটি উদাহরণ দেওয়া আছে, ধরে item_idনেওয়া আপনার প্রাথমিক কী:

INSERT INTO my_table
    (item_id, price)
VALUES
    (123456, 10.99)
ON
    CONFLICT (item_id)
DO UPDATE SET
    price = EXCLUDED.price

আসল পোস্ট ...

একটি সন্নিবেশ বা আপডেট হয়েছে কিনা তা দৃশ্যমানতা অর্জনের জন্য যখন আমি পৌঁছেছিলাম তখন একটি বাস্তবায়ন এখানে এসেছে।

সংজ্ঞাটি upsert_dataহ'ল মূল্য এবং আইটেম_আইডি দু'বার উল্লেখ না করে একক উত্সে মানগুলিকে একত্রিত করা: একবার আপডেটের জন্য, আবার sertোকানোর জন্য।

WITH upsert_data AS (
    SELECT
    '19.99'::numeric(10,2) AS price,
    'abcdefg'::character varying AS item_id
),
update_outcome AS (
    UPDATE pricing_tbl
    SET price = upsert_data.price
    FROM upsert_data
    WHERE pricing_tbl.item_id = upsert_data.item_id
    RETURNING 'update'::text AS action, item_id
),
insert_outcome AS (
    INSERT INTO
        pricing_tbl
    (price, item_id)
    SELECT
        upsert_data.price AS price,
        upsert_data.item_id AS item_id
    FROM upsert_data
    WHERE NOT EXISTS (SELECT item_id FROM update_outcome LIMIT 1)
    RETURNING 'insert'::text AS action, item_id
)
SELECT * FROM update_outcome UNION ALL SELECT * FROM insert_outcome

আপনি যদি ব্যবহারটি পছন্দ না করেন তবে upsert_dataএখানে একটি বিকল্প বাস্তবায়ন রয়েছে:

WITH update_outcome AS (
    UPDATE pricing_tbl
    SET price = '19.99'
    WHERE pricing_tbl.item_id = 'abcdefg'
    RETURNING 'update'::text AS action, item_id
),
insert_outcome AS (
    INSERT INTO
        pricing_tbl
    (price, item_id)
    SELECT
        '19.99' AS price,
        'abcdefg' AS item_id
    WHERE NOT EXISTS (SELECT item_id FROM update_outcome LIMIT 1)
    RETURNING 'insert'::text AS action, item_id
)
SELECT * FROM update_outcome UNION ALL SELECT * FROM insert_outcome

এটি কিভাবে সম্পাদন করে?
জেবি।

1
@jb। আমি চাই হিসাবে ভাল না। আপনি উল্লেখযোগ্য পারফরম্যান্সের জরিমানা দেখতে যাচ্ছেন straight তবে ছোট ব্যাচগুলির জন্য (1000 বা তার চেয়ে কম বলুন) এই উদাহরণটি ঠিক সূক্ষ্মভাবে সম্পাদন করা উচিত।
জশুয়া বার্নস

0

সন্নিবেশ বা আপডেটটি ঘটেছে কিনা তা এটি আপনাকে জানাবে:

with "update_items" as (
  -- Update statement here
  update items set price = 3499, name = 'Uncle Bob'
  where id = 1 returning *
)
-- Insert statement here
insert into items (price, name)
-- But make sure you put your values like so
select 3499, 'Uncle Bob'
where not exists ( select * from "update_items" );

আপডেটটি দেখা দিলে আপনি একটি সন্নিবেশ 0 পাবেন, অন্যথায় 1 বা ত্রুটি sertোকান।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.