PostgreSQL এ সদৃশ আপডেটে সন্নিবেশ করান?


644

কয়েক মাস আগে আমি স্ট্যাক ওভারফ্লোতে একটি উত্তর থেকে শিখেছি যে কীভাবে নিচের সিনট্যাক্সটি ব্যবহার করে মাইএসকিউএলটিতে একবারে একাধিক আপডেট করা যায়:

INSERT INTO table (id, field, field2) VALUES (1, A, X), (2, B, Y), (3, C, Z)
ON DUPLICATE KEY UPDATE field=VALUES(Col1), field2=VALUES(Col2);

আমি এখন পোস্টগ্র্যাস এসকিউএল এ স্যুইচ করেছি এবং দৃশ্যত এটি সঠিক নয়। এটি সমস্ত সঠিক সারণী উল্লেখ করছে তাই আমি ধরে নিলাম এটি বিভিন্ন কীওয়ার্ড ব্যবহারের বিষয় তবে আমি নিশ্চিত নই যে PostgreSQL ডকুমেন্টেশনে এটি কভার করা আছে।

স্পষ্ট করতে, আমি বেশ কয়েকটি জিনিস সন্নিবেশ করতে চাই এবং যদি সেগুলি ইতিমধ্যে তাদের আপডেট করার জন্য উপস্থিত থাকে।


38
যে কেউ এই প্রশ্নটি খুঁজে পান তার Depesz এর " নিবন্ধটি এত জটিল কেন?" নিবন্ধটি পড়া উচিত । এটি সমস্যাটি এবং সম্ভাব্য সমাধানগুলি অত্যন্ত ভালভাবে ব্যাখ্যা করে explains
ক্রেগ রিঞ্জার

8
ইউপিএসআরটি পোস্টগ্রাস 9.5 এ যুক্ত করা হবে: উইকি.পোস্টগ্রেসকল.আর.উইকি
টমড

4
@tommed - এটি সম্পন্ন করা হয়েছে: stackoverflow.com/a/34639631/4418
ওয়ারেন

উত্তর:


515

সংস্করণ 9.5-এর পরের পোস্টগ্রিসকিউএল-এ অন কনফ্লিক্ট ধারা সহ ইউপিএসআরটি সিনট্যাক্স রয়েছে নিম্নলিখিত সিনট্যাক্স সহ (মাইএসকিউএলের অনুরূপ)

INSERT INTO the_table (id, column_1, column_2) 
VALUES (1, 'A', 'X'), (2, 'B', 'Y'), (3, 'C', 'Z')
ON CONFLICT (id) DO UPDATE 
  SET column_1 = excluded.column_1, 
      column_2 = excluded.column_2;

"উপস্থাপক" এর জন্য পোস্টগ্রেস্কল-এর ইমেল গ্রুপ সংরক্ষণাগার অনুসন্ধান করা ম্যানুয়ালটিতে আপনি সম্ভবত যা করতে চান তার করার উদাহরণ খুঁজে বের করে :

উদাহরণ 38-2। আপডেট / ইনসার্টের ব্যতিক্রম

এই উদাহরণটি যথাযথ হিসাবে আপডেট বা INSERT সম্পাদন করতে ব্যতিক্রম হ্যান্ডলিং ব্যবহার করে:

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
        -- note that "a" must be unique
        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');

হ্যাকার মেলিং তালিকায় 9.1 এবং তারপরে সিটিই ব্যবহার করে বাল্কগুলিতে কীভাবে এটি করা যায় তার একটি উদাহরণ সম্ভবত রয়েছে :

WITH foos AS (SELECT (UNNEST(%foo[])).*)
updated as (UPDATE foo SET foo.a = foos.a ... RETURNING foo.id)
INSERT INTO foo SELECT foos.* FROM foos LEFT JOIN updated USING(id)
WHERE updated.id IS NULL;

একটি পরিষ্কার উদাহরণের জন্য a_horse_with_no_name এর উত্তর দেখুন ।


7
আমি কেবল এটির পক্ষে পছন্দ করি না এটি হ'ল ধীর হবে কারণ প্রতিটি উপস্থাপনাটি ডাটাবেসে তার নিজস্ব স্বতন্ত্র কল হবে।
baash05

@ বাশ05 এ এখানে প্রচুর পরিমাণে করার উপায় আছে, আমার আপডেট হওয়া উত্তরটি দেখুন।
স্টিফেন ডেন

2
আমি আলাদাভাবে কেবলমাত্র একটি জিনিস হ'ল কেবলমাত্র লুপের পরিবর্তে ১.২ লুপ ব্যবহার করি যাতে অন্য কোনও অনন্য বাধা লঙ্ঘিত হলে তা অনির্দিষ্টকালের জন্য স্পিন করবে না।
ওলমর্ক

2
excludedএখানে প্রথম সমাধানটিতে কী বোঝায় ?
আইচবিনালেন

2
দস্তাবেজগুলিতে @bbblenlen কনফ্লিক্ট করুন আপডেটের SET এবং যেখানে সারণির সারণির নাম (বা একটি উপনাম) ব্যবহার করে বিদ্যমান সারিটিতে অ্যাক্সেস রয়েছে এবং বিশেষ বর্জনিত টেবিলটি সন্নিবেশের জন্য প্রস্তাবিত সারিগুলিতে রয়েছে । এই ক্ষেত্রে, বিশেষ excludedসারণী আপনাকে সেই মানগুলিতে অ্যাক্সেস দেয় যা আপনি প্রথম স্থানে INSERT করার চেষ্টা করেছিলেন।
টিমিচেল

429

সতর্কতা: একই সময়ে একাধিক অধিবেশন থেকে কার্যকর করা হলে এটি নিরাপদ নয় (নীচে ক্যাভ্যাটগুলি দেখুন)।


পোস্টগ্রিস্কল-এ "ইউপিএসআরটি" করার আরেকটি চতুর উপায় হ'ল দুটি ক্রমিক আপডেট / ইনসার্ট বিবৃতি যা প্রতিটি সফল করার জন্য ডিজাইন করা হয়েছে বা তার কোনও প্রভাব নেই।

UPDATE table SET field='C', field2='Z' WHERE id=3;
INSERT INTO table (id, field, field2)
       SELECT 3, 'C', 'Z'
       WHERE NOT EXISTS (SELECT 1 FROM table WHERE id=3);

"ID = 3" সহ একটি সারি ইতিমধ্যে উপস্থিত থাকলে আপডেটটি সফল হবে, অন্যথায় এর কোনও প্রভাব নেই।

INSERT কেবল তখনই সফল হবে যদি "id = 3" সারিটি ইতিমধ্যে বিদ্যমান না থাকে।

আপনি এই দুটিকে একটি একক স্ট্রিংয়ে একত্রিত করতে পারেন এবং আপনার অ্যাপ্লিকেশন থেকে এক্সিকিউট করা একটি একক এসকিউএল বিবৃতি দিয়ে এগুলি উভয়ই চালাতে পারেন। একক লেনদেনে এগুলি একসাথে চালানোর পক্ষে সুপারিশ করা হয়।

এটি বিচ্ছিন্নভাবে বা লক করা টেবিলের সাথে চালানোর সময় খুব ভালভাবে কাজ করে তবে এটি জাতি শর্তের সাপেক্ষে এর অর্থ এটি একই সাথে ডুপ্লিকেট কী ত্রুটির সাথে ব্যর্থ হতে পারে যদি সারিবদ্ধভাবে একটি সারি সন্নিবেশ করা হয় বা কোনও সারি একসাথে মুছে ফেলা হয় তবে সন্নিবেশ করা কোনও সারি সমাপ্ত হতে পারে । SERIALIZABLEPostgreSQL 9.1 বা উচ্চতর একটি লেনদেন এটিকে খুব উচ্চতর সিরিয়ালাইজেশন ব্যর্থতার হারের ভিত্তিতে নির্ভরযোগ্যভাবে পরিচালনা করবে, যার অর্থ আপনাকে অনেক চেষ্টা করতে হবে। দেখুন কেন এত জটিল জটিল , যা এই কেসটিকে আরও বিশদে আলোচনা করে।

এই পদ্ধতির বিচ্ছিন্নতার হারানো আপডেটগুলির বিষয়বস্তু read committedযদি না অ্যাপ্লিকেশন আক্রান্ত সারি গণনাগুলি পরীক্ষা করে না এবং এটি পরীক্ষা করে না যে হয় insertবা updateক্ষতিগ্রস্থ সারিটি প্রভাবিত করে


6
সংক্ষিপ্ত উত্তর: রেকর্ডটি উপস্থিত থাকলে INSERT কিছুই করে না। দীর্ঘ উত্তর: INSERT এ নির্বাচন করুন যেখানে ক্লজটির ম্যাচ আছে যতগুলি ফলাফল ফিরে আসবে। এটি সর্বাধিক এক (যদি নম্বরটি উপ-নির্বাচনের ফলাফলে না হয়), অন্যথায় শূন্য zero INSERT এভাবে এক বা শূন্য সারি যুক্ত করবে।
পিটার বেকার

3
'যেখানে' অংশটি বিদ্যমান ব্যবহার করে সরল করা যেতে পারে:... where not exists (select 1 from table where id = 3);
এন্ডি তেজহোন

1
এটি সঠিক উত্তর হওয়া উচিত .. কিছুটা ছোটখাটো টুইটের সাহায্যে এটি একটি গণ আপডেট করতে ব্যবহার করা যেতে পারে ..
হুম..আমি

1
@ কেপলগিক, যে 9.1 সীমাবদ্ধতা লিখিতযোগ্য সিটিই (সাধারণ টেবিল এক্সপ্রেশন) এর সাথে রয়েছে যা উত্তরের অন্য একটিতে বর্ণিত হয়েছে। এই উত্তরে ব্যবহৃত সিনট্যাক্সটি খুব বেসিক এবং দীর্ঘ সময় ধরে সমর্থনযোগ্য।
বোভাইন

8
সতর্কতা, এই মধ্যে হারিয়ে আপডেট সাপেক্ষে read committedযদি না আপনার আবেদন চেক নিশ্চিত যে করতে বিচ্ছিন্নতা insertবা updateএকটি নন-জিরো rowcount আছে। Dba.stackexchange.com/q/78510/7788
ক্রেগ

227

PostgreSQL 9.1 এর সাহায্যে এটি একটি লিখনযোগ্য সিটিই ( সাধারণ সারণী প্রকাশ ) ব্যবহার করে অর্জন করা যায় :

WITH new_values (id, field1, field2) as (
  values 
     (1, 'A', 'X'),
     (2, 'B', 'Y'),
     (3, 'C', 'Z')

),
upsert as
( 
    update mytable m 
        set field1 = nv.field1,
            field2 = nv.field2
    FROM new_values nv
    WHERE m.id = nv.id
    RETURNING m.*
)
INSERT INTO mytable (id, field1, field2)
SELECT id, field1, field2
FROM new_values
WHERE NOT EXISTS (SELECT 1 
                  FROM upsert up 
                  WHERE up.id = new_values.id)

এই ব্লগ এন্ট্রি দেখুন:


নোট করুন যে এই সমাধানটি কোনও অনন্য কী লঙ্ঘন প্রতিরোধ করে না তবে এটি হারানো আপডেটগুলির পক্ষে ঝুঁকিপূর্ণ নয়।
দেখুন ফলো dba.stackexchange.com উপর ক্রেইগ রিঙ্গার দ্বারা আপ


1
@ ফ্রানসোয়া
বিউসোলিল:

2
@ এ_হর্স_বিহীন_নাম_নাম আপনি কীভাবে বোঝাতে চাইছেন যে জাতিদের অবস্থার সুযোগ খুব কম? আমি যখন এই কোয়েরি একই রেকর্ডের সাথে একই সাথে সম্পাদন করি তখন ত্রুটিটি পেয়েছি "ডুপ্লিকেট কী মানটি অনন্য প্রতিবন্ধকতা লঙ্ঘন করে" 100% না হওয়া অবধি প্রশ্নটি সনাক্ত না করে রেকর্ডটি সন্নিবেশ করা হয়েছে। এটি কি সম্পূর্ণ উদাহরণ?
জেরোইন ভ্যান ডিজক

4
@a_horse_with_no_name আপনার সমাধানটি সামঞ্জস্যপূর্ণ পরিস্থিতিতে কাজ করছে বলে মনে হচ্ছে আপনি যখন নীচের তালা দিয়ে উপস্থাপনের স্টেটমেন্টটি আবদ্ধ করবেন: কাজ শুরু করুন; ল্যাব টেবিল মাইটেবলটি শেয়ার সারি এক্সক্লুসিভ মোডে; <এখানে হ'ল; কমিট ওয়ার্ক;
জেরোইন ভ্যান ডিজক

2
@ জিরোয়ানভানডিজ্ক: ধন্যবাদ আমি "অনেক ছোট" দিয়ে যা বোঝাতে চেয়েছি তা হ'ল যদি আপডেটের এবং সন্নিবেশের মধ্যে সময়সীমা আরও ছোট হয় তবে সবকিছুই কেবল একটি একক বিবৃতি হিসাবে কিছুটা লেনদেন হয় (এবং পরিবর্তনের প্রতিশ্রুতি দেয়)। আপনি দুটি স্বতন্ত্র INSERT বিবৃতি দিয়ে সর্বদা পিকে লঙ্ঘন তৈরি করতে পারেন। আপনি যদি পুরো টেবিলটি লক করেন তবে আপনি কার্যকরভাবে এটিতে সমস্ত অ্যাক্সেসকে সিরিয়ালাইজ করুন (এমন কিছু যা আপনি সিরিয়ালাইজযোগ্য বিচ্ছিন্নতা স্তরের সাথে অর্জন করতে পারেন)।
a_horse_with_no_name

12
যদি সন্নিবেশকরণের লেনদেনটি পিছন ফিরে আসে তবে এই সমাধানটি হারানো আপডেটের বিষয়; কার্যকর করার জন্য কোনও চেক নেই যে UPDATEকোনও সারি প্রভাবিত করেছে।
ক্রেগ রিঞ্জার

132

PostgreSQL 9.5 এ আপনি আরও নতুন ব্যবহার করতে পারেন INSERT ... ON CONFLICT UPDATE

ডকুমেন্টেশন দেখুন ।

একটি মাইএসকিউএল INSERT ... ON DUPLICATE KEY UPDATEসরাসরি একটিতে পুনরায় প্রতিস্থাপন করা যেতে পারে ON CONFLICT UPDATE। উভয়ই এসকিউএল-মানক সিনট্যাক্স নয়, তারা উভয়ই ডাটাবেস-নির্দিষ্ট এক্সটেনশন। এর জন্য ভাল কারণ MERGEব্যবহার করা হয়নি , একটি নতুন বাক্য গঠন কেবল মজাদার জন্য তৈরি করা হয়নি। (মাইএসকিউএল এর সিনট্যাক্সেও এমন সমস্যা রয়েছে যার অর্থ এটি সরাসরি গ্রহণ করা হয়নি)।

যেমন দেওয়া সেটআপ:

CREATE TABLE tablename (a integer primary key, b integer, c integer);
INSERT INTO tablename (a, b, c) values (1, 2, 3);

মাইএসকিউএল কোয়েরি:

INSERT INTO tablename (a,b,c) VALUES (1,2,3)
  ON DUPLICATE KEY UPDATE c=c+1;

হয়ে:

INSERT INTO tablename (a, b, c) values (1, 2, 10)
ON CONFLICT (a) DO UPDATE SET c = tablename.c + 1;

পার্থক্য:

  • স্বতন্ত্রতা পরীক্ষার জন্য আপনাকে অবশ্যই কলামের নাম (বা অনন্য বাধা নাম) নির্দিষ্ট করতে হবে। এটাইON CONFLICT (columnname) DO

  • মূলশব্দটি SETঅবশ্যই ব্যবহার করা উচিত, যেন এটি একটি সাধারণ UPDATEবিবৃতি

এটিতে কিছু দুর্দান্ত বৈশিষ্ট্যও রয়েছে:

  • WHEREআপনার উপর একটি ধারা থাকতে পারে UPDATE(আপনাকে কার্যকরভাবে নির্দিষ্ট মানের জন্য ON CONFLICT UPDATEরূপান্তর ON CONFLICT IGNOREকরতে দেয়)

  • প্রস্তাবিত জন্য সন্নিবেশ মানগুলি সারি-ভেরিয়েবল হিসাবে উপলভ্য EXCLUDED, যার লক্ষ্য সারণির মতো কাঠামো রয়েছে। আপনি সারণির নাম ব্যবহার করে সারণীতে মূল মানগুলি পেতে পারেন। তাই এই ক্ষেত্রে EXCLUDED.cহতে হবে 10এবং (কারণ কি আমরা সন্নিবেশ করার চেষ্টা যে) "table".cহতে হবে 3কারণ টেবিলের মধ্যে বর্তমান মান আছে। আপনি উভয় বা উভয়ই SETঅভিব্যক্তি এবং WHEREধারাটিতে ব্যবহার করতে পারেন ।

উপস্থাপনের ব্যাকগ্রাউন্ডের জন্য পোস্টগ্রিসএসকিউএল-এ কীভাবে আপসার্ট (মার্জ, ইনসার্ট ... ডুপ্লিকেট আপডেট করা হবে) দেখুন?


আপনি উপরে বর্ণিত হিসাবে পোস্টগ্রিজ এসকিউএল এর 9.5 সমাধান সন্ধান করেছি কারণ মাইএসকিউএল এর অধীনে যখন আমি অটো বর্ধন ক্ষেত্রের ফাঁকফোকর অনুভব করছিলাম ON DUPLICATE KEY UPDATE। আমি পোস্টগ্রাস 9.5 ডাউনলোড করেছি এবং আপনার কোডটি প্রয়োগ করেছি তবে আশ্চর্যের সাথে একই সমস্যা পোস্টগ্র্রেসের অধীনে ঘটে: প্রাথমিক কীটির ক্রমিক ক্ষেত্রটি একটানা নয় (সন্নিবেশ এবং আপডেটের মধ্যে ফাঁক রয়েছে)) এখানে কি ঘটছে কোন ধারণা? এটা কি স্বাভাবিক? কোনও ধারণা কীভাবে এই আচরণ এড়ানো যায়? ধন্যবাদ.
ডাব্লুএম

@ ডাব্লুএম এটি বেশিরভাগই আপসেট অপারেশনের অন্তর্নিহিত। Theোকানোর চেষ্টা করার আগে ক্রমটি তৈরি করে এমন ফাংশনটি আপনাকে মূল্যায়ন করতে হবে। যেহেতু এই ধরণের ক্রমগুলি একই সাথে পরিচালিত করার জন্য ডিজাইন করা হয়েছে সেগুলি সাধারণ লেনদেন শব্দার্থবিজ্ঞানের হাত থেকে অব্যাহতিপ্রাপ্ত, তবে তারা প্রজন্ম না হলেও সাব-ট্রান্সজেকশনে ডাকা হয় না এবং ফিরে ঘুরিয়ে দেওয়া হয়, এটি স্বাভাবিকভাবে সম্পূর্ণ হয় এবং বাকী অপারেশনের সাথে প্রতিশ্রুতি দেয়। সুতরাং এটি "ফাঁকবিহীন" সিকোয়েন্স প্রয়োগের সাথেও ঘটবে। ডিবি এড়াতে পারে এমন একমাত্র উপায় কী চেক করার পরে অবধি সিক্যুয়েন্স জেনারেশন মূল্যায়ন বিলম্ব করা।
ক্রেগ রিঞ্জার

1
@WM যা তার নিজস্ব সমস্যা তৈরি করবে। মূলত, আপনি আটকে আছেন। তবে আপনি যদি সিরিয়াল / অটো_সংশোধন ফাঁকবিহীন হওয়ার উপর নির্ভর করে থাকেন তবে ইতিমধ্যে আপনি বাগ পেয়েছেন। আপনি অস্থায়ী ত্রুটি সহ রোলব্যাক কারণে ক্রম ফাঁক থাকতে পারে - লোড অধীনে পুনরায় বুট, ক্লায়েন্ট ত্রুটি মধ্য লেনদেন, ক্র্যাশ, ইত্যাদি তুমি অবশ্যই, কখনও উপর নির্ভর SERIAL/ SEQUENCEবা AUTO_INCREMENTথাকার ফাঁক না। আপনার যদি ফাঁকবিহীন ক্রমগুলির প্রয়োজন হয় তবে এগুলি আরও জটিল; আপনার সাধারণত একটি কাউন্টার টেবিল ব্যবহার করা উচিত। গুগল আপনাকে আরও বলবে। তবে সচেতন থাকুন ফাঁকবিহীন সিকোয়েন্সগুলি সমস্ত সন্নিবেশ সন্নিবেশকে প্রতিরোধ করে।
ক্রেগ রিঞ্জার

@ ডাব্লুএম যদি আপনার একেবারে ফাঁকবিহীন সিকোয়েন্স এবং উপস্থাপনের প্রয়োজন হয়, তবে আপনি কাউন্টার টেবিল ব্যবহার করে ফাঁকবিহীন সিকোয়েন্স প্রয়োগের সাথে ম্যানুয়ালটিতে আলোচিত ফাংশন-ভিত্তিক উপস্থাপনা পদ্ধতির ব্যবহার করতে পারেন। যেহেতু BEGIN ... EXCEPTION ...একটি বিয়োগফলগুলিতে রানগুলি একটি সাবট্রান্সজ্যাকশনগুলিতে ফিরে আসে যা ত্রুটি হয়ে ফিরে আসে, INSERTব্যর্থ হলে আপনার ক্রমবৃদ্ধিটি আবার ঘোরানো হবে ।
ক্রেগ রিঞ্জার

আপনাকে অনেক ধন্যবাদ @ ক্রেইগ রিঞ্জার, এটি বেশ তথ্যপূর্ণ ছিল। আমি বুঝতে পেরেছি যে আমি কেবল সেই স্বয়ংক্রিয় বর্ধিত প্রাথমিক কীটি ছেড়ে দিতে পারি। আমি 3 টি ক্ষেত্রের সমন্বিত প্রাথমিক তৈরি করেছি এবং আমার বর্তমান বর্তমান প্রয়োজনের জন্য, ফাঁকবিহীন অটো ইনক্রিমেন্ট ক্ষেত্রের সত্যিকার প্রয়োজন নেই। আপনাকে আবারও ধন্যবাদ, আপনার প্রদত্ত তথ্যগুলি ভবিষ্যতে প্রাকৃতিক এবং স্বাস্থ্যকর ডিবি আচরণ রোধ করার চেষ্টা করে আমার সময় সাশ্রয় করবে। আমি এখন এটি আরও ভাল বুঝতে।
ডাব্লুএম

17

আমি এখানে আসার সময় আমি একই জিনিসটির সন্ধান করছিলাম, তবে জেনেরিক "আপসেট" ফাংশনটির অভাব আমাকে কিছুটা বিরক্ত করেছিল তাই আমি ভেবেছিলাম যে আপনি কেবল আপডেটটি পাস করতে পারেন এবং ম্যানুয়ালটিতে ফাংশনটিতে যুক্তি হিসাবে এসকিএল সন্নিবেশ করতে পারবেন

এটি দেখতে এই রকম হবে:

CREATE FUNCTION upsert (sql_update TEXT, sql_insert TEXT)
    RETURNS VOID
    LANGUAGE plpgsql
AS $$
BEGIN
    LOOP
        -- first try to update
        EXECUTE sql_update;
        -- check if the row is found
        IF FOUND THEN
            RETURN;
        END IF;
        -- not found so insert the row
        BEGIN
            EXECUTE sql_insert;
            RETURN;
            EXCEPTION WHEN unique_violation THEN
                -- do nothing and loop
        END;
    END LOOP;
END;
$$;

এবং সম্ভবত আপনি যা করতে চেয়েছিলেন তা করতে "ব্যাচ" উপস্থাপনা ", আপনি স্কিল_আপডেট বিভক্ত করতে এবং পৃথক আপডেটগুলি লুপ করতে টিসিএল ব্যবহার করতে পারেন, প্রিফারেন্স হিট খুব ছোট হবে দেখুন http://archives.postgresql.org/pgsql- কর্মক্ষমতা / 2006-04 / msg00557.php

সর্বাধিক ব্যয় আপনার কোড থেকে ক্যোয়ারী চালাচ্ছে, ডাটাবেসের দিক থেকে এক্সিকিউশন খরচটি অনেক কম


3
আপনাকে এখনও এটিকে আবার চেষ্টা করে দেখতে হবে এবং DELETEআপনি টেবিলটি লক না SERIALIZABLEকরে পোস্টগ্র্রেএসকিউএল ৯.১ বা তার বেশি সংখ্যক লেনদেনের বিচ্ছিন্ন না হলে এটি একযোগের সাথে রেসগুলির ঝুঁকিপূর্ণ ।
ক্রেগ রিঞ্জার

13

এটি করার কোনও সহজ আদেশ নেই।

সর্বাধিক সঠিক পন্থা হ'ল ডক্সের মতো ফাংশন ব্যবহার করা ।

আর একটি সমাধান (যদিও এটি নিরাপদ নয়) হ'ল রিটার্নিংয়ের সাথে আপডেট করা, কোন সারিটি আপডেট হয়েছিল তা পরীক্ষা করে দেখুন এবং তার মধ্যে বাকীটি সন্নিবেশ করান

এর লাইন ধরে কিছু:

update table
set column = x.column
from (values (1,'aa'),(2,'bb'),(3,'cc')) as x (id, column)
where table.id = x.id
returning id;

আইডি ধরে ধরে: 2 ফিরিয়ে দেওয়া হয়েছিল:

insert into table (id, column) values (1, 'aa'), (3, 'cc');

অবশ্যই এটি শিগগিরই বা পরে (সমবর্তী পরিবেশে) জামিন দেবে, কারণ এখানে স্পষ্টভাবে রেসের শর্ত রয়েছে তবে সাধারণত এটি কার্যকর হবে।

এখানে এই বিষয়ে একটি দীর্ঘ এবং আরও নিবন্ধ রয়েছে


1
এই অপশনটি ব্যবহার করা হলে, আপডেটটি কিছু না করলেও আইডিটি ফিরে এসেছে কিনা তা নিশ্চিত হয়ে নিন। আমি ডাটাবেসগুলিকে "আপডেট টেবিল ফু সেট বার = 4 যেখানে বার = 4" এর মতো অনুকূল প্রশ্নগুলি দেখেছি।
থেলেম

10

ব্যক্তিগতভাবে, আমি সন্নিবেশ বিবৃতিতে সংযুক্ত একটি "নিয়ম" সেট আপ করেছি। বলুন যে আপনার কাছে একটি "ডিএনএস" সারণী ছিল যা প্রতি সময় ভিত্তিতে গ্রাহক প্রতি ডিএনএস হিট রেকর্ড করে:

CREATE TABLE dns (
    "time" timestamp without time zone NOT NULL,
    customer_id integer NOT NULL,
    hits integer
);

আপনি আপডেট হওয়া মানগুলির সাথে সারিগুলি পুনরায় toোকাতে সক্ষম হতে চেয়েছিলেন বা সেগুলি ইতিমধ্যে বিদ্যমান না থাকলে সেগুলি তৈরি করতে সক্ষম হয়েছিলেন। গ্রাহক_আইডি এবং সময়টির উপর ভিত্তি করে তৈরি। এটার মতো কিছু:

CREATE RULE replace_dns AS 
    ON INSERT TO dns 
    WHERE (EXISTS (SELECT 1 FROM dns WHERE ((dns."time" = new."time") 
            AND (dns.customer_id = new.customer_id)))) 
    DO INSTEAD UPDATE dns 
        SET hits = new.hits 
        WHERE ((dns."time" = new."time") AND (dns.customer_id = new.customer_id));

আপডেট: একযোগে সন্নিবেশগুলি ঘটতে থাকলে এটি ব্যর্থ হওয়ার সম্ভাবনা রয়েছে, কারণ এটি অনন্য_মিলনের ব্যতিক্রম তৈরি করবে। যাইহোক, অ-অবসানিত লেনদেন অব্যাহত থাকবে এবং সফল হবে এবং আপনাকে কেবল সমাপ্ত লেনদেনের পুনরাবৃত্তি করতে হবে।

তবে, যদি সারাক্ষণ প্রচুর সংখ্যক সন্নিবেশ ঘটে থাকে, আপনি সন্নিবেশ বিবৃতিগুলির চারপাশে একটি টেবিল লক রাখতে চাইবেন: শেয়ার সারি এক্সক্লুসিভ লকিং আপনার লক্ষ্য সারণিতে সারিগুলি সন্নিবেশ করানো, মুছতে বা আপডেট করতে পারে এমন কোনও ক্রিয়াকলাপ রোধ করবে। তবে, আপডেটগুলি যা অনন্য কী আপডেট করে না সেগুলি নিরাপদ, সুতরাং আপনি যদি কোনও কাজ না করেন তবে পরিবর্তে পরামর্শক লকগুলি ব্যবহার করুন।

এছাড়াও, কপি কমান্ডটি বিধিগুলি ব্যবহার করে না, সুতরাং আপনি যদি কপি দিয়ে সন্নিবেশ করিয়ে থাকেন তবে পরিবর্তে আপনাকে ট্রিগারগুলি ব্যবহার করতে হবে।


9

আমি এই ফাংশনটি মার্জটি ব্যবহার করি

CREATE OR REPLACE FUNCTION merge_tabla(key INT, data TEXT)
  RETURNS void AS
$BODY$
BEGIN
    IF EXISTS(SELECT a FROM tabla WHERE a = key)
        THEN
            UPDATE tabla SET b = data WHERE a = key;
        RETURN;
    ELSE
        INSERT INTO tabla(a,b) VALUES (key, data);
        RETURN;
    END IF;
END;
$BODY$
LANGUAGE plpgsql

1
কেবল updateপ্রথমটি করা এবং তারপরে আপডেট হওয়া সারিগুলির সংখ্যা পরীক্ষা করা আরও দক্ষ । (আহমদের উত্তর দেখুন)
এ_ ঘোড়া_ও_না_নাম

8

আমি উপরের "আপসেট" ফাংশনটি কাস্টম করি, আপনি যদি লিখুন এবং প্রতিস্থাপন করতে চান:

`

 CREATE OR REPLACE FUNCTION upsert(sql_insert text, sql_update text)

 RETURNS void AS
 $BODY$
 BEGIN
    -- first try to insert and after to update. Note : insert has pk and update not...

    EXECUTE sql_insert;
    RETURN;
    EXCEPTION WHEN unique_violation THEN
    EXECUTE sql_update; 
    IF FOUND THEN 
        RETURN; 
    END IF;
 END;
 $BODY$
 LANGUAGE plpgsql VOLATILE
 COST 100;
 ALTER FUNCTION upsert(text, text)
 OWNER TO postgres;`

এবং কার্যকর করার পরে, এই জাতীয় কিছু করুন:

SELECT upsert($$INSERT INTO ...$$,$$UPDATE... $$)

সংকলক ত্রুটি এড়াতে ডাবল ডলার-কমা রাখা গুরুত্বপূর্ণ

  • গতি পরীক্ষা করুন ...

7

সর্বাধিক-পছন্দ করা উত্তরের মতো, তবে কিছুটা দ্রুত কাজ করে:

WITH upsert AS (UPDATE spider_count SET tally=1 WHERE date='today' RETURNING *)
INSERT INTO spider_count (spider, tally) SELECT 'Googlebot', 1 WHERE NOT EXISTS (SELECT * FROM upsert)

(উত্স: http://www.the-art-of-web.com/sql/upsert/ )


3
এটি দুইটি অধিবেশনে একই সাথে চালিত হলে ব্যর্থ হবে, কারণ আপডেটগুলি কোনও বিদ্যমান সারি দেখতে পাবে না তাই উভয় আপডেট শূন্যের সারিগুলিতে আঘাত করবে, সুতরাং উভয় প্রশ্ন একটি সন্নিবেশ জারি করবে।
ক্রেগ রিঞ্জার

6

নাম মান জোড় হিসাবে অ্যাকাউন্ট সেটিংস পরিচালনার জন্য আমার একই সমস্যা। ডিজাইনের মানদণ্ডটি হ'ল বিভিন্ন ক্লায়েন্টের বিভিন্ন সেটিংস সেট থাকতে পারে।

আমার সমাধান, JWP এর অনুরূপ হ'ল আপনার অ্যাপ্লিকেশনের মধ্যে মার্জ রেকর্ড উত্পন্ন করে মুছে ফেলা এবং প্রতিস্থাপন করা।

এটি চমত্কার বুলেটপ্রুফ, প্ল্যাটফর্ম স্বতন্ত্র এবং যেহেতু ক্লায়েন্ট প্রতি প্রায় 20 টিরও বেশি সেটিংস নেই, এটি কেবল 3 মোটামুটি কম লোড ডিবি কল - সম্ভবত দ্রুততম পদ্ধতি।

স্বতন্ত্র সারিগুলি আপডেট করার বিকল্প - সন্নিবেশ করানো ব্যতিক্রমগুলি পরীক্ষা করা - বা এর কিছু সংমিশ্রণ হ'ল ঘৃণ্য কোড, ধীর এবং প্রায়শই বিরতি দেয় কারণ (উপরে উল্লিখিত হিসাবে) নন-স্ট্যান্ডার্ড এসকিউএল ব্যতিক্রম হ্যান্ডলিং ডিবি থেকে ডিবিতে পরিবর্তন - বা এমনকি প্রকাশের জন্য প্রকাশ হয় release

 #This is pseudo-code - within the application:
 BEGIN TRANSACTION - get transaction lock
 SELECT all current name value pairs where id = $id into a hash record
 create a merge record from the current and update record
  (set intersection where shared keys in new win, and empty values in new are deleted).
 DELETE all name value pairs where id = $id
 COPY/INSERT merged records 
 END TRANSACTION

তাই আপনাকে স্বাগতম। চমৎকার পরিচয়! :-)
ডন প্রশ্ন

1
এটি এর REPLACE INTOচেয়েও বেশি পছন্দসই INSERT INTO ... ON DUPLICATE KEY UPDATE, যা আপনি ট্রিগার ব্যবহার করলে সমস্যা দেখা দিতে পারে। আপনি মুছে ফেলা শেষ করবেন এবং আপডেটগুলি পরিবর্তে ট্রিগার / বিধি সন্নিবেশ করান।
সিএইচও

5

বিবৃতিটির পোস্টগ্রিসএসকিউএল ডকুমেন্টেশনINSERT অনুসারে ON DUPLICATE KEYমামলা পরিচালনা করা সমর্থিত নয়। সিনট্যাক্সের সেই অংশটি হ'ল স্বত্বগত মাইএসকিউএল এক্সটেনশন।


@ লুসিয়ানও MERGEওএলএপি অপারেশনের আরও সত্য; ব্যাখ্যার জন্য stackoverflow.com/q/17267417/398670 দেখুন । এটি সম্মতিযুক্ত শব্দার্থবিজ্ঞানের সংজ্ঞা দেয় না এবং বেশিরভাগ লোকেরা এটি সংরক্ষণের জন্য ব্যবহার করে কেবল বাগ তৈরি করছে।
ক্রেগ রিঞ্জার

5
CREATE OR REPLACE FUNCTION save_user(_id integer, _name character varying)
  RETURNS boolean AS
$BODY$
BEGIN
    UPDATE users SET name = _name WHERE id = _id;
    IF FOUND THEN
        RETURN true;
    END IF;
    BEGIN
        INSERT INTO users (id, name) VALUES (_id, _name);
    EXCEPTION WHEN OTHERS THEN
            UPDATE users SET name = _name WHERE id = _id;
        END;
    RETURN TRUE;
END;

$BODY$
  LANGUAGE plpgsql VOLATILE STRICT

5

ছোট সেট মার্জ করার জন্য, উপরের ফাংশনটি ব্যবহার করা ভাল। তবে আপনি যদি প্রচুর পরিমাণে ডেটা মার্জ করে থাকেন তবে আমি http://mbk.projects.postgresql.org এ সন্ধান করার পরামর্শ দেব

বর্তমান সর্বাধিক অনুশীলন যা আমি সচেতন তা হ'ল:

  1. টেম্প সারণিতে নতুন / আপডেট হওয়া ডেটা কপি করুন (নিশ্চিত, বা ব্যয় ঠিক থাকলে আপনি INSERT করতে পারেন)
  2. লকটি অর্জন করুন [alচ্ছিক] (পরামর্শ সারণী লকগুলির চেয়ে ভাল, আইএমও)
  3. একত্রিত করা. (মজার অংশ)

5

আপডেটের পরে সংশোধিত সারিগুলির সংখ্যা ফিরে আসবে। আপনি যদি জেডিবিসি (জাভা) ব্যবহার করেন তবে আপনি 0 এর বিপরীতে এই মানটি পরীক্ষা করতে পারেন এবং যদি কোনও সারি প্রভাবিত না হয় তবে পরিবর্তে INSERT ফায়ার করুন। আপনি যদি কিছু অন্যান্য প্রোগ্রামিং ভাষা ব্যবহার করেন তবে সম্ভবত পরিবর্তিত সারিগুলির সংখ্যা পাওয়া যেতে পারে, ডকুমেন্টেশন চেক করুন।

এটি মার্জিত নাও হতে পারে তবে আপনার কাছে অনেক সহজ এসকিউএল রয়েছে যা কলিং কোড থেকে আরও তুচ্ছ। ভিন্নভাবে, আপনি যদি পিএল / পিএসকিউএল তে দশ লাইন স্ক্রিপ্টটি লিখেন তবে কেবল আপনার একার জন্য সম্ভবত এক বা অন্য ধরণের ইউনিট পরীক্ষা করা উচিত।


4

সম্পাদনা: এটি প্রত্যাশার মতো কাজ করে না। গৃহীত উত্তরের মতো নয়, যখন দুটি প্রক্রিয়া বারবার upsert_fooএকই সাথে কল করে তখন এটি অনন্য কী লঙ্ঘন ঘটায়।

ইউরেকা! আমি একটি ক্যোয়ারিতে এটি করার একটি উপায় আবিষ্কার করেছি: UPDATE ... RETURNINGকোনও সারি প্রভাবিত হয়েছিল কিনা তা পরীক্ষা করার জন্য ব্যবহার করুন :

CREATE TABLE foo (k INT PRIMARY KEY, v TEXT);

CREATE FUNCTION update_foo(k INT, v TEXT)
RETURNS SETOF INT AS $$
    UPDATE foo SET v = $2 WHERE k = $1 RETURNING $1
$$ LANGUAGE sql;

CREATE FUNCTION upsert_foo(k INT, v TEXT)
RETURNS VOID AS $$
    INSERT INTO foo
        SELECT $1, $2
        WHERE NOT EXISTS (SELECT update_foo($1, $2))
$$ LANGUAGE sql;

UPDATEএকটি পৃথক পদ্ধতির মধ্যে কাজ করতে হবে কারণ, দুর্ভাগ্যবশত, এই একটি বাক্য গঠন ত্রুটি আছে:

... WHERE NOT EXISTS (UPDATE ...)

এখন এটি পছন্দসই হিসাবে কাজ করে:

SELECT upsert_foo(1, 'hi');
SELECT upsert_foo(1, 'bye');
SELECT upsert_foo(3, 'hi');
SELECT upsert_foo(3, 'bye');

1
আপনি যদি লেখার যোগ্য সিটিই ব্যবহার করেন তবে আপনি সেগুলিকে একটি বিবৃতিতে সংযুক্ত করতে পারেন। তবে এখানে পোস্ট করা বেশিরভাগ সমাধানের মতো, এটিও ভুল এবং সমবর্তী আপডেটগুলির উপস্থিতিতে ব্যর্থ হবে।
ক্রেগ রিঞ্জার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.