পোস্টগ্রেএসকিএল-এর সাথে কীভাবে "সন্নিবেশ উপেক্ষা করুন" এবং "সদৃশ কী আপডেটে" (স্ক্যুচল মার্জ) অনুকরণ করবেন?


140

কিছু এসকিউএল সার্ভারের এমন বৈশিষ্ট্য রয়েছে যেখানে INSERTএটি প্রাথমিক / অনন্য কী বাধা লঙ্ঘন করে যদি বাদ দেওয়া হয় is উদাহরণস্বরূপ, মাইএসকিউএল রয়েছে INSERT IGNORE

অনুকরণ করার INSERT IGNOREএবং ON DUPLICATE KEY UPDATEপোস্টগ্রিএসকিউএল সহ সেরা উপায় কী ?




6
9.5 হিসাবে, এটি স্থানীয়ভাবে সম্ভব: স্ট্যাকওভারফ্লো.com
ওয়ারেন

মাইএসকিউএল অনুকরণ করে: ON DUPLICATE KEY UPDATEপিজিএসকিউএল ৯.৫-তে এখনও কিছুটা অসম্ভব, কারণ পিজিএসকিউএল সমতুল্যর ON CLAUSEজন্য আপনাকে এই সীমাবদ্ধতার নাম সরবরাহ করতে হবে, যদিও মাইএসকিউএল এটি সংজ্ঞায়নের প্রয়োজন ছাড়াই যে কোনও প্রতিবন্ধকতা অর্জন করতে পারে। এটি আমাকে পুনরায় লেখার ছাড়াই এই বৈশিষ্ট্যটিকে "অনুকরণ" করতে বাধা দেয়।
নেভারইন্ডিংকিউ

উত্তর:


35

একটি আপডেট করার চেষ্টা করুন। যদি এটি কোনও সারি পরিবর্তন না করে যার অর্থ এটি বিদ্যমান ছিল না, তাই একটি সন্নিবেশ করুন। স্পষ্টতই, আপনি একটি লেনদেনের ভিতরে এটি করেন।

আপনি অবশ্যই ক্লায়েন্টের পাশে অতিরিক্ত কোডটি রাখতে না চাইলে কোনও ফাংশনে এটিকে আবদ্ধ করতে পারেন। সেই ভাবনায় খুব বিরল জাতি অবস্থার জন্য আপনারও একটি লুপ দরকার।

ডকুমেন্টেশনে এর একটি উদাহরণ রয়েছে: http://www.postgresql.org/docs/9.3/static/plpgsql-control-structures.html , নীচের অংশে 40-2 উদাহরণ।

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

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


4
"যদি আপনি প্রচুর পরিমাণে সারি নিয়ে কাজ করে থাকেন" এটি আমার ক্ষেত্রে ঠিক case আমি সর্বাধিক আপডেট / সারি সন্নিবেশ করতে চাই এবং মাইএসকিএল দিয়ে আমি কোনও লুপিং ছাড়াই কেবল একটি ক্যোয়ারির সাহায্যে এটি করতে পারি। এখন আমি আশ্চর্য হয়েছি পোস্টগ্রেএসকিএল দিয়েও এটি সম্ভব কিনা: বাল্ক আপডেট বা সন্নিবেশ করানোর জন্য মাত্র একটি ক্যোয়ারী ব্যবহার করতে। আপনি বলছেন: "আপনি এটিকে দুটি ক্যোয়ারিতে ভাগ করে নেওয়া সেরা, একটি ইনসার্টের জন্য এবং একটি আপডেটের জন্য" তবে আমি কীভাবে কীভাবে এমন একটি সন্নিবেশ করব যা সদৃশ কীগুলিতে ত্রুটি না ফেলে? (যেমন। "INSERT IGNORE")
gpilotino

4
ম্যাগনাসের অর্থ এই যে আপনি এই জাতীয় একটি কোয়েরি ব্যবহার করেন: "লেনদেন শুরু করুন; অস্থায়ী টেবিলটি অস্থায়ী_ টেবিলটি পরীক্ষা হিসাবে বেছে নিন * যেখানে অকার্যকর_ টেবিলটি 'data_file.csv' থেকে অনুলিপি করুন; লক টেবিল পরীক্ষা; অস্থায়ী_ টেবিল থেকে টেস্ট সেট ডেটা = অস্থায়ী_ টেবিল.ডেটা আপডেট করুন where টেস্ট.আইডি = অস্থায়ী_ টেবিল.আইডি; পরীক্ষায় প্রবেশ করুন অস্থায়ী_ টেবিল থেকে * নির্বাচন করুন যেখানে আইডি নেই (পরীক্ষা থেকে আইডি নির্বাচন করুন) হিসাবে "
টমেটজকি

25
আপডেট: PostgreSQL 9.5 এর সাথে এটি এখনকার মতো সহজ INSERT ... ON CONFLICT DO NOTHING;। উত্তরও দেখুন stackoverflow.com/a/34639631/2091700
আলফায়া

গুরুত্বপূর্ণ, এসকিউএল-মানক MERGEহয় না একটি সম্পাতবিন্দু নিরাপদ upsert, যদি না আপনি একটি নিতে LOCK TABLEপ্রথম। লোকেরা সেভাবে এটি ব্যবহার করে তবে এটি ভুল।
ক্রেগ রিঞ্জার

1
V9.5 এর সাথে এটি এখন একটি 'নেটিভ' বৈশিষ্ট্য, সুতরাং দয়া করে @ আলফা'র ​​মন্তব্যটি দেখুন (কেবলমাত্র বিজ্ঞাপনটি দেওয়া হয়েছে যে উত্তরটির বিজ্ঞাপন দেয়)
ক্যামিলো দেলভাস্তো

178

পোস্টগ্রিসকিউএল 9.5 সহ, এটি এখন স্থানীয় কার্যকারিতা (যেমন মাইএসকিউএল বেশ কয়েক বছর ধরে ছিল):

প্রবেশ করুন ... কনফ্লিক্টে কিছুই করবেন না / আপডেট করুন ("উচ্চতর")

9.5 "ইউপিএসআরটি" ক্রিয়াকলাপের জন্য সমর্থন নিয়ে আসে। INSERT- কে একটি কনফ্লিক্ট ডু আপডেট / আইজিএনওর ধারা স্বীকার করার জন্য বাড়ানো হয়েছে। এই ধারাটি হ'ল নকলের লঙ্ঘনের ক্ষেত্রে বিকল্প ব্যবস্থা গ্রহণের জন্য নির্দিষ্ট করে।

...

নতুন সিনট্যাক্সের আরও উদাহরণ:

INSERT INTO user_logins (username, logins)
VALUES ('Naomi',1),('James',1) 
ON CONFLICT (username)
DO UPDATE SET logins = user_logins.logins + EXCLUDED.logins;

100

সম্পাদনা করুন: আপনি যদি ওয়ারেনের উত্তরটি মিস করেন তবে PG9.5 এ এখন স্থানীয়ভাবে রয়েছে; আপগ্রেড করার সময়!


বিলে কারভিনের উত্তরের উপর ভিত্তি করে একটি নিয়ম ভিত্তিক পদ্ধতির দেখতে কেমন হবে তা বানান করতে (একই ডিবিতে অন্য একটি স্কিমা থেকে স্থানান্তরিত করা, এবং একটি বহু-কলাম প্রাথমিক কী সহ):

CREATE RULE "my_table_on_duplicate_ignore" AS ON INSERT TO "my_table"
  WHERE EXISTS(SELECT 1 FROM my_table 
                WHERE (pk_col_1, pk_col_2)=(NEW.pk_col_1, NEW.pk_col_2))
  DO INSTEAD NOTHING;
INSERT INTO my_table SELECT * FROM another_schema.my_table WHERE some_cond;
DROP RULE "my_table_on_duplicate_ignore" ON "my_table";

দ্রষ্টব্য: বিধিটি বাতিল INSERTনা হওয়া অবধি সমস্ত ক্রিয়াকলাপে প্রযোজ্য , সুতরাং যথেষ্ট অ্যাডহক নয়।


@ সেমাকে আপনি বোঝাতে চাইছেন যদি another_schema.my_tableসীমাবদ্ধতা অনুসারে সদৃশ থাকে my_table?
ইওহানম

2
@ ইওহানম আমি পোস্টগ্রিস্কএল 9.3 এ নিয়মটি পরীক্ষা করেছি এবং এখনও একাধিক সারি সন্নিবেশ বিবৃতি সহ ডুপ্লিকেটগুলি সন্নিবেশ করতে পারতাম যেমন INSERT INTO "my_table" (a, b), (a, b); (যে সারি (ক, খ) ধরে নেওয়া যাক এখনো "my_table" মধ্যে উপস্থিত না।)
সেমা

@ সেমা, গেটচা - এর অর্থ অবশ্যই সমস্ত ডেটা sertedোকানোর জন্য শুরুতে নিয়মটি কার্যকর করা হবে এবং প্রতিটি সারি সন্নিবেশ করার পরে পুনরায় সংশোধন করা হবে না। একটি পদ্ধতি INSERT INTO "my_table" SELECT DISTINCT ON (pk_col_1, pk_col_2) * FROM the_tmp_table;
হ'ল

@ ইওহানম আরেকটি পদ্ধতি হ'ল নকলের সীমাবদ্ধতাগুলি অস্থায়ীভাবে শিথিল করা এবং dোকানোতে সদৃশগুলি গ্রহণ করা, তবে পরে নকলগুলি সরিয়েDELETE FROM my_table WHERE ctid IN (SELECT ctid FROM (SELECT ctid,ROW_NUMBER() OVER (PARTITION BY pk_col_1,pk_col_2) AS rn FROM my_table) AS dups WHERE dups.rn > 1);
সেমা

@ সেমায় বর্ণিত আমার সমস্যা হচ্ছে। আমি যদি একটি সন্নিবেশ (ক, খ), (ক, খ) করি তবে এটি একটি ত্রুটি ছুঁড়ে। এই ক্ষেত্রেও ত্রুটিগুলি দমন করার কোনও উপায় আছে?
ডায়োগো মেলো

35

আপনার যাদের পোস্টগ্রাগেস 9.5 বা তার বেশি রয়েছে তাদের জন্য নতুন অন ​​কনফ্লিক্ট ডু নথিং সিনট্যাক্সটি কাজ করা উচিত:

INSERT INTO target_table (field_one, field_two, field_three ) 
SELECT field_one, field_two, field_three
FROM source_table
ON CONFLICT (field_one) DO NOTHING;

আমাদের মধ্যে যাদের পূর্ববর্তী সংস্করণ রয়েছে, তাদের ডান যোগদানটি পরিবর্তে কাজ করবে:

INSERT INTO target_table (field_one, field_two, field_three )
SELECT source_table.field_one, source_table.field_two, source_table.field_three
FROM source_table 
LEFT JOIN target_table ON source_table.field_one = target_table.field_one
WHERE target_table.field_one IS NULL;

সমবর্তী পরিবেশে একটি বড় inোকানোর সময় দ্বিতীয় পদ্ধতির কাজ হয় না। এই কোয়েরিটি কার্যকর হওয়ার সময় আপনি Unique violation: 7 ERROR: duplicate key value violates unique constraintযখন target_tableতাতে একটি অন্য সারি প্রবেশ করিয়েছিলেন , যদি তাদের কীগুলি সত্যই একে অপরের নকল করে। আমি বিশ্বাস করি যে লক করা সাহায্য করবে, তবে স্বচ্ছন্দভাবে ক্ষতিগ্রস্থ হবে। target_table
জি কাশতানোভ

1
ON CONFLICT (field_one) DO NOTHINGউত্তরের সেরা অংশ।
আবেল কালেজো

24

পেতে সন্নিবেশ উপেক্ষা যুক্তিবিজ্ঞান আপনি নীচের মত কিছু করতে পারেন। আমি কেবল আক্ষরিক মানগুলির একটি নির্বাচিত বিবরণীটি সর্বাধিক কাজ করে সন্নিবেশ করলাম, তারপরে আপনি নকল না দিয়ে ক্লুপটি দিয়ে মুখোশটি বের করতে পারেন। সদৃশ যুক্তিতে আপডেট পেতে আমার সন্দেহ হয় একটি pl / pgsql লুপের প্রয়োজন হবে।

INSERT INTO manager.vin_manufacturer
(SELECT * FROM( VALUES
  ('935',' Citroën Brazil','Citroën'),
  ('ABC', 'Toyota', 'Toyota'),
  ('ZOM',' OM','OM')
  ) as tmp (vin_manufacturer_id, manufacturer_desc, make_desc)
  WHERE NOT EXISTS (
    --ignore anything that has already been inserted
    SELECT 1 FROM manager.vin_manufacturer m where m.vin_manufacturer_id = tmp.vin_manufacturer_id)
)

যদি টিএমপিতে একটি সদৃশ সারি থাকে, তবে এটি কী ঘটতে পারে?
হেনলি চিউ

আপনি সর্বদা স্বতন্ত্র কীওয়ার্ড দিয়ে নির্বাচন করতে পারেন।
কিও

5
ঠিক একটি এফওয়াইআই হিসাবে, "যেখানে নেই" কৌশলটি একাধিক লেনদেন জুড়ে কাজ করে না কারণ বিভিন্ন লেনদেন অন্যান্য লেনদেন থেকে নতুন যুক্ত হওয়া ডেটা দেখতে পারে না।
ডেভ জোহেনসেন

21
INSERT INTO mytable(col1,col2) 
    SELECT 'val1','val2' 
    WHERE NOT EXISTS (SELECT 1 FROM mytable WHERE col1='val1')

সব একই জিনিস করার চেষ্টা করে একাধিক লেনদেনের প্রভাব কী? এটি কি সম্ভব যেখানে নির্বাহের অস্তিত্ব নেই এবং সন্নিবেশ করানো অন্য কিছু লেনদেন সম্পাদন করে একটি সারি সন্নিবেশ করে? এবং যদি পোস্টগ্র্রেস এটিকে আটকাতে পারে তবে পোস্টগ্রাগগুলি যখন এই আঘাত করে তখন সমস্ত লেনদেন জুড়ে একটি সংযোগের বিন্দু প্রবর্তন করে না?
ικrτhικ

এটি একাধিক লেনদেনের সাথে কাজ করে না, কারণ নতুন যুক্ত হওয়া ডেটা অন্যান্য লেনদেনগুলিতে দৃশ্যমান নয়।
ডেভ জোহেনসেন

12

দেখে মনে হচ্ছে PostgreSQL একটি নিয়ম নামে একটি স্কিমা অবজেক্ট সমর্থন করে ।

http://www.postgresql.org/docs/current/static/rules-update.html

প্রদত্ত প্রাথমিক কী মান সহ কোনও সারি উপস্থিত থাকলে ON INSERTতা তৈরি করে আপনি একটি প্রদত্ত টেবিলের জন্য একটি নিয়ম তৈরি করতে পারেন NOTHING, বা অন্যথায় এটির UPDATEপরিবর্তে এটি করতে পারেনINSERT প্রদত্ত প্রাথমিক কী মানটির সাথে যদি একটি সারি উপস্থিত থাকে তবে পারেন।

আমি নিজে চেষ্টা করে দেখিনি, তাই আমি অভিজ্ঞতা থেকে কথা বলতে পারি না বা উদাহরণ দিতে পারি না।


1
যদি আমি ভালভাবে বুঝতে পারি তবে এই বিধিগুলি ট্রিগারগুলি যা প্রতিবার বিবৃতি দেওয়ার সময় কার্যকর করা হয়। আমি যদি কেবল একটি প্রশ্নের জন্য বিধি প্রয়োগ করতে চাই তবে কী হবে? আমাকে কি নিয়ম তৈরি করতে হবে তাৎক্ষণিকভাবে তা ধ্বংস করে দিতে হবে? (রেস শর্তগুলির সম্পর্কে কী?)
gpilotino

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

4
পোস্টগ্রেএসকিউএল লেনদেনের ডিডিএল সমর্থন করে, এর অর্থ হল যে আপনি যদি কোনও নিয়ম তৈরি করেন এবং এটি একটি একক লেনদেনের মধ্যে ফেলে দেন তবে সেই নিয়মটি কখনই সেই লেনদেনের বাইরে প্রদর্শিত হত না (এবং এর বাইরে কখনও কোনও প্রভাব পড়েনি)।
cdowie

6

যেমন @ মন্তরী তার মন্তব্যে উল্লেখ করেছেন। পোস্টগ্রিস টেবিলগুলিতে সন্নিবেশ করানোর সময়, দ্বন্দ্বের উপর দ্বিধা (..) কিছুই করবেন না ডুপ্লিকেট ডেটা notোকানোর জন্য ব্যবহার করার জন্য সেরা কোড নয় .:

query = "INSERT INTO db_table_name(column_name)
         VALUES(%s) ON CONFLICT (column_name) DO NOTHING;"

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

q = 'ALTER id_column serial RESTART WITH 1'

যদি আমার ডেটাতে একটি আইডি ক্ষেত্র থাকে, আমি এটিকে প্রাথমিক আইডি / সিরিয়াল আইডি হিসাবে ব্যবহার করি না, আমি একটি আইডি কলাম তৈরি করি এবং আমি এটি সিরিয়ালে সেট করি। আমি আশা করি এই তথ্যটি সকলের পক্ষে সহায়ক। * আমার কাছে সফটওয়্যার বিকাশ / কোডিংয়ের কোনও কলেজ ডিগ্রি নেই। কোডিংয়ে আমি যা কিছু জানি, আমি নিজেই অধ্যয়ন করি।


এটি যৌগিক অনন্য সূচকগুলিতে কাজ করে না!
নুলিক

4

এই সমাধানটি বিধি ব্যবহার করা এড়িয়ে চলে:

BEGIN
   INSERT INTO tableA (unique_column,c2,c3) VALUES (1,2,3);
EXCEPTION 
   WHEN unique_violation THEN
     UPDATE tableA SET c2 = 2, c3 = 3 WHERE unique_column = 1;
END;

তবে এটির একটি পারফরম্যান্স অপূর্ণতা রয়েছে (দেখুন) পোস্টগ্রাইএসকিউএল.আর.এক্স দেখুন ):

এক্সেসপশন ক্লজ সমেত একটি ব্লক প্রবেশ ছাড়তে এবং প্রস্থান ছাড়াই উল্লেখযোগ্যভাবে বেশি ব্যয়বহুল একটি ছাড়াই। অতএব, প্রয়োজন ছাড়াই এক্সেসপশন ব্যবহার করবেন না।


1

প্রচুর পরিমাণে, আপনি alwaysোকানোর আগে সর্বদা সারিটি মুছতে পারেন। যে সারিটি বিদ্যমান নেই তা মুছে ফেলার ফলে ত্রুটি হয় না, তাই এটি নিরাপদে এড়িয়ে যায়।


2
এই পদ্ধতিটি অদ্ভুত বর্ণের অবস্থার জন্য বেশ প্রবণ হবে, আমি এটির প্রস্তাব দেব না ...
স্টিভেন শ্লানস্কার

1
+1 এটি সহজ এবং জেনেরিক। যত্ন সহ ব্যবহার করা হলে এটি আসলে একটি সহজ সমাধান হতে পারে।
ওয়াউটার ভ্যান নিফটারিক

1
বিদ্যমান ডেটা পোস্ট-.োকানোর পরে পরিবর্তন করা হয়েছে (তবে সদৃশ কীতে নয়) এটি কাজ করবে না এবং আমরা আপডেটগুলি রাখতে চাই। এটি এমন পরিস্থিতিতে যখন এসকিউএল স্ক্রিপ্ট থাকে যেগুলি বেশ কয়েকটি ভিন্ন সিস্টেমের জন্য লেখা থাকে, যেমন ডিবি আপডেট, যা উত্পাদন, কিউএ, ডেভ এবং পরীক্ষার সিস্টেমে চালিত হয়।
হ্যানো ফিয়েটজ

1
আপনি যদি DEFERRABLE INITIALLY DEFERREDপতাকাগুলি দিয়ে তৈরি করেন তবে বিদেশী কী কোনও সমস্যা হতে পারে ।
টেমোটো

-1

ডেটা আমদানি স্ক্রিপ্টগুলির জন্য, "যদি উপস্থিত না থাকে" প্রতিস্থাপন করতে, একভাবে, কিছুটা বিশ্রী সূত্র রয়েছে যা তবুও কাজ করে:

DO
$do$
BEGIN
PERFORM id
FROM whatever_table;

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