বিদেশী কী রয়েছে এমন একটি সারি আমি কীভাবে সন্নিবেশ করব?


54

PostgreSQL v9.1 ব্যবহার করে। আমার কাছে নিম্নলিখিত সারণী রয়েছে:

CREATE TABLE foo
(
    id BIGSERIAL     NOT NULL UNIQUE PRIMARY KEY,
    type VARCHAR(60) NOT NULL UNIQUE
);

CREATE TABLE bar
(
    id BIGSERIAL NOT NULL UNIQUE PRIMARY KEY,
    description VARCHAR(40) NOT NULL UNIQUE,
    foo_id BIGINT NOT NULL REFERENCES foo ON DELETE RESTRICT
);

বলুন প্রথম টেবিলটি fooএভাবেই জনপ্রিয় হয়েছে:

INSERT INTO foo (type) VALUES
    ( 'red' ),
    ( 'green' ),
    ( 'blue' );

সারণিটি barউল্লেখ করে সহজেই সারিগুলি সন্নিবেশ করার কোনও উপায় আছে কি foo? বা আমার দুটি ধাপে এটি করা উচিত, প্রথমে fooআমার পছন্দটিটি অনুসন্ধান করে এবং তারপরে একটি নতুন সারি bar? োকানো ?

সিউডো-কোডের উদাহরণ এখানে দেখানো হয়েছে যে আমি কী করছিলাম আশা করা হয়েছিল:

INSERT INTO bar (description, foo_id) VALUES
    ( 'testing',     SELECT id from foo WHERE type='blue' ),
    ( 'another row', SELECT id from foo WHERE type='red'  );

উত্তর:


67

আপনার সিনট্যাক্সটি প্রায় ভাল, সাবকোয়ারির চারপাশে কিছু প্রথম বন্ধনী প্রয়োজন এবং এটি কাজ করবে:

INSERT INTO bar (description, foo_id) VALUES
    ( 'testing',     (SELECT id from foo WHERE type='blue') ),
    ( 'another row', (SELECT id from foo WHERE type='red' ) );

এসকিউএল-ফিডল পরীক্ষিত

সংক্ষিপ্ত বাক্য গঠন সহ অন্য উপায়, আপনার যদি সন্নিবেশ করার জন্য অনেক মান থাকে:

WITH ins (description, type) AS
( VALUES
    ( 'more testing',   'blue') ,
    ( 'yet another row', 'green' )
)  
INSERT INTO bar
   (description, foo_id) 
SELECT 
    ins.description, foo.id
FROM 
  foo JOIN ins
    ON ins.type = foo.type ;

এটি কয়েকবার পড়ার চেষ্টা করেছে, তবে আমি এখন বুঝতে পেরেছি যে আপনি সরবরাহ করেছেন provided আমি এটা পছন্দ করি. সিস্টেমটি প্রথম যখন উপস্থিত হবে তখন মুষ্টিমেয় জ্ঞাত মানগুলির সাথে আমার ডাটাবেস বুটস্ট্র্যাপ করতে এটি ব্যবহার করুন।
স্টাফেন

37

সরল INSERT

INSERT INTO bar (description, foo_id)
SELECT val.description, f.id
FROM  (
   VALUES
      (text 'testing', text 'blue')  -- explicit type declaration; see below
    , ('another row', 'red' )
    , ('new row1'   , 'purple')      -- purple does not exist in foo, yet
    , ('new row2'   , 'purple')
   ) val (description, type)
LEFT   JOIN foo f USING (type);
  • LEFT [OUTER] JOINপরিবর্তে এর ব্যবহারের [INNER] JOINঅর্থ val হ'ল কোনও মিল পাওয়া না গেলে সারিগুলি সরিয়ে ফেলা হয় নাfoo । পরিবর্তে, NULLজন্য প্রবেশ করানো হয় foo_id

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

  • idকলামের নামটি হ'ল বিস্তৃত ছড়িয়ে পড়া বিরোধী-নিদর্শন। foo_idএবং bar_idবর্ণনামূলক কিছু হতে হবে । একগুচ্ছ টেবিলগুলিতে যোগদান করার সময়, আপনি নামযুক্ত একাধিক কলাম দিয়ে শেষ করবেন id...

  • সরল textবা এর varcharপরিবর্তে বিবেচনা করুন varchar(n)। আপনার যদি সত্যিই দৈর্ঘ্যের সীমাবদ্ধতা আরোপ করতে হয় তবে একটি CHECKসীমাবদ্ধতা যুক্ত করুন:

  • আপনার স্পষ্টত ধরণের ক্যাসেট যুক্ত করার প্রয়োজন হতে পারে। যেহেতু VALUESঅভিব্যক্তিটি কোনও টেবিলের সাথে সরাসরি সংযুক্ত থাকে না ( INSERT ... VALUES ...প্রকারের মতো ), প্রকারগুলি উত্পন্ন করা যায় না এবং স্পষ্টত ধরণের ঘোষণা ছাড়াই ডিফল্ট ডেটা টাইপ ব্যবহৃত হয়, যা সব ক্ষেত্রেই কাজ না করে। এটি প্রথম সারিতে করা যথেষ্ট, বাকিগুলি লাইনে পড়বে।

একই সময়ে এফকে সারি নিখোঁজ করুন

আপনি যদি fooএকটি একক এসকিউএল স্টেটমেন্টে ফ্লাইতে অ-অস্তিত্বপ্রাপ্ত এন্ট্রিগুলি তৈরি করতে চান তবে সিটিই হ'ল সহায়ক

WITH sel AS (
   SELECT val.description, val.type, f.id AS foo_id
   FROM  (
      VALUES
         (text 'testing', text 'blue')
       , ('another row', 'red'   )
       , ('new row1'   , 'purple')
       , ('new row2'   , 'purple')
      ) val (description, type)
   LEFT   JOIN foo f USING (type)
   )
, ins AS ( 
   INSERT INTO foo (type)
   SELECT DISTINCT type FROM sel WHERE foo_id IS NULL
   RETURNING id AS foo_id, type
   )
INSERT INTO bar (description, foo_id)
SELECT sel.description, COALESCE(sel.foo_id, ins.foo_id)
FROM   sel
LEFT   JOIN ins USING (type);

সন্নিবেশ করানোর জন্য দুটি নতুন ডামি সারি নোট করুন। দুটিই বেগুনি , যা fooএখনও নেই। প্রথম বিবৃতিতে প্রয়োজনীয়তা বর্ণনা করার জন্য দুটি সারি ।DISTINCTINSERT

ধাপে ধাপে ব্যাখ্যা

  1. 1 ম সিটিই selইনপুট ডেটারের বহুগুণ সারি সরবরাহ করে। Subquery valসঙ্গে VALUESঅভিব্যক্তি উৎস হিসেবে একটি টেবিল বা subquery সঙ্গে প্রতিস্থাপিত হতে পারে। অবিলম্বে LEFT JOINকরার জন্য fooসংযোজন করতে foo_idজন্য পূর্ব বিদ্যমান typeসারি। অন্যান্য সমস্ত সারি foo_id IS NULLএইভাবে পায়।

  2. ২ য় সিটিই স্বতন্ত্র নতুন প্রকারগুলি ( ) insসন্নিবেশ করায় এবং সদ্য উত্পন্ন উত্সাহ দেয় - একসাথে সারি সন্নিবেশ করানোর জন্য যোগ দেয়।foo_id IS NULLfoofoo_idtype

  3. চূড়ান্ত INSERTবাহিরটি এখন প্রতিটি সারির জন্য একটি foo.id সন্নিবেশ করতে পারে: টাইপটি প্রাক-বিদ্যমান, অথবা এটি পদক্ষেপ 2 এ সন্নিবেশ করা হয়েছিল।

কড়া কথায় বলতে গেলে উভয় সন্নিবেশ "সমান্তরালভাবে" ঘটে, তবে এটি যেহেতু এটি একক বিবৃতি, তাই ডিফল্ট FOREIGN KEYসীমাবদ্ধতাগুলি অভিযোগ করবে না। ডিফল্টরূপে বিবৃতি শেষে রেফারেন্সিয়াল অখণ্ডতা প্রয়োগ করা হয়।

পোস্টগ্রিসের জন্য এসকিউএল ফিডল 9.3। (9.1 এ একই কাজ করে।)

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

বারবার ব্যবহারের জন্য ফাংশন

বারবার ব্যবহারের জন্য আমি একটি এসকিউএল ফাংশন তৈরি করব যা প্যারামিটার হিসাবে রেকর্ডগুলির একটি অ্যারে নেয় এবং এক্সপ্রেশনটির unnest(param)জায়গায় ব্যবহার VALUESকরে।

অথবা, যদি রেকর্ডগুলির অ্যারেগুলির সিনট্যাক্স আপনার পক্ষে খুব অগোছালো হয় তবে পরামিতি হিসাবে কমা-বিচ্ছিন্ন স্ট্রিংটি ব্যবহার করুন _param। ফর্ম উদাহরণস্বরূপ:

'description1,type1;description2,type2;description3,type3'

তারপরে VALUESউপরের বিবৃতিতে ভাবটি প্রতিস্থাপন করতে এটি ব্যবহার করুন :

SELECT split_part(x, ',', 1) AS description
       split_part(x, ',', 2) AS type
FROM unnest(string_to_array(_param, ';')) x;


পোস্টগ্রিস 9.5 এ ইউপিএসআরটির সাথে ফাংশন

প্যারামিটার পাস করার জন্য একটি কাস্টম সারি প্রকার তৈরি করুন। আমরা এটি না করেই করতে পারি, তবে এটি সহজ:

CREATE TYPE foobar AS (description text, type text);

ফাংশন:

CREATE OR REPLACE FUNCTION f_insert_foobar(VARIADIC _val foobar[])
  RETURNS void AS
$func$
   WITH val AS (SELECT * FROM unnest(_val))    -- well-known row type
   ,    ins AS ( 
      INSERT INTO foo AS f (type)
      SELECT DISTINCT v.type                   -- DISTINCT!
      FROM   val v
      ON     CONFLICT(type) DO UPDATE          -- type already exists
      SET    type = excluded.type WHERE FALSE  -- never executed, but lock rows
      RETURNING f.type, f.id
      )
   INSERT INTO bar AS b (description, foo_id)
   SELECT v.description, COALESCE(f.id, i.id)  -- assuming most types pre-exist
   FROM        val v
   LEFT   JOIN foo f USING (type)              -- already existed
   LEFT   JOIN ins i USING (type)              -- newly inserted
   ON     CONFLICT (description) DO UPDATE     -- description already exists
   SET    foo_id = excluded.foo_id             -- real UPSERT this time
   WHERE  b.foo_id IS DISTINCT FROM excluded.foo_id  -- only if actually changed
$func$  LANGUAGE sql;

কল করুন:

SELECT f_insert_foobar(
     '(testing,blue)'
   , '(another row,red)'
   , '(new row1,purple)'
   , '(new row2,purple)'
   , '("with,comma",green)'  -- added to demonstrate row syntax
   );

একযোগে লেনদেন সহ পরিবেশের জন্য দ্রুত এবং রক-সলিড।

উপরের প্রশ্নগুলি ছাড়াও, এটি ...

  • ... প্রযোজ্য SELECTবা INSERTচালু foo: typeএফকে টেবিলের মধ্যে বিদ্যমান না এমন যে কোনও এখনও .োকানো হয়। ধরে নিচ্ছি বেশিরভাগ ধরণের প্রাক-অস্তিত্ব রয়েছে। একেবারে নিশ্চিত হয়ে ও বর্ণের পরিস্থিতি বাতিল করার জন্য, আমাদের বিদ্যমান বিদ্যমান সারিগুলি লক করা আছে (যাতে সমবর্তী লেনদেন হস্তক্ষেপ করতে না পারে)। যদি এটি আপনার ক্ষেত্রে খুব অসতর্ক থাকে তবে আপনি প্রতিস্থাপন করতে পারেন:

      ON     CONFLICT(type) DO UPDATE          -- type already exists
      SET    type = excluded.type WHERE FALSE  -- never executed, but lock rows

    সঙ্গে

      ON     CONFLICT(type) DO NOTHING
  • ... প্রযোজ্য INSERTবা UPDATE(সত্য "ইউপিএসআরটি") চালু bar: descriptionইতিমধ্যে বিদ্যমান থাকলে typeএটি আপডেট হয়:

      ON     CONFLICT (description) DO UPDATE     -- description already exists
      SET    foo_id = excluded.foo_id             -- real UPSERT this time
      WHERE  b.foo_id IS DISTINCT FROM excluded.foo_id  -- only if actually changed

    তবে শুধুমাত্র যদি typeআসলে পরিবর্তন হয়:

  • ... একটি VARIADICপ্যারামিটার সহ মান হিসাবে পরিচিত সারি প্রকারগুলি পাস করে । সর্বোচ্চ 100 পরামিতি নোট করুন! তুলনা করা:

    একাধিক সারি পাস করার আরও অনেক উপায় আছে ...

সম্পর্কিত:


আপনার INSERT missing FK rows at the same timeউদাহরণস্বরূপ, কোনও লেনদেনের ক্ষেত্রে এটি এসকিউএল সার্ভারে জাতি অবস্থার ঝুঁকি হ্রাস করবে?
উপাদান 11

1
@ এলিমেন্ট 11: উত্তর পোস্টগ্রিসের জন্য, তবে যেহেতু আমরা একটি একক এসকিউএল কমান্ডের কথা বলছি , এটি কোনও ক্ষেত্রেই এটি একক লেনদেন। এটি একটি বড় লেনদেনের মধ্যে কার্যকর করা কেবল সম্ভাব্য রেস শর্তগুলির জন্য সময় উইন্ডোকে বাড়িয়ে তুলবে । এসকিউএল সার্ভারের ক্ষেত্রে: ডেটা-সংশোধনকারী সিটিই মোটেও সমর্থিত নয় (কেবলমাত্র SELECTএকটি ধারাতে WITH)। সূত্র: এমএস ডকুমেন্টেশন
এরউইন ব্র্যান্ডসটেটার

1
এছাড়াও আপনি সাথে এটা করতে পারেন INSERT ... RETURNING \gsetমধ্যে psqlতারপর psql যেমন ফিরে মান ব্যবহার :'variables', কিন্তু এই শুধুমাত্র একক সারি টিপে জন্য কাজ করে।
ক্রেগ রিঞ্জার

@ এরউইন ব্র্যান্ডস্টেটর এটি দুর্দান্ত, তবে এগুলি বোঝার জন্য আমি বোধহয় নতুন, আপনি কীভাবে এটি কাজ করে তা ব্যাখ্যা করে "একই সাথে INSERT এফ কে সারি অনুপস্থিত" -এ কিছু মন্তব্য যুক্ত করতে পারেন? এছাড়াও, এসকিউএলফিডাল কাজের উদাহরণগুলির জন্য ধন্যবাদ!
গ্ল্যালেন

@ গ্ল্যালেন: আমি ধাপে ধাপে ব্যাখ্যা যুক্ত করেছি। সম্পর্কিত আরও অনেক লিঙ্ক এবং আরও ব্যাখ্যা সহ ম্যানুয়াল রয়েছে। আপনি প্রয়োজন বুঝতে কি ক্যোয়ারী করে অথবা আপনি আপনার মাথার উপর হতে পারে।
এরউইন ব্র্যান্ডসটেটার

4

খুঁজে দেখো. এগুলি বারে inোকানোর জন্য আপনার মূলত foo id এর দরকার।

নির্দিষ্ট পোস্টগ্রাগ নয়, বিটিডব্লিউ। (এবং আপনি এটির মতো ট্যাগ করেননি) - সাধারণত এসকিউএল কীভাবে এটি কাজ করে। এখানে কোনও শর্টকাট নেই।

অ্যাপ্লিকেশন অনুসারে, আপনার কাছে স্মৃতিতে foo আইটেমগুলির একটি ক্যাশে থাকতে পারে। আমার টেবিলগুলিতে প্রায় 3 টি পর্যন্ত অনন্য ক্ষেত্র থাকে:

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

উদাহরণ:

  • অ্যাকাউন্ট (একটি ব্যবসায়িক প্রয়োগে) -> আইডি বিদেশী কীগুলির জন্য ব্যবহৃত একটি অন্তর্গত। -> আইডেন্টিফায়ার একটি গাইড এবং ওয়েব পোর্টাল ইত্যাদিতে ব্যবহৃত হয় - সর্বদা স্বীকৃত। -> কোডটি ম্যানুয়ালি সেট করা আছে। নিয়ম: একবার সেট করুন এটি পরিবর্তন হয় না।

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


10
আপনি সচেতন যে আপনি আরডিবিএমএসকে ত্রুটি-প্রবণ ক্যাশে এড়িয়ে একটি একক এসকিউএল বিবৃতিতে আপনার জন্য অনুসন্ধান করতে দিতে পারেন?
এরউইন ব্র্যান্ডসেটেটার

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

14
অ-পরিবর্তনকারী উপাদান? সবচেয়ে ব্যয়বহুল উপাদান? লাইসেন্সিং ব্যয় (পোস্টগ্রিসকিউএল এর জন্য)? ওআরএম সংজ্ঞায়িত কি? না আমি সে সব সম্পর্কে অবগত ছিলাম না।
এরউইন ব্র্যান্ডসটেটার
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.