জ্যাঙ্গো ওআরএম-এর সিলেক্ট_ রিলেটেড এবং প্রিফেট_ রিলেটেডের মধ্যে পার্থক্য কী?


291

জাজানো ডকে,

select_related() বিদেশী-কী সম্পর্কগুলি অনুসরণ করে "সম্পর্কিত সম্পর্কিত-অবজেক্ট ডেটা নির্বাচন করে যখন এটি তার ক্যোয়ারি কার্যকর করে।

prefetch_related() প্রতিটি সম্পর্কের জন্য পৃথক অনুসন্ধান করে এবং পাইথনে "যোগদান" করে।

"অজগরটিতে যোগদান" এর অর্থ কী? কেউ উদাহরণ দিয়ে উদাহরণ দিয়ে বলতে পারেন?

আমার বোঝা হ'ল বিদেশী মূল সম্পর্কের জন্য, ব্যবহার করুন select_related; এবং এম 2 এম সম্পর্কের জন্য, ব্যবহার করুন prefetch_related। এটা কি সঠিক?


2
অজগরটিতে যোগদান সম্পাদনের অর্থ এই যে যোগদানটি ডাটাবেসে হবে না। একটি সিলেক্ট রিলেটেড সহ, আপনার যোগদানটি ডাটাবেসে ঘটে এবং আপনি কেবল একটি ডাটাবেস কোয়েরিতে ভোগেন। প্রিফেচ_ রিলেটেড সহ, আপনি দুটি ক্যারিয়ার চালাবেন এবং তারপরে ফলাফলগুলি ওআরএম'র সাথে 'যুক্ত' হয়ে যাবে যাতে আপনি এখনও অবজেক্ট.রিলেটেড_সেট টাইপ করতে পারেন
মার্ক গালোওয়ে

3
পাদটীকা হিসাবে, টিমি ও'মাহনি তাদের পার্থক্যগুলিও ডেটাবেস হিট ব্যবহার করে ব্যাখ্যা করতে পারেন: লিঙ্ক
মারকোস

উত্তর:


422

আপনার বোঝার বিষয়টি বেশিরভাগ ক্ষেত্রেই সঠিক। আপনি select_relatedযখন যে বস্তুটি নির্বাচন করতে যাচ্ছেন সেটি কোনও একক বস্তু, তাই OneToOneFieldবা ক ব্যবহার করুন ForeignKey। আপনি prefetch_relatedযখন কোনও জিনিসগুলির একটি "সেট" পেতে যাবেন আপনি ব্যবহার করুন , সুতরাং ManyToManyFieldআপনি যেমন বলেছিলেন বা বিপরীত ForeignKeys কেবল "বিপরীত ForeignKeyএস" বলতে আমি কী বোঝাতে চাই তা এখানে একটি উদাহরণ:

class ModelA(models.Model):
    pass

class ModelB(models.Model):
    a = ForeignKey(ModelA)

ModelB.objects.select_related('a').all() # Forward ForeignKey relationship
ModelA.objects.prefetch_related('modelb_set').all() # Reverse ForeignKey relationship

পার্থক্যটি হ'ল select_relatedকোনও এসকিউএল যোগদান করে এবং তাই এসকিউএল সার্ভার থেকে সারণির অংশ হিসাবে ফলাফল ফিরে পায়। prefetch_relatedঅন্যদিকে অন্য কোয়েরি কার্যকর করে এবং তাই মূল অবজেক্টে রিডানড্যান্ট কলামগুলি হ্রাস করে ( ModelAউপরের উদাহরণে)। আপনি prefetch_relatedযে কোনওটির select_relatedজন্য ব্যবহার করতে পারেন তার জন্য ব্যবহার করতে পারেন ।

ট্রেড অফগুলি হ'ল prefetch_relatedসার্ভারে আবার নির্বাচন করার জন্য আইডিগুলির একটি তালিকা তৈরি এবং প্রেরণ করতে হবে, এতে কিছুক্ষণ সময় লাগতে পারে। লেনদেনের ক্ষেত্রে এটি করার একটি দুর্দান্ত উপায় আছে কিনা তা আমি নিশ্চিত নই, তবে আমার বোঝা হ'ল জ্যাঙ্গো সর্বদা একটি তালিকা প্রেরণ করে এবং নির্বাচন করুন ... যেখানে পিকে ইন (..., ..., ...) মূলত। এক্ষেত্রে যদি প্রিফেচড ডেটাগুলি অপ্রতুল হয় (আসুন মার্কিন স্টেট অবজেক্টগুলি লোকের ঠিকানায় লিঙ্কিত হয়) এটি খুব ভাল হতে পারে, তবে এটি যদি একে অপরের কাছাকাছি হয় তবে এটি প্রচুর যোগাযোগ নষ্ট করতে পারে। যদি সন্দেহ হয়, উভয়ই চেষ্টা করে দেখুন যা আরও ভাল সম্পাদন করে per

উপরে আলোচিত সমস্ত কিছুই মূলত ডাটাবেসগুলির সাথে যোগাযোগ সম্পর্কে। পাইথনের পাশে অবশ্য prefetch_relatedঅতিরিক্ত সুবিধা রয়েছে যে ডাটাবেসে প্রতিটি বস্তুর প্রতিনিধিত্ব করতে একটি একক বস্তু ব্যবহৃত হয়। সঙ্গে select_relatedসদৃশ বস্তু একে "পিতা বা মাতা" বস্তুর জন্য পাইথন মধ্যে তৈরি করা হবে। যেহেতু পাইথনের বস্তুগুলির একটি শালীন বিট মেমরির ওভারহেড থাকে এটিও বিবেচ্য হতে পারে।


3
দ্রুত তবে কি?
ইলাদ রৌপ্য

24
select_relatedএকটি জিজ্ঞাসা হয় যখন prefetch_relatedদুটি হয়, তাই পূর্ববর্তীটি দ্রুত হয়। কিন্তু select_relatedকরার জন্য আপনাকে সাহায্য করবে না ManyToManyField'এর
bhinesley

31
ধীর উত্তরের জন্য দুঃখিত এটা আসলে নির্ভর করে। select_relatedএসকিউএল- prefetch_relatedতে একটি জোইন ব্যবহার করে যেখানে প্রথম মডেলটিতে ক্যোয়ারী চালানো হয়, প্রিফেচ করার জন্য প্রয়োজনীয় সমস্ত আইডি সংগ্রহ করে এবং তারপরে সমস্ত আইডি প্রয়োজন যেখানে হ'ল একটি আইএন দিয়ে একটি কোয়েরি চালায়। যদি আপনি একই বিদেশী কী ব্যবহার করে 3-5 টি মডেল বলে থাকেন select_relatedতবে প্রায় অবশ্যই ভাল। যদি আপনার কাছে একই বিদেশী কী ব্যবহার করে 100s বা মডেলগুলির সংখ্যা থাকে তবে prefetch_relatedআসলে এটি আরও ভাল হতে পারে। এর মধ্যে আপনাকে পরীক্ষা করতে হবে এবং কী হবে তা দেখতে হবে।
ক্রেজিস্টাস্টা

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

1
@ গর্ডন উইগ্রলে হ্যাঁ, আমি যখন এটি লিখেছি তখন কিছুক্ষণ হয়ে গেছে, তাই আমি ফিরে গিয়ে কিছুটা স্পষ্ট করেছিলাম। আমি নিশ্চিত নই যে আমি "ডিবিতে কম স্মৃতি ব্যবহার করি" বিটের সাথে একমত, তবে সবকিছুর জন্য হ্যাঁ। এবং এটি পাইথন সাইডে কম মেমরি ব্যবহার করতে পারে।
ক্রেজিস্টাস্টা

26

অপ্রয়োজনীয় ডিবি কোয়েরিগুলির পূর্বে উভয় পদ্ধতি একই উদ্দেশ্য অর্জন করে। তারা দক্ষতার জন্য বিভিন্ন পন্থা ব্যবহার করে।

এই পদ্ধতিগুলির মধ্যে যে কোনও একটির ব্যবহারের একমাত্র কারণ হ'ল যখন একটি একক বৃহত ক্যোয়ারী অনেকগুলি ছোট প্রশ্নগুলির চেয়ে ভাল। জ্যাঙ্গো ডেটাবেসের বিরুদ্ধে ডিমান্ড কোয়েরিগুলি সম্পাদন করার চেয়ে পূর্বের স্মৃতিতে মডেলগুলি তৈরি করতে বৃহত ক্যোয়ারি ব্যবহার করে।

select_relatedপ্রতিটি দেখার সাথে একটি সংযুক্তি সম্পাদন করে তবে সমস্ত যোগদানের টেবিলগুলির কলামগুলি অন্তর্ভুক্ত করার জন্য নির্বাচনকে প্রসারিত করে। তবে এই পদ্ধতির একটি সতর্কতা রয়েছে।

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

"পাইথন যোগদান" জন্য prefetch_relatedএকটু বেশি আশংকাজনক তারপর এটি হতে হবে। এটি প্রতিটি টেবিলে যোগদানের জন্য একটি পৃথক ক্যোয়ারী তৈরি করে। এটি এই টেবিলের প্রত্যেকটিকে একটি ক্লজ, যেখানে যেমন সহ ফিল্টার করে:

SELECT "credential"."id",
       "credential"."uuid",
       "credential"."identity_id"
FROM   "credential"
WHERE  "credential"."identity_id" IN
    (84706, 48746, 871441, 84713, 76492, 84621, 51472);

সম্ভাব্য অত্যধিক সারিগুলির সাথে একক যোগদানের পরিবর্তে প্রতিটি টেবিল পৃথক ক্যোয়ারিতে বিভক্ত।


1

জাজানো ডকুমেন্টেশন যেমন বলে:

prefetch_related ()

একটি ক্যুরিসেটটি ফেরত দেয় যা নির্দিষ্ট প্রতিটি লকআপের জন্য সম্পর্কিত একক ব্যাচে স্বয়ংক্রিয়ভাবে পুনরুদ্ধার করে।

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

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

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

এটি সম্পর্কে আরও তথ্য: https://docs.djangoproject.com/en/2.2/ref/models/querysets/#prefetch- সম্পর্কিত


1

ইতিমধ্যে পোস্ট করা উত্তরগুলি দিয়ে গেছে। কেবল ভেবেছিলাম যদি আমি প্রকৃত উদাহরণ সহ একটি উত্তর যুক্ত করি তবে এটি আরও ভাল হবে।

যাক 'আপনার কাছে 3 জ্যাঙ্গো মডেল রয়েছে যা সম্পর্কিত।

class M1(models.Model):
    name = models.CharField(max_length=10)

class M2(models.Model):
    name = models.CharField(max_length=10)
    select_relation = models.ForeignKey(M1, on_delete=models.CASCADE)
    prefetch_relation = models.ManyToManyField(to='M3')

class M3(models.Model):
    name = models.CharField(max_length=10)

এখানে আপনি খোঁজ করতে পারেন M2মডেল এবং তার আপেক্ষিক M1বস্তু ব্যবহার select_relationক্ষেত্র ও M3ব্যবহার অবজেক্টের prefetch_relationক্ষেত্র।

তবে আমরা যেহেতু M1এর সম্পর্কের বিষয়টি উল্লেখ করেছি তা M2হ'ল ForeignKey, এটি যে কোনও বস্তুর জন্য কেবল 1 টি রেকর্ড দেয় M2। একই জিনিস OneToOneFieldপাশাপাশি প্রযোজ্য ।

কিন্তু M3's সম্পর্ক M2একটি হল ManyToManyFieldযা যে কোন সংখ্যার আসতে পারে M1অবজেক্ট।

আপনার 2 আছে এমন একটি ক্ষেত্রে বিবেচনা করুন M2 অবজেক্ট রয়েছে এমনm21 , m22যাদের আইডির সাথে একই 5 টি সম্পর্কিত বিষয় রয়েছে । আপনি যখন এই সমস্ত বস্তুর জন্য যুক্ত বস্তুগুলি আনয়ন করেন , আপনি যদি নির্বাচিত সম্পর্কিত ব্যবহার করেন, এটি এভাবে কাজ করবে।M31,2,3,4,5M3M2

পদক্ষেপ:

  1. এই m21বস্তু।
  2. যার আইডি হ'ল M3বস্তুর সাথে সম্পর্কিত সমস্ত বস্তুর অনুসন্ধান করুন ।m211,2,3,4,5
  3. m22বস্তু এবং অন্যান্য সমস্ত বস্তুর জন্য একই জিনিস পুনরাবৃত্তি করুন M2

আমাদের যেমন আছে 1,2,3,4,5 উভয়ের জন্য আইডি রয়েছে m21, m22অবজেক্টস, আমরা যদি সিলেক্ট রিলেটেড অপশনটি ব্যবহার করি তবে এটি একই আইডি যা দু'বার আগেই প্রাপ্ত হয়েছিল তার জন্য দু'বার ডিবি জিজ্ঞাসা করবে।

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

এইভাবে আপনি M3একবারে সমস্ত বস্তু অনুসন্ধান করছেন যা কার্যকারিতা উন্নত করে।

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