এসকিউএলাইট আপ্সার্ট / আপডেট বা ইনসার্ট


105

এসকিউএলাইট ডাটাবেসটির বিপরীতে আমার ইউপিএসআরটি / ইনসার্ট বা আপডেট করা উচিত।

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

এই টেবিল হবে:

প্লেয়ার - (আইডিতে প্রাথমিক কী, ব্যবহারকারীর নাম অনন্য)

|  id   | user_name |  age   |
------------------------------
|  1982 |   johnny  |  23    |
|  1983 |   steven  |  29    |
|  1984 |   pepee   |  40    |

উত্তর:


60

এটি একটি দেরী উত্তর। স্কুইলেট ৩.২৪.০.০ থেকে শুরু করে, জুন ৪, ২০১ on এ প্রকাশিত, অবশেষে পোস্টগ্রিসএসকিউএল সিনট্যাক্সের নিম্নলিখিত ইউপিএসইআরটি ধারাটির জন্য সমর্থন রয়েছে ।

INSERT INTO players (user_name, age)
  VALUES('steven', 32) 
  ON CONFLICT(user_name) 
  DO UPDATE SET age=excluded.age;

দ্রষ্টব্য: ৩.২৪.০.০ এর আগে এসকিউএলাইটের একটি সংস্করণ ব্যবহার করার জন্য, দয়া করে নীচে এই উত্তরটি রেফারেন্স করুন (আমার দ্বারা পোস্ট করা, @ মার্কিআইভি)।

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


আপাতত, উবুন্টু সংগ্রহস্থলের কোনও রিলিজ নেই।
bl79

কেন আমি এটিকে অ্যান্ড্রয়েডে ব্যবহার করতে পারি না? চেষ্টা করেছি db.execSQL("insert into bla(id,name) values (?,?) on conflict(id) do update set name=?")। আমাকে "অন" শব্দটিতে একটি বাক্য গঠন ত্রুটি দেয়
বাসটিয়ান ভয়েগট

4
@ বাস্টিয়ানভিওগ্ট কারণ অ্যান্ড্রয়েডের বিভিন্ন সংস্করণে ইনস্টল করা এসকিউএলটি 3 লাইব্রেরিগুলি 3.24.0 এর চেয়ে পুরানো। দেখুন: বিকাশকারী.অ্যান্ড্রয়েড .com/references/android/datedia/sqlite/… দুঃখের বিষয়, অ্যান্ড্রয়েড বা আইওএস এ আপনার এসকিউএল 3 (বা অন্য কোনও সিস্টেম লাইব্রেরি) এর একটি নতুন বৈশিষ্ট্য প্রয়োজন, আপনার নিজের এসকিউএলাইটের একটি নির্দিষ্ট সংস্করণ বান্ডিল করতে হবে সিস্টেম ইনস্টল করা পরিবর্তে অ্যাপ্লিকেশন।
prapin

ইউপিএসআরটি পরিবর্তে, এটি প্রথমে sertোকানোর চেষ্টা করার পরে এটি কোনও ইন্ডেটের বেশি নয়? ;)
মার্ক এ। ডোনোহো

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

108

প্রশ্নোত্তর স্টাইল

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


বিকল্প 1: আপনি সারি মুছে ফেলতে পারবেন

অন্য কথায়, আপনার কাছে বিদেশী কী নেই, বা যদি আপনার কাছে থাকে তবে আপনার এসকিউএল ইঞ্জিনটি এমনভাবে কনফিগার করা আছে যাতে কোনও সততা ব্যতিক্রম না থাকে are যাওয়ার উপায় হ'ল INSERT or REPLACE । আপনি যদি এমন কোনও প্লেয়ার সন্নিবেশ / আপডেট করার চেষ্টা করছেন যাঁর আইডি ইতিমধ্যে বিদ্যমান রয়েছে, এসকিউএলাইট ইঞ্জিন সেই সারিটি মুছে ফেলবে এবং আপনার সরবরাহ করা ডেটা sertোকাবে। এখন প্রশ্ন আসে: পুরানো আইডি যুক্ত রাখতে কী করতে হবে?

ধরা যাক আমরা ব্যবহারকারীর নাম = 'স্টিভেন' এবং বয়স = 32 সহ ইউপিএসআরটি করতে চাই ।

এই কোডটি দেখুন:

INSERT INTO players (id, name, age)

VALUES (
    coalesce((select id from players where user_name='steven'),
             (select max(id) from drawings) + 1),
    32)

কৌশলটি একত্রিত হয়। এটি 'স্টিভেন' ব্যবহারকারীর আইডি যদি থাকে তবে তা ফেরত দেয় এবং অন্যথায় এটি নতুন নতুন আইডি প্রদান করে।


বিকল্প 2: আপনি সারিটি মোছার সামর্থ্য রাখতে পারবেন না

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

সুতরাং, আমি প্রথমে একটি আইএফ ক্লজটি ভেবেছিলাম তবে এসকিউএলাইটের কেবলমাত্র CASE রয়েছে । আর এই ক্ষেত্রে ব্যবহার করা যাবে না (বা অন্তত আমি এটি পরিচালনা না) এক সম্পাদন করতে আপডেট করেন (প্লেয়ার থেকে আইডি নির্বাচন যেখানে USER_NAME = 'স্টিভেন') বিদ্যমান ক্যোয়ারী, এবং ঢোকান যদি না করেনি। যাও না।

এবং তারপরে, অবশেষে আমি সাফল্যের সাথে নিষ্ঠুর শক্তি ব্যবহার করেছি। যুক্তিটি হ'ল, আপনি যে প্রতিটি ইউপিএসআরটি সম্পাদন করতে চান তার জন্য প্রথমে একটি INSERT বা IGNORE কার্যকর করুন যাতে আমাদের ব্যবহারকারীর সাথে সারি রয়েছে কিনা তা নিশ্চিত করুন এবং তারপরে আপনি যে তথ্যটি inোকানোর চেষ্টা করেছিলেন ঠিক সেই একই ডেটা দিয়ে একটি আপডেট আপডেট জিজ্ঞাসা করুন।

আগের মতো একই ডেটা: ব্যবহারকারীর নাম = 'স্টিভেন' এবং বয়স = 32।

-- make sure it exists
INSERT OR IGNORE INTO players (user_name, age) VALUES ('steven', 32); 

-- make sure it has the right data
UPDATE players SET user_name='steven', age=32 WHERE user_name='steven'; 

এবং যে সব!

সম্পাদনা

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

-- Try to update any existing row
UPDATE players SET age=32 WHERE user_name='steven';

-- Make sure it exists
INSERT OR IGNORE INTO players (user_name, age) VALUES ('steven', 32); 

10
Ditto ... অপশন 2 দুর্দান্ত। ব্যতীত, আমি এটি অন্য উপায়ে করেছি: একটি আপডেট চেষ্টা করে দেখুন, সারিগুলি>> 0 ব্যবহার করেছেন কিনা তা যাচাই করুন, না হলে একটি সন্নিবেশ করান।
টম স্পেন্সার

এটিও বেশ ভাল পদ্ধতির, কেবলমাত্র ছোট্ট ব্যর্থতা হ'ল "উপস্থাপনার" জন্য আপনার কাছে কেবল একটি এসকিউএল নেই।
বগুসাচ

4
শেষ কোডের নমুনায় আপডেট বিবৃতিতে আপনার পুনরায় সেট করা ব্যবহারকারী_নাম দরকার নেই। বয়স নির্ধারণের জন্য এটি যথেষ্ট।
সার্জ স্টেটসুক

72

এখানে এমন একটি দৃষ্টিভঙ্গি রয়েছে যার জন্য ব্রুট-ফোর্স 'এড়ানো' প্রয়োজন হয় না যা কেবলমাত্র কোনও মূল লঙ্ঘন হলে কাজ করবে। আপনি আপডেটে নির্দিষ্ট করা কোনও শর্তের উপর ভিত্তি করে এই উপায়টি কাজ করে ।

এটা চেষ্টা কর...

-- Try to update any existing row
UPDATE players
SET age=32
WHERE user_name='steven';

-- If no update happened (i.e. the row didn't exist) then insert one
INSERT INTO players (user_name, age)
SELECT 'steven', 32
WHERE (Select Changes() = 0);

কিভাবে এটা কাজ করে

এখানে 'ম্যাজিক সস' ক্লজটিতে ব্যবহার Changes()করছে WhereChanges()শেষ অপারেশন দ্বারা প্রভাবিত সারিগুলির সংখ্যা উপস্থাপন করে, যা এই ক্ষেত্রে আপডেট।

উপরের উদাহরণে, যদি আপডেট থেকে কোনও পরিবর্তন না হয় (যেমন রেকর্ডটির অস্তিত্ব নেই) তবে Changes()= 0 সুতরাং বিবৃতিতে থাকা Whereধারাটি Insertসত্যকে মূল্যায়ন করে এবং নির্দিষ্ট ডেটা সহ একটি নতুন সারি প্রবেশ করানো হয়।

তাহলে Update করেনি আপডেট একটি বিদ্যমান সারি, তারপর Changes()= 1 (বা আরো সঠিকভাবে, যদি একাধিক সারি আপডেট করা হয়েছে শূন্য নয়), তাই এ 'কোথায় দফা Insertএখন মিথ্যা মূল্যায়ণ এবং এইভাবে কোন সন্নিবেশ করা হবে।

এর সৌন্দর্যে কোনও নিষ্ঠুর শক্তি প্রয়োজন নেই, অযৌক্তিকভাবে মুছে ফেলা হয় না, তারপরে ডেটা পুনরায় সন্নিবেশ করা হয় যার ফলস্বরূপ বিদেশী-মূল সম্পর্কের ডাউনস্ট্রিম কীগুলিতে গণ্ডগোলের সৃষ্টি হতে পারে।

অতিরিক্তভাবে, যেহেতু এটি কেবলমাত্র একটি স্ট্যান্ডার্ড Whereধারা, এটি কেবলমাত্র মূল লঙ্ঘন নয়, আপনি সংজ্ঞায়িত কোনও কিছুর উপর ভিত্তি করে তৈরি করতে পারেন। তেমনি, আপনি Changes()যে কোনও জায়গাতেই প্রয়োজন / প্রয়োজনের যে কোনও মত প্রকাশের অনুমতি রয়েছে এর সাথে আপনি সংমিশ্রণে ব্যবহার করতে পারেন ।


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

@ মার্কিআইভি এবং যদি দুটি আইটেম অবশ্যই আপডেট করা বা sertedোকানো হয় তবে কী হবে? উদাহরণস্বরূপ, প্রথমগুলি আপডেট হয়েছিল এবং দ্বিতীয়টি বিদ্যমান নেই। এই ক্ষেত্রে মিথ্যাChanges() = 0 ফিরে আসবে এবং দুটি সারি INSERT বা REPLACE করবে
Andriy Antonov

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

সবচেয়ে খারাপ বিষয় হ'ল যদি সারিটি বিদ্যমান থাকে তবে সারিটি পরিবর্তন হয়েছে কিনা তা নির্বিশেষে আপডেট পদ্ধতিটি কার্যকর করতে হবে।
জিমি

4
কেন এটি একটি খারাপ জিনিস? এবং যদি ডেটা পরিবর্তন না হয় তবে আপনি কেন UPSERTপ্রথম স্থানে কল করছেন? তবে তারপরেও, আপডেটটি হওয়া খুব ভাল বিষয়, সেটিং করা Changes=1বা অন্যথায় INSERTবিবৃতিটি ভুলভাবে আগুন ধরিয়ে দেয়, যা আপনি এটি চান না।
মার্ক এ। ডোনোহো

25

সমস্ত উপস্থাপিত উত্তরগুলির সমস্যার সাথে এটি ট্রিগারগুলি (এবং সম্ভবত অন্যান্য পার্শ্ব প্রতিক্রিয়া) অ্যাকাউন্টে নেওয়ার সম্পূর্ণ অভাব রয়েছে। সমাধান মত

INSERT OR IGNORE ...
UPDATE ...

যখন সারিটির অস্তিত্ব না থাকে তখন মৃত্যুদন্ড কার্যকর করা (সন্নিবেশের জন্য এবং তারপরে আপডেটের জন্য) উভয় দিকে পরিচালিত করে।

সঠিক সমাধান হয়

UPDATE OR IGNORE ...
INSERT OR IGNORE ...

সেক্ষেত্রে কেবলমাত্র একটি বিবৃতি কার্যকর করা হয় (যখন সারি উপস্থিত থাকে বা না থাকে)।


4
আমি আপনার পয়েন্ট দেখুন. আমি আমার প্রশ্ন আপডেট করব। যাইহোক, আমি কেন UPDATE OR IGNOREপ্রয়োজনীয় তা জানি না , কারণ কোনও সারি পাওয়া না গেলে আপডেট করা ক্রাশ হবে না।
বিগুসাচ

4
পাঠযোগ্যতা? অ্যান্ডির কোডটি এক নজরে কী করছে তা আমি দেখতে পাচ্ছি। তোমার বগুসাচ আমাকে বের করার জন্য এক মিনিট অধ্যয়ন করতে হয়েছিল।
ব্র্যান্ডন

6

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

UPDATE players SET user_name="gil", age=32 WHERE user_name='george'; 
SELECT changes();

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

INSERT INTO players (user_name, age) VALUES ('gil', 32); 

এটি @ ফিজনুল তার মন্তব্যে যে প্রস্তাব করেছে তার সমতুল্য (যদিও আমি তার সমাধানে যাব)। এটি ঠিক আছে এবং আসলে সূক্ষ্মভাবে কাজ করে, তবে আপনার কাছে কোনও অনন্য এসকিউএল স্টেটমেন্ট নেই। ইউপিএসআরটি পিকে বা অন্যান্য অনন্য কীগুলির উপর ভিত্তি করে তৈরি করা আমার কাছে বোধগম্য নয়।
বিগুসাচ

4

আপনি নিজের ব্যবহারকারীর নাম অনন্য সীমাবদ্ধতার জন্য কেবল একটি অনুলিপি রিপ্লেস ক্লজ যুক্ত করতে পারেন এবং তারপরে কেবল সন্নিবেশ করুন, কোনও বিরোধের ক্ষেত্রে কী করবেন তা নির্ধারণের জন্য এটি এসকিউএলাইটে রেখে leaving দেখুন: https://sqlite.org/lang_conflict.html

ট্রিগারগুলি মুছে ফেলার বিষয়ে বাক্যটিও নোট করুন: যখন বিরোধের সমাধান বিরোধিতা যখন সীমাবদ্ধতা মেটানোর জন্য সারিগুলি সরিয়ে দেয়, তখন ট্রিগারগুলি আগুন মুছুন এবং যদি কেবল পুনরাবৃত্ত ট্রিগারগুলি সক্ষম করা হয়।


2

বিকল্প 1: সন্নিবেশ করুন -> আপডেট করুন

আপনি যদি উভয়ই এড়াতে changes()=0এবং INSERT OR IGNOREএমনকি সারিটি মোছার সামর্থ্য নাও রাখতে চান - আপনি এই যুক্তিটি ব্যবহার করতে পারেন;

প্রথমে sertোকান (উপস্থিত না থাকলে) এবং তারপরে অনন্য কী দিয়ে ফিল্টার করে আপডেট করুন

উদাহরণ

-- Table structure
CREATE TABLE players (
    id        INTEGER       PRIMARY KEY AUTOINCREMENT,
    user_name VARCHAR (255) NOT NULL
                            UNIQUE,
    age       INTEGER       NOT NULL
);

-- Insert if NOT exists
INSERT INTO players (user_name, age)
SELECT 'johnny', 20
WHERE NOT EXISTS (SELECT 1 FROM players WHERE user_name='johnny' AND age=20);

-- Update (will affect row, only if found)
-- no point to update user_name to 'johnny' since it's unique, and we filter by it as well
UPDATE players 
SET age=20 
WHERE user_name='johnny';

ট্রিগার সম্পর্কে

বিজ্ঞপ্তি: কোন ট্রিগারগুলি ডাকা হচ্ছে তা দেখার জন্য আমি এটি পরীক্ষা করে দেখিনি, তবে আমি নিম্নলিখিতটি ধরে নিয়েছি :

সারি উপস্থিত না থাকলে

  • প্রবেশের আগে
  • ইনস্টল অফ ব্যবহার করুন S
  • প্রবেশের পরে
  • আপডেটের আগে
  • ইনস্টল অফ ব্যবহার করে আপডেট করুন
  • আপডেটের পরে

সারি যদি বিদ্যমান থাকে

  • আপডেটের আগে
  • ইনস্টল অফ ব্যবহার করে আপডেট করুন
  • আপডেটের পরে

বিকল্প 2: সন্নিবেশ করুন বা প্রতিস্থাপন করুন - আপনার নিজের আইডি রাখুন

এইভাবে আপনি একটি একক এসকিউএল কমান্ড রাখতে পারেন

-- Table structure
CREATE TABLE players (
    id        INTEGER       PRIMARY KEY AUTOINCREMENT,
    user_name VARCHAR (255) NOT NULL
                            UNIQUE,
    age       INTEGER       NOT NULL
);

-- Single command to insert or update
INSERT OR REPLACE INTO players 
(id, user_name, age) 
VALUES ((SELECT id from players WHERE user_name='johnny' AND age=20),
        'johnny',
        20);

সম্পাদনা করুন: বিকল্প 2 যোগ করা হয়েছে।

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