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


266

এখানে খুব ঘন ঘন জিজ্ঞাসিত প্রশ্ন হ'ল একটি উপস্থাপনা কীভাবে করা যায়, যা মাইএসকিউএল কল করে INSERT ... ON DUPLICATE UPDATEএবং মানটি MERGEঅপারেশনের অংশ হিসাবে সমর্থন করে।

প্রদত্ত পোস্টগার্রেএসকিউএল সরাসরি সমর্থন করে না (pg 9.5 এর আগে), আপনি কীভাবে এটি করেন? নিম্নোক্ত বিবেচনা কর:

CREATE TABLE testtable (
    id integer PRIMARY KEY,
    somedata text NOT NULL
);

INSERT INTO testtable (id, somedata) VALUES
(1, 'fred'),
(2, 'bob');

এইবার কল্পনা করুন যে আপনি "upsert" tuples করতে চান (2, 'Joe'), (3, 'Alan'), সুতরাং নতুন টেবিল বিষয়বস্তু হবে:

(1, 'fred'),
(2, 'Joe'),    -- Changed value of existing tuple
(3, 'Alan')    -- Added new tuple

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

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

এই কৌশলগুলি "বিদ্যমান না থাকলে সন্নিবেশ করান, অন্যথায় কিছু করবেন না", অর্থাত "নকল কী উপেক্ষা করে" সন্নিবেশ করুন "এর জন্যও দরকারী।



8
@ মিশেলহ্যাম্পটনের লক্ষ্যটি ছিল একটি সুনির্দিষ্ট সংস্করণ তৈরি করা যা একাধিক পুরানো উত্তর - এবং লক করা দ্বারা বিভ্রান্ত হয় না, যাতে কেউ এ সম্পর্কে কিছুই করতে পারে না। আমি কাছের লোকের সাথে একমত নই।
ক্রেগ রিঞ্জার

কেন, তাহলে এটি শীঘ্রই পুরানো হয়ে যাবে - এবং লক হয়ে গেছে, তাই কেউ এ সম্পর্কে কিছুই করতে পারে না।
মাইকেল হ্যাম্পটন

2
@ মিশেলহ্যাম্পটন যদি আপনি উদ্বিগ্ন হন তবে সম্ভবত আপনি যার সাথে লিঙ্ক করেছেন তাকে পতাকাঙ্কিত করতে পারেন এবং এটি আনলক করার জন্য বলতে পারেন যাতে এটি পরিষ্কার করা যায়, তবে আমরা এটিকে মার্জ করতে পারি I'm আমি কেবলমাত্র একমাত্র সুস্পষ্ট কাছাকাছি থাকার কারণে অসুস্থ- যেমন একটি বিভ্রান্তিকর এবং ভুল জগাখিচুড়ি হওয়ার জন্য আপ-ডুপ।
ক্রেগ রিঞ্জার

1
যে প্রশ্নোত্তর লক করা হয় না!
মাইকেল হ্যাম্পটন

উত্তর:


396

9.5 এবং আরও নতুন:

PostgreSQL 9.5 এবং আরও নতুন সমর্থন INSERT ... ON CONFLICT UPDATE(এবং ON CONFLICT DO NOTHING), অর্থাৎ উপস্থাপন।

সঙ্গে তুলনাON DUPLICATE KEY UPDATE

দ্রুত ব্যাখ্যা

ব্যবহারের জন্য ম্যানুয়ালটি দেখুন - বিশেষত সিন্ট্যাক্স ডায়াগ্রামে দ্বন্দ্ব_অধি ধারা এবং ব্যাখ্যামূলক পাঠ্য

নীচে দেওয়া 9.4 এবং এর চেয়ে বেশি পুরানো সমাধানগুলির বিপরীতে, এই বৈশিষ্ট্যটি একাধিক বিরোধী সারিগুলির সাথে কাজ করে এবং এর জন্য একচেটিয়া লকিং বা পুনরায় চেষ্টা করার প্রয়োজন নেই।

বৈশিষ্ট্য যুক্ত করার প্রতিশ্রুতি এখানে এবং এর বিকাশের চারপাশের আলোচনা এখানে


আপনি যদি 9.5-এ থাকেন এবং পশ্চাদপটে সামঞ্জস্যপূর্ণ হওয়ার প্রয়োজন না থাকলে আপনি এখন পড়া বন্ধ করতে পারেন


9.4 এবং আরও পুরানো:

পোস্টগ্রিসকিউএল-তে কোনও বিল্ট-ইন UPSERT(বা MERGE) সুবিধা নেই, এবং সমবর্তী ব্যবহারের ক্ষেত্রে দক্ষতার সাথে এটি করা খুব কঠিন।

এই নিবন্ধটি কার্যকরভাবে বিস্তারিতভাবে আলোচনা করেছে

সাধারণভাবে আপনাকে দুটি বিকল্পের মধ্যে পছন্দ করতে হবে:

  • পুনরায় চেষ্টা করে পৃথক সন্নিবেশ / আপডেট অপারেশন; অথবা
  • টেবিলটি লক করে ব্যাচ মার্জ করছে doing

স্বতন্ত্র সারির পুনরায় চেষ্টা করুন লুপ

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

PostgreSQL ডকুমেন্টেশনে একটি দরকারী পদ্ধতি রয়েছে যা আপনাকে এটি ডাটাবেসের অভ্যন্তরে একটি লুপে করতে দেয় । এটি হারানো আপডেটগুলি থেকে রক্ষা করে এবং সর্বাধিক নিষ্পাপ সমাধানগুলির বিপরীতে রেসগুলি সন্নিবেশ করায়। এটি কেবল READ COMMITTEDমোডে কাজ করবে এবং এটি কেবল নিরাপদ যদি এটি লেনদেনে আপনি একমাত্র কাজ করেন, যদিও। ট্রিগার বা গৌণ অনন্য কীগুলি অনন্য লঙ্ঘনের কারণ হলে ফাংশনটি সঠিকভাবে কাজ করবে না।

এই কৌশলটি খুব অদক্ষ। যখনই ব্যবহারিক আপনার কাজের সারি সারি করা উচিত এবং পরিবর্তে নীচে বর্ণিত হিসাবে একটি বাল্ক আপসেট করা উচিত।

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

কিছু চেষ্টা করা সমাধানগুলি SELECT দৌড় বিবেচনা করতে ব্যর্থ হয়। আপনি যদি স্পষ্ট এবং সহজ চেষ্টা করেন:

-- THIS IS WRONG. DO NOT COPY IT. It's an EXAMPLE.

BEGIN;

UPDATE testtable
SET somedata = 'blah'
WHERE id = 2;

-- Remember, this is WRONG. Do NOT COPY IT.

INSERT INTO testtable (id, somedata)
SELECT 2, 'blah'
WHERE NOT EXISTS (SELECT 1 FROM testtable WHERE testtable.id = 2);

COMMIT;

তারপরে যখন দুটি রান এক সাথে হয় তখন বেশ কয়েকটি ব্যর্থতা মোড থাকে। একটি আপডেট পুনরায় চেক সঙ্গে ইতিমধ্যে আলোচিত সমস্যা। আর এক যেখানে উভয় UPDATEএকই সময়ে শূন্য সারি মেলে এবং অবিরত। তারপর তারা উভয় কি EXISTSপরীক্ষা, যা ঘটবে সামনেINSERT । উভয়ই শূন্য সারি পায়, তাই উভয়ই তা করে INSERT। একটি সদৃশ কী ত্রুটি সঙ্গে ব্যর্থ।

এজন্য আপনার পুনরায় চেষ্টা করার লুপ দরকার। আপনি ভাবতে পারেন যে আপনি চতুর এসকিউএল দিয়ে নকল কী বা ত্রুটিযুক্ত আপডেটগুলি প্রতিরোধ করতে পারেন, তবে আপনি পারবেন না। আপনাকে সারি গণনাগুলি পরীক্ষা করতে বা ডুপ্লিকেট কী ত্রুটিগুলি (নির্বাচিত পদ্ধতির উপর নির্ভর করে) পরিচালনা করতে হবে এবং পুনরায় চেষ্টা করতে হবে।

এর জন্য আপনার নিজের সমাধানটি রোল করবেন না। বার্তা সারি লাগানোর মতো, এটি সম্ভবত ভুল।

লক সহ বাল্ক আপসার্ট

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

এই ক্ষেত্রে, আপনি সাধারণত নিম্নলিখিত প্রক্রিয়াটি অনুসরণ করেন:

  • CREATEএকটি TEMPORARYটেবিল

  • COPY বা টেম্প টেবিলটিতে নতুন ডেটা বাল্ক-sertোকান

  • LOCKলক্ষ্য টেবিল IN EXCLUSIVE MODE। এটি অন্য লেনদেনের অনুমতি দেয় SELECTতবে টেবিলটিতে কোনও পরিবর্তন আনবে না।

  • UPDATE ... FROMটেম্প টেবিলের মানগুলি ব্যবহার করে বিদ্যমান রেকর্ডগুলির একটি করুন ;

  • INSERTলক্ষ্য সারণীতে ইতিমধ্যে বিদ্যমান না এমন একটি সারি করুন;

  • COMMIT, লক মুক্তি।

উদাহরণস্বরূপ, প্রশ্নে দেওয়া উদাহরণের জন্য, INSERTটেম্প টেবিলটি স্থাপনের জন্য বহু-মূল্যবান ব্যবহার করে :

BEGIN;

CREATE TEMPORARY TABLE newvals(id integer, somedata text);

INSERT INTO newvals(id, somedata) VALUES (2, 'Joe'), (3, 'Alan');

LOCK TABLE testtable IN EXCLUSIVE MODE;

UPDATE testtable
SET somedata = newvals.somedata
FROM newvals
WHERE newvals.id = testtable.id;

INSERT INTO testtable
SELECT newvals.id, newvals.somedata
FROM newvals
LEFT OUTER JOIN testtable ON (testtable.id = newvals.id)
WHERE testtable.id IS NULL;

COMMIT;

সম্পর্কিত পড়া

কি হবে MERGE?

এসকিউএল-স্ট্যান্ডার্ডটিতে MERGEপ্রকৃত অর্থে বাজে সংজ্ঞা রয়েছে এবং প্রথমে কোনও টেবিল লক না করে সংরক্ষণের জন্য উপযুক্ত নয়।

এটি ডেটা মার্জ করার জন্য একটি সত্যই কার্যকর ওএলএপি বিবৃতি, তবে এটি সম্মতি-সুরক্ষার জন্য কোনও কার্যকর সমাধান নয়। অন্যান্য ডিবিএমএস ব্যবহারের MERGEজন্য ব্যবহারকারীর জন্য প্রচুর পরামর্শ রয়েছে তবে এটি আসলে ভুল।

অন্যান্য ডিবি:


বাল্ক আপ্রেসে, ইনসার্ট ফিল্টার করার পরিবর্তে নতুনদের থেকে মুছে ফেলার কি কোনও মূল্য আছে? উদাহরণস্বরূপ আপডেট হিসাবে (আপডেট করা হচ্ছে ... newvals.id প্রত্যাবর্তন করা হচ্ছে) নতুন নতুন থেকে আপডেট মুছে ফেলুন newvals.id = আপডেট.id, তারপরে একটি খালি INSERT INTO টেস্টেবল নির্বাচন করুন * নতুন থেকে? এর সাথে আমার ধারণা: INSERT এ দু'বার ফিল্টার করার পরিবর্তে (যোগদানের জন্য / যেখানে এবং অনন্য প্রতিবন্ধকতার জন্য), আপডেট থেকে পুনরায় ব্যবহার করুন আপডেটটি, যা ইতিমধ্যে র্যামে রয়েছে, এবং এটি আরও ছোট হতে পারে। টেস্টেবলের তুলনায় কয়েকটি সারি মেলে এবং / অথবা নতুনগুলি যদি ছোট হয় তবে এটি একটি জয় হতে পারে।
গুন্নলাউগুর ব্রিইম

1
এখনও অমীমাংসিত সমস্যা রয়েছে এবং অন্যান্য বিক্রেতাদের জন্য এটি কী কাজ করে এবং কী করে না তা পরিষ্কার নয়। 1. পোস্টগ্র্রেস লুপিং সমাধানটি উল্লিখিত হিসাবে একাধিক অনন্য কীগুলির ক্ষেত্রে কাজ করে না। ২. মাইএসকিএলের জন্য অনুলিপি কী একাধিক অনন্য কীগুলির জন্যও কাজ করে না। ৩. মাইএসকিউএল, এসকিউএল সার্ভার এবং ওরাকল এর অন্যান্য সমাধানগুলি কি উপরে কাজ পোস্ট করেছে? এই ক্ষেত্রে ব্যতিক্রমগুলি কি সম্ভব এবং আমাদের কী লুপ করতে হবে?
ডান বি

@ উদ্যানব এটি পোস্টগ্র্রেএসকিউএল সম্পর্কে কেবল সত্য। কোনও ক্রস-বিক্রেতার সমাধান নেই। PostgreSQL এর সমাধান একাধিক সারিগুলির জন্য কাজ করে না, আপনাকে দুর্ভাগ্যক্রমে প্রতি সারিতে একটি করে লেনদেন করতে হবে। MERGEএসকিউএল সার্ভার এবং ওরাকল এর জন্য যে "সমাধানগুলি" ব্যবহার করা হয় সেগুলি ভুল এবং রেস শর্তগুলির প্রবণ, যেমন উপরে উল্লিখিত রয়েছে। সেগুলি কীভাবে পরিচালনা করতে হয় তা জানতে আপনাকে প্রতিটি ডিবিএমএসের বিশেষত খতিয়ে দেখতে হবে, আমি কেবল পোস্টগ্রাইএসকিউএল সম্পর্কিত পরামর্শ দিতে পারি। মূল সার্ভারে নেটিভ আপসেটের জন্য সমর্থন যুক্ত হয়ে গেলে পোস্টগ্রাইএসকিউএল-তে নিরাপদ মাল্টি-সারি উপস্থাপনা করার একমাত্র উপায় হ'ল।
ক্রেগ রিঞ্জার 31'15

এমনকি পোস্টগ্র্যাসকিউএল-র ক্ষেত্রেও সমাধানটি কার্যকর হয় না যেখানে কোনও টেবিলের একাধিক অনন্য কী রয়েছে (কেবলমাত্র একটি সারি আপডেট করে)। সেক্ষেত্রে আপনাকে কীটি আপডেট করা হচ্ছে তা নির্দিষ্ট করতে হবে। উদাহরণস্বরূপ jdbc ব্যবহার করে ক্রস-বিক্রেতার সমাধান থাকতে পারে।
ডান বি

2
পোস্টগ্র্রেস এখন ইউপিএসআরটি - git.postgresql.org/gitweb/…
ক্রিস

32

পোস্টগ্র্রেএসকিউএল-এর প্রাক-9.5 সংস্করণ সহ একক সন্নিবেশ সমস্যার জন্য অন্য একটি সমাধানে অবদান রাখার চেষ্টা করছি। ধারণাটি হ'ল প্রথমে সন্নিবেশ সম্পাদনের চেষ্টা করা, এবং রেকর্ডটি ইতিমধ্যে উপস্থিত থাকলে তা আপডেট করার জন্য:

do $$
begin 
  insert into testtable(id, somedata) values(2,'Joe');
exception when unique_violation then
  update testtable set somedata = 'Joe' where id = 2;
end $$;

মনে রাখবেন যে টেবিলের সারিগুলির কোনও মোছা না থাকলে কেবলমাত্র এই সমাধানটি প্রয়োগ করা যেতে পারে ।

আমি এই সমাধানটির দক্ষতা সম্পর্কে জানি না, তবে এটি আমার কাছে যথেষ্ট যুক্তিসঙ্গত বলে মনে হয়।


3
আপনাকে ধন্যবাদ, আমি ঠিক তাই খুঁজছিলাম এটি কেন খুঁজে পাওয়া এত কঠিন ছিল তা বুঝতে পারি না।
ইসাপির

3
হাঁ। এই সরলীকরণটি কোনও ডিলিট না থাকলে কেবল এবং শুধুমাত্র কাজ করে।
ক্রেগ রিঞ্জার

@ ক্রেইগ্রিঞ্জার আপনি কি ব্যাখ্যা করতে পারবেন যে যদি মুছে ফেলা হয় তবে ঠিক কী হবে?
turbanoff

@ তুরবানফ সন্নিবেশটি ব্যর্থ হতে পারে কারণ রেকর্ডটি ইতিমধ্যে রয়েছে, তারপরে এটি একই সাথে মুছে ফেলা হবে এবং আপডেটটি পরে শূন্য সারিগুলিকে প্রভাবিত করে কারণ সারিটি মোছা হয়েছিল।
ক্রেগ রিঞ্জার

পছন্দ করুন মোছা একইসাথে ঘটে । যদি এটি কাজ করে তবে বাইরে কী কী সম্ভব ? যদি মুছে ফেলা একই সাথে কাজ করে - তবে এটি আমাদের ব্লকের ঠিক পরে কার্যকর করা যেতে পারে। আমার কি বলার চেষ্টা করছি - যদি আমরা সমবর্তী মোছার আছে - তারপর এই কোড woks একই হিসাবে সঠিক পদ্ধতিতেinsert on update
turbanoff

28

insert ... on conflict ...( Pg 9.5+ ) এর জন্য কয়েকটি উদাহরণ এখানে দেওয়া হয়েছে :

  • সন্নিবেশ করুন, বিবাদে - কিছুই করবেন না
    insert into dummy(id, name, size) values(1, 'new_name', 3)
    on conflict do nothing;`  
  • সংঘর্ষে সন্নিবেশ করুন - আপডেট করুন , কলামের মাধ্যমে সংঘাতের লক্ষ্য নির্দিষ্ট করুন ।
    insert into dummy(id, name, size) values(1, 'new_name', 3)
    on conflict(id)
    do update set name = 'new_name', size = 3;  
  • সংঘাতের উপর সন্নিবেশ করুন - আপডেট করুন , সীমাবদ্ধ নামের মাধ্যমে দ্বন্দ্ব লক্ষ্য নির্দিষ্ট করুন ।
    insert into dummy(id, name, size) values(1, 'new_name', 3)
    on conflict on constraint dummy_pkey
    do update set name = 'new_name', size = 4;

দুর্দান্ত উত্তর - প্রশ্ন: কেন বা কোন পরিস্থিতিতে কলাম বা সীমাবদ্ধ নামের মাধ্যমে লক্ষ্য স্পেসিফিকেশন ব্যবহার করা উচিত? বিভিন্ন ব্যবহারের ক্ষেত্রে কোনও সুবিধা / অসুবিধা আছে কি?
নাথান বেনটন

1
@ নাথানবেন্টন আমার মনে হয় কমপক্ষে 2 টি পার্থক্য রয়েছে: (1) কলামের নাম প্রোগ্রামার দ্বারা নির্দিষ্ট করা হয়েছে, তবে সীমাবদ্ধতার নাম হয় প্রোগ্রামার দ্বারা নির্দিষ্ট করা যেতে পারে, বা টেবিল / কলামের নাম অনুসারে ডাটাবেস দ্বারা উত্পন্ন হতে পারে। (2) প্রতিটি কলামে একাধিক বাধা থাকতে পারে। এটি বলেছিল, কোনটি ব্যবহার করবেন তা চয়ন করা আপনার ক্ষেত্রে নির্ভর করে।
এরিক ওয়াং

8

পোস্টগ্র্রেস> = 9.5 এর জন্য এসকিউএএলএলএকচেমি উপগ্রহ

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

আমি গতকাল দিয়েছি এমন অন্য একটি উত্তর থেকে ক্রস পোস্টিং ( https://stackoverflow.com/a/44395983/2156909 )

এসকিউএলএলচেমি ON CONFLICTএখন দুটি পদ্ধতির সাহায্য করে on_conflict_do_update()এবং on_conflict_do_nothing():

নথি থেকে অনুলিপি করা:

from sqlalchemy.dialects.postgresql import insert

stmt = insert(my_table).values(user_email='a@b.com', data='inserted data')
stmt = stmt.on_conflict_do_update(
    index_elements=[my_table.c.user_email],
    index_where=my_table.c.user_email.like('%@gmail.com'),
    set_=dict(data=stmt.excluded.data)
    )
conn.execute(stmt)

http://docs.sqlalchemy.org/en/latest/dialects/postgresql.html?highlight=conflict#insert-on-conflict-upsert


4
পাইথন এবং এসকিউএএলএলচেমির প্রশ্নটিতে উল্লেখ করা হয়নি।
আলেকজান্ডার ইমেলিয়ানভ

আমি প্রায়শই আমার সমাধানগুলিতে পাইথন ব্যবহার করি। তবে আমি এসকিউএএলএলচেমির দিকে নজর রাখিনি (বা এটি সম্পর্কে অবগত ছিল)। এটি একটি মার্জিত বিকল্প বলে মনে হচ্ছে। ধন্যবাদ. যদি এটি চেক আউট হয় তবে আমি এটি আমার সংস্থার কাছে উপস্থাপন করব।
রবার্ট

3
WITH UPD AS (UPDATE TEST_TABLE SET SOME_DATA = 'Joe' WHERE ID = 2 
RETURNING ID),
INS AS (SELECT '2', 'Joe' WHERE NOT EXISTS (SELECT * FROM UPD))
INSERT INTO TEST_TABLE(ID, SOME_DATA) SELECT * FROM INS

Postgresql 9.3 এ পরীক্ষিত


@ ক্রেইগ্রারঞ্জার: আপনি কি এ বিষয়ে বিস্তারিত বলতে পারবেন? সিটি পারমাণবিক না?
parisni

2
@ পারিস্নি নং। প্রতিটি সিটিই শব্দটি নিজের লেখার সম্পাদন করলে তার নিজস্ব স্ন্যাপশট পায়। এছাড়াও সারিগুলিতে কোনও ধরণের প্রাকটিক লকিং করা হয়নি যা পাওয়া যায় নি তাই তারা এখনও অন্য একটি অধিবেশন দ্বারা একই সাথে তৈরি করা যেতে পারে। যদি আপনি SERIALIZABLEবিচ্ছিন্নতা ব্যবহার করেন তবে সিরিয়ালাইজেশন ব্যর্থতার সাথে আপনি যদি একটি গর্ভপাত বন্ধ করতে চান তবে অন্যথায় আপনি সম্ভবত একটি অনন্য লঙ্ঘন পেয়ে যাবেন। পুনরায় উদ্বেগ উপস্থাপন করবেন না, পুনর্নবীকরণ ভুল হবে। ব্যবহার INSERT ... ON CONFLICT ...। আপনার পোস্টগ্রিজ এসকিউএল যদি খুব পুরানো হয় তবে আপডেট করুন।
ক্রেগ রিঞ্জার

@ ক্রেইগরিঞ্জারটি INSERT ... ON CLONFLICT ...বাল্ক লোডিংয়ের উদ্দেশ্যে নয়। আপনার পোস্ট থেকে, LOCK TABLE testtable IN EXCLUSIVE MODE;একটি সিটিই এর মধ্যে থাকা পারমাণবিক জিনিসগুলি পাওয়ার জন্য একান্ত কার্যকরী। না?
parisni

@ প্যারিসনি এটি কি বাল্ক লোডিংয়ের উদ্দেশ্যে নয়? বল কে? postgresql.org/docs/current/sql-insert.html#SQL-ON-CONFLICT । অবশ্যই, উত্সাহের মতো আচরণ ছাড়াই বাল্ক লোডিংয়ের তুলনায় এটি অনেক ধীর গতির, তবে এটি সুস্পষ্ট এবং আপনি যা-ই করুন না কেন বিষয়টি হ'ল। এটি সাবট্রান্সেক্টস ব্যবহারের চেয়ে দ্রুততর, এটি অবশ্যই। দ্রুততম পন্থাটি হ'ল টার্গেট টেবিলটি লক করা হয় তবে অবশ্যই একটি insert ... where not exists ...বা অনুরূপ করুন।
ক্রেগ রিঞ্জার

1

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

প্রথমে আমদানি

import itertools as it

from functools import partial
from operator import itemgetter

from sqlalchemy.exc import IntegrityError
from app import session
from models import Posts

এখন একটি দম্পতি সাহায্যকারী কাজ

def chunk(content, chunksize=None):
    """Groups data into chunks each with (at most) `chunksize` items.
    https://stackoverflow.com/a/22919323/408556
    """
    if chunksize:
        i = iter(content)
        generator = (list(it.islice(i, chunksize)) for _ in it.count())
    else:
        generator = iter([content])

    return it.takewhile(bool, generator)


def gen_resources(records):
    """Yields a dictionary if the record's id already exists, a row object 
    otherwise.
    """
    ids = {item[0] for item in session.query(Posts.id)}

    for record in records:
        is_row = hasattr(record, 'to_dict')

        if is_row and record.id in ids:
            # It's a row but the id already exists, so we need to convert it 
            # to a dict that updates the existing record. Since it is duplicate,
            # also yield True
            yield record.to_dict(), True
        elif is_row:
            # It's a row and the id doesn't exist, so no conversion needed. 
            # Since it's not a duplicate, also yield False
            yield record, False
        elif record['id'] in ids:
            # It's a dict and the id already exists, so no conversion needed. 
            # Since it is duplicate, also yield True
            yield record, True
        else:
            # It's a dict and the id doesn't exist, so we need to convert it. 
            # Since it's not a duplicate, also yield False
            yield Posts(**record), False

এবং অবশেষে আপসার ফাংশন

def upsert(data, chunksize=None):
    for records in chunk(data, chunksize):
        resources = gen_resources(records)
        sorted_resources = sorted(resources, key=itemgetter(1))

        for dupe, group in it.groupby(sorted_resources, itemgetter(1)):
            items = [g[0] for g in group]

            if dupe:
                _upsert = partial(session.bulk_update_mappings, Posts)
            else:
                _upsert = session.add_all

            try:
                _upsert(items)
                session.commit()
            except IntegrityError:
                # A record was added or deleted after we checked, so retry
                # 
                # modify accordingly by adding additional exceptions, e.g.,
                # except (IntegrityError, ValidationError, ValueError)
                db.session.rollback()
                upsert(items)
            except Exception as e:
                # Some other error occurred so reduce chunksize to isolate the 
                # offending row(s)
                db.session.rollback()
                num_items = len(items)

                if num_items > 1:
                    upsert(items, num_items // 2)
                else:
                    print('Error adding record {}'.format(items[0]))

আপনি এটি কীভাবে ব্যবহার করেন তা এখানে

>>> data = [
...     {'id': 1, 'text': 'updated post1'}, 
...     {'id': 5, 'text': 'updated post5'}, 
...     {'id': 1000, 'text': 'new post1000'}]
... 
>>> upsert(data)

এতে যে সুবিধাটি এসে গেছে bulk_save_objectsতা হ'ল এটি সম্পর্ক, ত্রুটি পরীক্ষা করা ইত্যাদি etcোকাতে ( বাল্ক অপারেশনগুলির বিপরীতে ) পরিচালনা করতে পারে


এটি আমার কাছেও ভুল দেখাচ্ছে। আপনি যদি আইডিগুলির তালিকা সংগ্রহের পরে কোনও একযোগে সেশন একটি সারি প্রবেশ করান তবে কী হবে? নাকি একটি মুছে দেয়?
ক্রেগ রিঞ্জার ২ '

ভাল পয়েন্ট @ ক্রেইগ্রারঞ্জার আমি এর অনুরূপ কিছু করি তবে কেবল 1 টি অধিবেশন কাজ সম্পাদন করে। একাধিক সেশন পরিচালনা করার সর্বোত্তম উপায় কী? একটি লেনদেন সম্ভবত?
27:37

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

পছন্দ করুন অন্যান্য বৈধতা ব্যর্থতার কারণে আমি আসলে আমার নিজের ক্ষেত্রে আবার চেষ্টা করেছিলাম lo আমি এই উত্তরটি সেই অনুযায়ী আপডেট করব।
রেবুনো
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.