ORM (ऑब्जेक्ट-रिलेशनल मैपिंग) में "N + 1 चयन समस्या" क्या है?


1596

"एन + 1 चयन समस्या" को आम तौर पर ऑब्जेक्ट-रिलेशनल मैपिंग (ओआरएम) चर्चाओं में एक समस्या के रूप में कहा जाता है, और मैं समझता हूं कि कुछ के लिए बहुत सारे डेटाबेस प्रश्न बनाने के साथ कुछ करना है जो ऑब्जेक्ट में सरल लगता है विश्व।

क्या किसी के पास समस्या का अधिक विस्तृत विवरण है?


2
यह n + 1 समस्या को समझने पर अच्छी व्याख्या के साथ एक बढ़िया लिंक है। यह इस समस्या का मुकाबला करने के लिए समाधान भी शामिल करता है: Architect.dzone.com/articles/how-identify-and-resilve-n1
aces।


इस समस्या के समाधान की तलाश कर रहे सभी लोगों के लिए, मुझे इसका वर्णन करने वाला एक पोस्ट मिला। stackoverflow.com/questions/32453989/…
डेन्डमॉन

2
जवाबों को ध्यान में रखते हुए, क्या इसे 1 + एन समस्या नहीं कहा जाना चाहिए? जैसा कि यह एक शब्दावली है, मैं विशेष रूप से, ओपी से पूछ रहा हूं।
user1418717

जवाबों:


1014

मान लें कि आपके पास Carऑब्जेक्ट्स (डेटाबेस पंक्तियों) का Carसंग्रह है , और प्रत्येक में Wheelऑब्जेक्ट्स (भी पंक्तियों) का संग्रह है । दूसरे शब्दों में, CarWheelएक 1-से-कई संबंध है।

अब, मान लें कि आपको सभी कारों के माध्यम से पुनरावृति करने की आवश्यकता है, और प्रत्येक के लिए, पहियों की एक सूची का प्रिंट आउट लें। भोले ओ / आर कार्यान्वयन निम्नलिखित होगा:

SELECT * FROM Cars;

और फिर प्रत्येक के लिए Car:

SELECT * FROM Wheel WHERE CarId = ?

दूसरे शब्दों में, आपके पास कारों के लिए एक चयन है, और फिर एन अतिरिक्त चयन, जहां एन कारों की कुल संख्या है।

वैकल्पिक रूप से, सभी पहियों को प्राप्त कर सकते हैं और मेमोरी में लुकअप कर सकते हैं:

SELECT * FROM Wheel

यह N + 1 से 2 तक डेटाबेस के लिए राउंड-ट्रिप की संख्या को कम कर देता है। अधिकांश ORM टूल आपको N + 1 का चयन करने से रोकने के कई तरीके देते हैं।

संदर्भ: हाइबरनेट के साथ जावा दृढ़ता , अध्याय 13।


139
"यह बुरा है" पर स्पष्ट करने के लिए - आप SELECT * from Wheel;एन + 1 के बजाय 1 चयन ( ) के साथ सभी पहियों को प्राप्त कर सकते हैं । एक बड़े एन के साथ, प्रदर्शन हिट बहुत महत्वपूर्ण हो सकता है।
tucuxi

211
@tucuxi मुझे आश्चर्य है कि आपको गलत होने के लिए बहुत सारे अपवोट मिले। एक डेटाबेस इंडेक्स के बारे में बहुत अच्छा है, एक विशिष्ट कारिड के लिए क्वेरी करना बहुत तेजी से वापस आ जाएगा। लेकिन अगर आपको सभी पहिए एक बार मिल गए हैं, तो आपको अपने आवेदन में CarID की खोज करनी होगी, जो अनुक्रमित नहीं है, यह धीमा है। जब तक आपके पास प्रमुख विलंबता समस्याएँ होती हैं, तब आपके डेटाबेस तक पहुँचना n + 1 वास्तव में तेज़ होता है - और हाँ, मैंने इसे वास्तविक विश्व कोड की एक बड़ी विविधता के साथ बेंचमार्क किया है।
एरियल

73
@ariel 'सही' तरीका सभी पहियों को प्राप्त करना है, जिसे CarId (1 चुनिंदा) द्वारा आदेश दिया गया है, और अगर CarId से अधिक विवरण की आवश्यकता है, तो सभी कारों के लिए एक दूसरी क्वेरी करें (कुल 2 प्रश्न)। चीजों को प्रिंट करना अब इष्टतम है, और कोई अनुक्रमणिका या द्वितीयक भंडारण की आवश्यकता नहीं थी (आप परिणामों पर पुनरावृति कर सकते हैं, उन सभी को डाउनलोड करने की आवश्यकता नहीं है)। आपने गलत बात मान ली। यदि आप अभी भी अपने बेंचमार्क के बारे में आश्वस्त हैं, तो क्या आप अपने प्रयोग और परिणामों के बारे में बताते हुए एक लंबी टिप्पणी (या पूर्ण उत्तर) पोस्ट करना चाहेंगे?
टक्सीक्सी

92
"हाइबरनेट (मैं अन्य ओआरएम फ्रेमवर्क से परिचित नहीं हूं) आपको इसे संभालने के कई तरीके देता है।" और ये तरीका है
तैमूर

58
@ एरियल अलग-अलग मशीनों पर डेटाबेस और एप्लिकेशन सर्वर के साथ अपने बेंचमार्क चलाने का प्रयास करें। मेरे अनुभव में, डेटाबेस के लिए गोल यात्राएं क्वेरी की तुलना में ओवरहेड में अधिक खर्च होती हैं। तो हाँ, प्रश्न वास्तव में तेज़ हैं, लेकिन यह दौर यात्राएं हैं जो कहर बरपाती हैं। मैंने "WHERE Id = const " को "WHERE Id IN ( const) में बदल दिया है , const , ...)" में और परिमाण के आदेश इससे बाहर हो जाते हैं।
हंस

110
SELECT 
table1.*
, table2.*
INNER JOIN table2 ON table2.SomeFkId = table1.SomeId

आपको यह परिणाम मिलता है कि तालिका 2 में चाइल्ड रो की तालिका 2 में प्रत्येक चाइल्ड रो के लिए टेबल 1 परिणाम वापस करके दोहराव का कारण बनता है। ओ / आर मैपर्स को एक अद्वितीय कुंजी फ़ील्ड के आधार पर तालिका 1 इंस्टेंसेस को अलग करना चाहिए, फिर सभी तालिका 2 कॉलम का उपयोग योग की भावनाओं को पॉप्युलेट करने के लिए करें।

SELECT table1.*

SELECT table2.* WHERE SomeFkId = #

N + 1 वह जगह है जहां पहली क्वेरी प्राथमिक ऑब्जेक्ट को पॉप्युलेट करती है और दूसरी क्वेरी सभी चाइल्ड ऑब्जेक्ट्स को प्रत्येक अद्वितीय प्राथमिक ऑब्जेक्ट के लिए पॉप्युलेट करती है।

विचार करें:

class House
{
    int Id { get; set; }
    string Address { get; set; }
    Person[] Inhabitants { get; set; }
}

class Person
{
    string Name { get; set; }
    int HouseId { get; set; }
}

और एक समान संरचना वाले टेबल। "22 वैली सेंट" पते के लिए एक एकल क्वेरी वापस आ सकती है:

Id Address      Name HouseId
1  22 Valley St Dave 1
1  22 Valley St John 1
1  22 Valley St Mike 1

O / RM को ID = 1, पता = "22 वैली सेंट" के साथ होम का एक उदाहरण भरना चाहिए और फिर डेव, जॉन और माइक के लिए लोगों के उदाहरणों के साथ इंहबिटेंट्स सरणी को केवल एक क्वेरी के साथ आबाद करना चाहिए।

ऊपर उपयोग किए गए समान पते के लिए N + 1 क्वेरी में परिणाम होगा:

Id Address
1  22 Valley St

जैसे एक अलग क्वेरी के साथ

SELECT * FROM Person WHERE HouseId = 1

और जिसके परिणामस्वरूप एक अलग डेटा सेट होता है

Name    HouseId
Dave    1
John    1
Mike    1

और अंतिम परिणाम एकल क्वेरी के साथ ऊपर के समान है।

सिंगल सेलेक्ट करने के फायदे यह है कि आपको वह सारा डेटा मिल जाता है जो हो सकता है कि आप अंततः चाहते हों। N + 1 के फायदे क्वेरी जटिलता हैं और आप आलसी लोडिंग का उपयोग कर सकते हैं जहां बच्चे के परिणाम सेट केवल पहले अनुरोध पर लोड किए जाते हैं।


4
N + 1 का अन्य लाभ यह है कि यह तेज़ है क्योंकि डेटाबेस एक इंडेक्स से सीधे परिणाम वापस कर सकता है। ज्वाइन करना और फिर छांटना एक अस्थायी तालिका की आवश्यकता होती है, जो धीमी है। N + 1 से बचने का एकमात्र कारण यह है कि यदि आपके पास अपने डेटाबेस से बहुत अधिक विलंबता है।
एरियल

17
जुड़ना और छांटना काफी तेज हो सकता है (क्योंकि आप अनुक्रमित और संभवतः छंटनी वाले क्षेत्रों में शामिल होंगे)। आपका 'n + 1' कितना बड़ा है? क्या आप गंभीरता से मानते हैं कि n + 1 समस्या केवल उच्च-विलंबता डेटाबेस कनेक्शन पर लागू होती है?
ट्यूक्सी

9
@ariel - आपकी सलाह कि N + 1 "सबसे तेज़" गलत है, भले ही आपके बेंचमार्क सही हों। वो कैसे संभव है? En.wikipedia.org/wiki/Anecdotal_evidence देखें , और इस प्रश्न के अन्य उत्तर में मेरी टिप्पणी भी।
व्हाइटनीलैंड जू

7
@ एरियल - मुझे लगता है कि मैंने इसे ठीक समझा :)। मैं केवल यह बताने की कोशिश कर रहा हूं कि आपका परिणाम केवल परिस्थितियों के एक सेट पर लागू होता है। मैं आसानी से एक काउंटर उदाहरण का निर्माण कर सकता था जो विपरीत दिखाया गया था। क्या इसका कोई मतलब है?
व्हाइटनीलैंड

13
दोहराने के लिए, SELECT N + 1 समस्या इसके मूल में है: मेरे पास पुनर्प्राप्त करने के लिए 600 रिकॉर्ड हैं। क्या उन सभी 600 को एक क्वेरी में, या 1 को 600 प्रश्नों में एक बार में प्राप्त करना तेज़ है। जब तक आप MyISAM पर हैं और / या आपके पास खराब सामान्यीकृत / खराब अनुक्रमित स्कीमा है (जिस स्थिति में ओआरएम समस्या नहीं है), एक उचित ट्यून डीबी 2 एमएस में 600 पंक्तियों को वापस कर देगा, जबकि व्यक्तिगत पंक्तियों को वापस कर देगा। प्रत्येक के बारे में 1 एमएस। इसलिए हम अक्सर N + 1 को सैकड़ों मिलीसेकंड लेते हुए देखते हैं जहाँ एक जुड़ाव केवल एक जोड़े को ले जाता है
कुत्तों की

64

उत्पाद के साथ एक से कई संबंधों के साथ आपूर्तिकर्ता। एक सप्लायर के पास कई उत्पाद हैं।

***** Table: Supplier *****
+-----+-------------------+
| ID  |       NAME        |
+-----+-------------------+
|  1  |  Supplier Name 1  |
|  2  |  Supplier Name 2  |
|  3  |  Supplier Name 3  |
|  4  |  Supplier Name 4  |
+-----+-------------------+

***** Table: Product *****
+-----+-----------+--------------------+-------+------------+
| ID  |   NAME    |     DESCRIPTION    | PRICE | SUPPLIERID |
+-----+-----------+--------------------+-------+------------+
|1    | Product 1 | Name for Product 1 |  2.0  |     1      |
|2    | Product 2 | Name for Product 2 | 22.0  |     1      |
|3    | Product 3 | Name for Product 3 | 30.0  |     2      |
|4    | Product 4 | Name for Product 4 |  7.0  |     3      |
+-----+-----------+--------------------+-------+------------+

कारक:

  • आपूर्तिकर्ता के लिए आलसी मोड "सच" (डिफ़ॉल्ट) पर सेट

  • उत्पाद पर क्वेरी के लिए उपयोग की गई फ़ेच मोड का चयन करें

  • फ़ेच मोड (डिफ़ॉल्ट): आपूर्तिकर्ता जानकारी एक्सेस की जाती है

  • कैशिंग पहली बार एक भूमिका नहीं निभाता है

  • आपूर्तिकर्ता पहुँचा है

फ़ेच मोड का चयन करें फ़ेच (डिफ़ॉल्ट)

// It takes Select fetch mode as a default
Query query = session.createQuery( "from Product p");
List list = query.list();
// Supplier is being accessed
displayProductsListWithSupplierName(results);

select ... various field names ... from PRODUCT
select ... various field names ... from SUPPLIER where SUPPLIER.id=?
select ... various field names ... from SUPPLIER where SUPPLIER.id=?
select ... various field names ... from SUPPLIER where SUPPLIER.id=?

परिणाम:

  • उत्पाद के लिए 1 चयन विवरण
  • आपूर्तिकर्ता के लिए एन चुनिंदा कथन

यह N + 1 चुनिंदा समस्या है!


3
क्या यह प्रदायक के लिए 1 का चयन करना है तो N उत्पाद के लिए चयन करता है?
bencampbell_14

@bencampbell_ हाँ, शुरू में मुझे भी ऐसा ही लगा। लेकिन फिर उनके उदाहरण के साथ, यह कई आपूर्तिकर्ताओं के लिए एक उत्पाद है।
मोहम्मद फैजान खान

38

मैं अन्य उत्तरों पर सीधे टिप्पणी नहीं कर सकता, क्योंकि मेरे पास पर्याप्त प्रतिष्ठा नहीं है। लेकिन यह ध्यान देने योग्य है कि समस्या अनिवार्य रूप से केवल इसलिए उत्पन्न होती है क्योंकि, ऐतिहासिक रूप से, बहुत सारे डीबीएम काफी घटिया हो गए हैं जब यह जुड़ने की बात आती है (MySQL एक विशेष रूप से उल्लेखनीय उदाहरण है)। इसलिए n + 1 में, अक्सर, शामिल होने की तुलना में विशेष रूप से तेज़ होता है। और फिर n + 1 पर सुधार करने के तरीके हैं, लेकिन फिर भी एक जुड़ाव की आवश्यकता के बिना, जो कि मूल समस्या से संबंधित है।

हालाँकि, MySQL अब बहुत बेहतर है, जब यह जुड़ने की बात आती थी। जब मैंने पहली बार MySQL सीखा, तो मैंने बहुत कुछ शामिल किया। तब मुझे पता चला कि वे कितने धीमे हैं, और कोड के बजाय n + 1 पर स्विच किया गया है। लेकिन, हाल ही में, मैं वापस जुड़ने के लिए आगे बढ़ रहा हूं, क्योंकि MySQL अब उन्हें संभालने की तुलना में बहुत बेहतर है, जब मैंने पहली बार इसका उपयोग करना शुरू किया था।

इन दिनों, तालिकाओं के ठीक से अनुक्रमित सेट पर एक साधारण जुड़ाव प्रदर्शन की शर्तों में शायद ही कभी एक समस्या है। और अगर यह एक प्रदर्शन हिट देता है, तो सूचकांक संकेत का उपयोग अक्सर उन्हें हल करता है।

यह यहां MySQL विकास टीम में से एक द्वारा चर्चा की गई है:

http://jorgenloland.blogspot.co.uk/2013/02/dbt-3-q3-6-x-performance-in-mysql-5610.html

तो सारांश यह है: यदि आप MySQL के उनके साथ असामान्य प्रदर्शन के कारण अतीत में जुड़ने से बचते रहे हैं, तो नवीनतम संस्करणों पर फिर से प्रयास करें। आपको शायद सुखद आश्चर्य होगा।


7
MySQL के शुरुआती संस्करणों को एक संबंधपरक DBMS कहना काफी खिंचाव है ... यदि उन समस्याओं का सामना करने वाले लोग एक वास्तविक डेटाबेस का उपयोग कर रहे थे, तो उन्हें उन प्रकार की समस्याओं का सामना नहीं करना पड़ता। ;-)
क्रेग

2
दिलचस्प बात यह है कि INNODB इंजन के परिचय और उसके बाद के अनुकूलन से MySQL में इस प्रकार की कई समस्याओं का समाधान हो गया था, लेकिन आप अभी भी MYISAM को बढ़ावा देने की कोशिश कर रहे लोगों में भाग लेंगे क्योंकि उन्हें लगता है कि यह तेज़ है।
क्रेग

5
FYI, JOINRDBMS में प्रयुक्त 3 सामान्य एल्गोरिदम में से एक 'नेस्टेड लूप्स' कहलाता है। यह मूल रूप से हुड के नीचे एक एन + 1 चयन है। फर्क सिर्फ इतना है कि डीबी ने क्लाइंट कोड के बजाय इसे उस पथ से नीचे ले जाने के लिए डेटा कोड और इंडेक्स के आधार पर उपयोग करने के लिए एक बुद्धिमान विकल्प बनाया।
ब्रैंडन

2
@ब्रांडन जी! जॉय संकेत और INDEX संकेत की तरह, सभी मामलों में एक निश्चित निष्पादन पथ मजबूर शायद ही डेटाबेस को हरा देगा। डेटाबेस लगभग हमेशा बहुत है, डेटा प्राप्त करने के लिए इष्टतम दृष्टिकोण को चुनने में बहुत अच्छा है। हो सकता है कि dbs के शुरुआती दिनों में आपको अपने प्रश्न को एक अजीबोगरीब तरीके से 'वाक्यांश' करने की आवश्यकता हो, साथ ही साथ db को समेटने के लिए, लेकिन विश्व स्तर के इंजीनियरिंग के दशकों के बाद, अब आप अपने डेटाबेस से एक रिलेशनल प्रश्न पूछकर और उसे बताकर सर्वश्रेष्ठ प्रदर्शन प्राप्त कर सकते हैं। आपके लिए उस डेटा को लाने और इकट्ठा करने का तरीका छांटें।
कुत्ते 1

3
न केवल डेटाबेस इंडेक्स और आंकड़ों का उपयोग कर रहा है, सभी ऑपरेशन भी स्थानीय I / O हैं, जिनमें से अधिकांश अक्सर डिस्क के बजाय अत्यधिक कुशल कैश के खिलाफ काम कर रहा है। डेटाबेस प्रोग्रामर इस प्रकार की चीजों को अनुकूलित करने के लिए बहुत अधिक ध्यान आकर्षित करते हैं।
क्रेग

27

हम इस समस्या के कारण Django में ORM से दूर चले गए। मूल रूप से, यदि आप कोशिश करते हैं और करते हैं

for p in person:
    print p.car.colour

ORM ख़ुशी से सभी लोगों (आमतौर पर एक व्यक्ति वस्तु के उदाहरण के रूप में) को वापस कर देगा, लेकिन फिर उसे प्रत्येक व्यक्ति के लिए कार तालिका को क्वेरी करने की आवश्यकता होगी।

यह एक सरल और बहुत प्रभावी तरीका है जिसे मैं " फैनफोल्डिंग " कहता हूं , जो उस निरर्थक विचार से बचता है कि एक रिलेशनल डेटाबेस से क्वेरी परिणाम मूल तालिकाओं पर वापस मैप करना चाहिए जिसमें से क्वेरी बनाई गई है।

चरण 1: चौड़ा चयन करें

  select * from people_car_colour; # this is a view or sql function

यह कुछ इस तरह लौटेगा

  p.id | p.name | p.telno | car.id | car.type | car.colour
  -----+--------+---------+--------+----------+-----------
  2    | jones  | 2145    | 77     | ford     | red
  2    | jones  | 2145    | 1012   | toyota   | blue
  16   | ashby  | 124     | 99     | bmw      | yellow

चरण 2: ऑब्जेक्ट करें

तीसरे आइटम के बाद विभाजित करने के लिए एक तर्क के साथ एक सामान्य वस्तु निर्माता में परिणाम चूसो। इसका मतलब यह है कि "जोन्स" ऑब्जेक्ट को एक से अधिक बार नहीं बनाया जाएगा।

चरण 3: रेंडर

for p in people:
    print p.car.colour # no more car queries

अजगर के लिए fanfolding के कार्यान्वयन के लिए इस वेब पेज को देखें ।


10
मुझे खुशी है कि मैं आपके पोस्ट पर ठोकर खाई, क्योंकि मुझे लगा कि मैं पागल हो रहा हूं। जब मुझे N + 1 समस्या के बारे में पता चला, तो मेरा तात्कालिक विचार था- ठीक है, आप सिर्फ एक ऐसा दृश्य क्यों नहीं बनाते हैं, जिसमें आपकी जरूरत की सभी जानकारी हो, और उस दृश्य से खींच लें? आपने मेरी स्थिति को मान्य कर दिया है। धन्यवाद महोदय।
एक डेवलपर

14
हम इस समस्या के कारण Django में ORM से दूर चले गए। है ना? Django के पास select_related, जो इसे हल करने के लिए है - वास्तव में, इसके डॉक्स आपके p.car.colourउदाहरण के समान उदाहरण के साथ शुरू होते हैं ।
एड्रियन 17

8
यह एक पुराना anwswer है, हमारे पास select_related()और prefetch_related()अभी Django में है।
मारियस जमरो

1
ठंडा। लेकिन select_related()दोस्त और किसी भी तरह के स्पष्ट रूप से उपयोगी एक्सट्रपलेशन के किसी भी ऐसा करने के लिए नहीं लगता है LEFT OUTER JOIN। समस्या एक इंटरफेस समस्या नहीं है, लेकिन एक मुद्दा यह है कि अजीब विचार है कि वस्तुओं और संबंधपरक डेटा के साथ करने के लिए एक मुद्दा है .... मेरे विचार में।
रोरिकेल

26

चूंकि यह एक बहुत ही सामान्य प्रश्न है, इसलिए मैंने यह लेख लिखा है , जिस पर यह उत्तर आधारित है।

N + 1 क्वेरी समस्या क्या है

N + 1 क्वेरी समस्या तब होती है जब डेटा एक्सेस फ्रेमवर्क ने N को अतिरिक्त SQL स्टेटमेंट निष्पादित किया वही डेटा लाने के लिए जिसे प्राथमिक SQL क्वेरी निष्पादित करते समय पुनर्प्राप्त किया जा सकता था।

N का मान जितना बड़ा होगा, उतने अधिक प्रश्नों का निष्पादन होगा, प्रदर्शन प्रभाव उतना ही बड़ा होगा। और, धीमी क्वेरी लॉग के विपरीत, जो धीमी गति से चलने वाले प्रश्नों को खोजने में आपकी मदद कर सकता है, N + 1 समस्या हाजिर नहीं होगी क्योंकि प्रत्येक अलग-अलग अतिरिक्त क्वेरी धीमी क्वेरी लॉग को ट्रिगर न करने के लिए पर्याप्त रूप से तेज़ चलती है।

समस्या बड़ी संख्या में अतिरिक्त प्रश्नों को निष्पादित कर रही है, जो कुल मिलाकर, प्रतिक्रिया समय को धीमा करने के लिए पर्याप्त समय लेती है।

आइए विचार करें कि हमारे पास निम्नलिखित पोस्ट और पोस्ट_काम डेटाबेस टेबल हैं जो एक से कई टेबल संबंध बनाते हैं :

<Code> post </ code> और <code> post_comments </ code> टेबल

हम निम्नलिखित 4 postपंक्तियों को बनाने जा रहे हैं :

INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence - Part 1', 1)

INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence - Part 2', 2)

INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence - Part 3', 3)

INSERT INTO post (title, id)
VALUES ('High-Performance Java Persistence - Part 4', 4)

और, हम 4 post_commentबाल रिकॉर्ड भी बनाएंगे :

INSERT INTO post_comment (post_id, review, id)
VALUES (1, 'Excellent book to understand Java Persistence', 1)

INSERT INTO post_comment (post_id, review, id)
VALUES (2, 'Must-read for Java developers', 2)

INSERT INTO post_comment (post_id, review, id)
VALUES (3, 'Five Stars', 3)

INSERT INTO post_comment (post_id, review, id)
VALUES (4, 'A great reference book', 4)

सादे SQL के साथ N + 1 क्वेरी समस्या

यदि आप post_commentsइस SQL ​​क्वेरी का उपयोग कर चयन करते हैं :

List<Tuple> comments = entityManager.createNativeQuery("""
    SELECT
        pc.id AS id,
        pc.review AS review,
        pc.post_id AS postId
    FROM post_comment pc
    """, Tuple.class)
.getResultList();

और, बाद में, आप post titleप्रत्येक के लिए संबद्ध लाने का निर्णय लेते हैं post_comment:

for (Tuple comment : comments) {
    String review = (String) comment.get("review");
    Long postId = ((Number) comment.get("postId")).longValue();

    String postTitle = (String) entityManager.createNativeQuery("""
        SELECT
            p.title
        FROM post p
        WHERE p.id = :postId
        """)
    .setParameter("postId", postId)
    .getSingleResult();

    LOGGER.info(
        "The Post '{}' got this review '{}'",
        postTitle,
        review
    );
}

आप N + 1 क्वेरी समस्या को ट्रिगर करने जा रहे हैं, क्योंकि एक SQL क्वेरी के बजाय, आपने 5 (1 + 4) निष्पादित किया है:

SELECT
    pc.id AS id,
    pc.review AS review,
    pc.post_id AS postId
FROM post_comment pc

SELECT p.title FROM post p WHERE p.id = 1
-- The Post 'High-Performance Java Persistence - Part 1' got this review
-- 'Excellent book to understand Java Persistence'

SELECT p.title FROM post p WHERE p.id = 2
-- The Post 'High-Performance Java Persistence - Part 2' got this review
-- 'Must-read for Java developers'

SELECT p.title FROM post p WHERE p.id = 3
-- The Post 'High-Performance Java Persistence - Part 3' got this review
-- 'Five Stars'

SELECT p.title FROM post p WHERE p.id = 4
-- The Post 'High-Performance Java Persistence - Part 4' got this review
-- 'A great reference book'

N + 1 क्वेरी समस्या को हल करना बहुत आसान है। आपको मूल SQL क्वेरी में आवश्यक सभी डेटा निकालने की ज़रूरत है, जैसे:

List<Tuple> comments = entityManager.createNativeQuery("""
    SELECT
        pc.id AS id,
        pc.review AS review,
        p.title AS postTitle
    FROM post_comment pc
    JOIN post p ON pc.post_id = p.id
    """, Tuple.class)
.getResultList();

for (Tuple comment : comments) {
    String review = (String) comment.get("review");
    String postTitle = (String) comment.get("postTitle");

    LOGGER.info(
        "The Post '{}' got this review '{}'",
        postTitle,
        review
    );
}

इस बार, केवल एक एसक्यूएल क्वेरी को निष्पादित किया जाता है ताकि हम उन सभी डेटा को प्राप्त कर सकें जिनका हम आगे उपयोग करने में रुचि रखते हैं।

JPA और हाइबरनेट के साथ N + 1 क्वेरी समस्या

JPA और हाइबरनेट का उपयोग करते समय, ऐसे कई तरीके हैं जिनसे आप N + 1 क्वेरी समस्या को ट्रिगर कर सकते हैं, इसलिए यह जानना बहुत महत्वपूर्ण है कि आप इन स्थितियों से कैसे बच सकते हैं।

अगले उदाहरणों के लिए, विचार करें कि हम postऔर post_commentsतालिकाओं को निम्न संस्थाओं के लिए मैप कर रहे हैं :

<कोड> पोस्ट </ कोड> और <कोड> पोस्ट कॉमिशन </ कोड> इकाइयां

जेपीए मैपिंग इस तरह दिखती है:

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    private Long id;

    private String title;

    //Getters and setters omitted for brevity
}

@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {

    @Id
    private Long id;

    @ManyToOne
    private Post post;

    private String review;

    //Getters and setters omitted for brevity
}

FetchType.EAGER

FetchType.EAGERअपने JPA संघों के लिए या तो स्पष्ट रूप से या स्पष्ट रूप से उपयोग करना एक बुरा विचार है क्योंकि आप अधिक डेटा प्राप्त करने जा रहे हैं जिसकी आपको आवश्यकता है। अधिक, FetchType.EAGERरणनीति एन + 1 क्वेरी मुद्दों के लिए भी प्रवण है।

दुर्भाग्य से, @ManyToOneऔर @OneToOneएसोसिएशन FetchType.EAGERडिफ़ॉल्ट रूप से उपयोग करते हैं , इसलिए यदि आपके मैपिंग इस तरह दिखते हैं:

@ManyToOne
private Post post;

आप FetchType.EAGERरणनीति का उपयोग कर रहे हैं , और, हर बार जब आप एक JPQL या मानदंड एपीआई क्वेरी के साथ JOIN FETCHकुछ PostCommentसंस्थाओं को लोड करते समय उपयोग करना भूल जाते हैं :

List<PostComment> comments = entityManager
.createQuery("""
    select pc
    from PostComment pc
    """, PostComment.class)
.getResultList();

आप N + 1 क्वेरी समस्या को ट्रिगर करने जा रहे हैं:

SELECT 
    pc.id AS id1_1_, 
    pc.post_id AS post_id3_1_, 
    pc.review AS review2_1_ 
FROM 
    post_comment pc

SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 1
SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 2
SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 3
SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 4

निष्पादित किए जाने वाले अतिरिक्त चयन कथनों पर ध्यान दें क्योंकि postएसोसिएशन को वापस लौटने से पहले लाया जाना ListहैPostComment संस्थाओं।

डिफ़ॉल्ट लाने की योजना के विपरीत, जिसे आप उपयोग कर रहे हैं , जेपीएनएल या मानदंड एपीआई क्वेरी की findविधि को बुलाते हुए EnrityManager, एक स्पष्ट योजना को परिभाषित करता है कि हाइबरनेट स्वचालित रूप से एक जॉय फ़च को इंजेक्ट करके नहीं बदल सकता है। तो, आपको इसे मैन्युअल रूप से करने की आवश्यकता है।

यदि आपको postएसोसिएशन की बिल्कुल भी आवश्यकता नहीं है , तो आप उपयोग करते समय भाग्य से बाहर हैं FetchType.EAGERक्योंकि इसे लाने से बचने का कोई तरीका नहीं है। इसलिए इसका उपयोग करना बेहतर हैFetchType.LAZY डिफ़ॉल्ट रूप ।

लेकिन, यदि आप postएसोसिएशन का उपयोग करना चाहते हैं , तो आप JOIN FETCHएन + 1 क्वेरी समस्या से निपटने के लिए उपयोग कर सकते हैं :

List<PostComment> comments = entityManager.createQuery("""
    select pc
    from PostComment pc
    join fetch pc.post p
    """, PostComment.class)
.getResultList();

for(PostComment comment : comments) {
    LOGGER.info(
        "The Post '{}' got this review '{}'", 
        comment.getPost().getTitle(), 
        comment.getReview()
    );
}

इस बार, हाइबरनेट एकल SQL कथन निष्पादित करेगा:

SELECT 
    pc.id as id1_1_0_, 
    pc.post_id as post_id3_1_0_, 
    pc.review as review2_1_0_, 
    p.id as id1_0_1_, 
    p.title as title2_0_1_ 
FROM 
    post_comment pc 
INNER JOIN 
    post p ON pc.post_id = p.id

-- The Post 'High-Performance Java Persistence - Part 1' got this review 
-- 'Excellent book to understand Java Persistence'

-- The Post 'High-Performance Java Persistence - Part 2' got this review 
-- 'Must-read for Java developers'

-- The Post 'High-Performance Java Persistence - Part 3' got this review 
-- 'Five Stars'

-- The Post 'High-Performance Java Persistence - Part 4' got this review 
-- 'A great reference book'

आपको FetchType.EAGERभ्रूण की रणनीति से क्यों बचना चाहिए, इसके बारे में अधिक जानकारी के लिए देखें इस इस लेख को देखें

FetchType.LAZY

यहां तक ​​कि अगर आप FetchType.LAZYसभी संघों के लिए स्पष्ट रूप से उपयोग करने के लिए स्विच करते हैं , तो भी आप एन + 1 मुद्दे से टकरा सकते हैं।

इस बार, post एसोसिएशन को इस तरह मैप किया गया है:

@ManyToOne(fetch = FetchType.LAZY)
private Post post;

अब, जब आप प्राप्त करते हैं PostComment संस्थाओं को :

List<PostComment> comments = entityManager
.createQuery("""
    select pc
    from PostComment pc
    """, PostComment.class)
.getResultList();

हाइबरनेट एकल SQL कथन निष्पादित करेगा:

SELECT 
    pc.id AS id1_1_, 
    pc.post_id AS post_id3_1_, 
    pc.review AS review2_1_ 
FROM 
    post_comment pc

लेकिन, यदि बाद में, आप आलसी-भार का संदर्भ देने जा रहे हैं post एसोसिएशन :

for(PostComment comment : comments) {
    LOGGER.info(
        "The Post '{}' got this review '{}'", 
        comment.getPost().getTitle(), 
        comment.getReview()
    );
}

आपको N + 1 क्वेरी समस्या मिलेगी:

SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 1
-- The Post 'High-Performance Java Persistence - Part 1' got this review 
-- 'Excellent book to understand Java Persistence'

SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 2
-- The Post 'High-Performance Java Persistence - Part 2' got this review 
-- 'Must-read for Java developers'

SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 3
-- The Post 'High-Performance Java Persistence - Part 3' got this review 
-- 'Five Stars'

SELECT p.id AS id1_0_0_, p.title AS title2_0_0_ FROM post p WHERE p.id = 4
-- The Post 'High-Performance Java Persistence - Part 4' got this review 
-- 'A great reference book'

चूँकि postएसोसिएशन को लेज़ीली लिया जाता है, लॉग संदेश बनाने के लिए आलसी एसोसिएशन तक पहुँचने पर एक माध्यमिक एसक्यूएल स्टेटमेंट निष्पादित किया जाएगा।

फिर से, फिक्स JOIN FETCHमें JPQL क्वेरी के लिए एक क्लॉज जोड़ना होता है :

List<PostComment> comments = entityManager.createQuery("""
    select pc
    from PostComment pc
    join fetch pc.post p
    """, PostComment.class)
.getResultList();

for(PostComment comment : comments) {
    LOGGER.info(
        "The Post '{}' got this review '{}'", 
        comment.getPost().getTitle(), 
        comment.getReview()
    );
}

और, जैसे में FetchType.EAGER उदाहरण की , यह JPQL क्वेरी एक एकल SQL कथन उत्पन्न करेगा।

यहां तक ​​कि अगर आप का उपयोग कर रहे हैं FetchType.LAZYऔर एक द्विदिश के बाल संघ का संदर्भ नहीं है@OneToOne JPA संबंध , तो भी आप N + 1 क्वेरी समस्या को ट्रिगर कर सकते हैं।

@OneToOneसंघों द्वारा उत्पन्न N + 1 क्वेरी समस्या को कैसे दूर कर सकते हैं, इस बारे में अधिक जानकारी के लिए , इस लेख को देखें

N + 1 क्वेरी समस्या का स्वचालित रूप से पता लगाने के लिए कैसे

यदि आप अपने डेटा एक्सेस लेयर में N + 1 क्वेरी समस्या का स्वचालित रूप से पता लगाना चाहते हैं, तो यह आलेख बताता है कि आप db-utilओपन-सोर्स प्रोजेक्ट का उपयोग करके कैसे कर सकते हैं ।

सबसे पहले, आपको निम्नलिखित मावेन निर्भरता को जोड़ने की आवश्यकता है:

<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>db-util</artifactId>
    <version>${db-util.version}</version>
</dependency>

बाद में, आपको बस उपयोग करना है SQLStatementCountValidator अंतर्निहित SQL कथनों को उत्पन्न उपयोगिता करना होगा जो उत्पन्न होते हैं:

SQLStatementCountValidator.reset();

List<PostComment> comments = entityManager.createQuery("""
    select pc
    from PostComment pc
    """, PostComment.class)
.getResultList();

SQLStatementCountValidator.assertSelectCount(1);

यदि आप FetchType.EAGERउपरोक्त परीक्षण मामले का उपयोग कर रहे हैं और चला रहे हैं , तो आपको निम्नलिखित परीक्षण मामले में विफलता मिलेगी:

SELECT 
    pc.id as id1_1_, 
    pc.post_id as post_id3_1_, 
    pc.review as review2_1_ 
FROM 
    post_comment pc

SELECT p.id as id1_0_0_, p.title as title2_0_0_ FROM post p WHERE p.id = 1

SELECT p.id as id1_0_0_, p.title as title2_0_0_ FROM post p WHERE p.id = 2


-- SQLStatementCountMismatchException: Expected 1 statement(s) but recorded 3 instead!

db-utilओपन-सोर्स प्रोजेक्ट के बारे में अधिक जानकारी के लिए , इस लेख को देखें


लेकिन अब आपको पेजिंग की समस्या है। यदि आपके पास 10 कारें हैं, तो 4 पहियों वाली प्रत्येक कार और आप प्रति पृष्ठ 5 कारों के साथ कारों को पगेट करना चाहते हैं। तो आप मूल रूप से आपके पास हैं SELECT cars, wheels FROM cars JOIN wheels LIMIT 0, 5। लेकिन जो आपको मिलता है वह 5 पहियों वाली 2 कारें हैं (पहली कार सभी 4 पहियों वाली और दूसरी कार केवल 1 पहिया के साथ), क्योंकि लिमिट पूरे परिणाम को सीमित करेगा, न कि केवल रूट क्लॉज।
CappY

2
मेरे पास उसके लिए भी एक लेख है।
व्लादिमीर मुहालसी

लेख के लिए धन्यवाद। मुझे इसे पढ़ना है। तेज़ स्क्रॉल द्वारा - मैंने देखा कि समाधान विंडो फ़ंक्शन है, लेकिन मारियाडीबी में वे काफी नए हैं - इसलिए समस्या पुराने संस्करणों में बनी हुई है। :)
कैप्पी

@VladMihalcea, मैंने आपके लेख से या पोस्ट से हर बार जब आपने N + 1 समस्या को समझाते हुए ManyToOne मामले का उल्लेख किया। लेकिन वास्तव में लोग N + 1 मुद्दे से संबंधित OneToMany मामले में ज्यादातर दिलचस्पी रखते हैं। क्या आप कृपया OneToMany केस का संदर्भ और व्याख्या कर सकते हैं?
जेजे बीम

18

मान लीजिए कि आपके पास कंपनी और EMPLOYEE है। कंपनी के पास कई EMPLOYEES हैं (यानी EMPLOYEE के पास एक Company_ID है)।

कुछ O / R कॉन्फ़िगरेशन में, जब आपके पास कोई मैप की गई कंपनी ऑब्जेक्ट होती है और उसके कर्मचारी ऑब्जेक्ट्स को एक्सेस करने के लिए जाते हैं, तो O / R टूल हर कर्मचारी के लिए एक का चयन करेगा, यदि आप सीधे SQL में चीजें कर रहे थे, तो आप कर सकते हैं select * from employees where company_id = XX । इस प्रकार एन (कर्मचारियों का #) प्लस 1 (कंपनी)

इस प्रकार EJB Entity Beans के प्रारंभिक संस्करणों ने काम किया। मेरा मानना ​​है कि हाइबरनेट जैसी चीजों ने इसे खत्म कर दिया है, लेकिन मुझे यकीन नहीं है। अधिकांश टूल में आमतौर पर मैपिंग के लिए उनकी रणनीति के रूप में जानकारी शामिल होती है।


18

यहाँ समस्या का एक अच्छा वर्णन है

अब जब आप इस समस्या को समझ गए हैं तो इसे आमतौर पर अपनी क्वेरी में शामिल होने से बचा जा सकता है। यह मूल रूप से आलसी लोड की गई वस्तु को लाने के लिए बाध्य करता है ताकि डेटा n + 1 प्रश्नों के बजाय एक क्वेरी में पुनर्प्राप्त हो। उम्मीद है की यह मदद करेगा।


17

विषय पर आयेंद पोस्ट की जाँच करें: NHibernate में एन + 1 समस्या के चयन का संयोजन

मूल रूप से, NHibernate या EntityFramework जैसे ORM का उपयोग करते समय, यदि आपके पास एक से कई (मास्टर-विस्तार) संबंध हैं, और प्रत्येक मास्टर रिकॉर्ड के अनुसार सभी विवरणों को सूचीबद्ध करना चाहते हैं, तो आपको N + 1 क्वेरी कॉल करना होगा डेटाबेस, "एन" मास्टर रिकॉर्ड की संख्या: 1 सभी मास्टर रिकॉर्ड प्राप्त करने के लिए क्वेरी, और एन प्रश्न, एक मास्टर रिकॉर्ड, प्रति मास्टर रिकॉर्ड के सभी विवरण प्राप्त करने के लिए।

अधिक डेटाबेस क्वेरी कॉल → अधिक विलंबता समय → अनुप्रयोग / डेटाबेस प्रदर्शन में कमी।

हालांकि, ओआरएम में मुख्य रूप से जॉइन का उपयोग करके इस समस्या से बचने के विकल्प हैं।


3
जोड़ एक अच्छा समाधान (अक्सर) नहीं हैं, क्योंकि उनका परिणाम कार्टेसियन उत्पाद हो सकता है, जिसका अर्थ है कि परिणाम पंक्तियों की संख्या रूट तालिका परिणामों की संख्या प्रत्येक बच्चे की तालिका में परिणामों की संख्या से गुणा होती है। विशेष रूप से कई विधर्मी स्तरों पर बुरा। प्रत्येक पर 100 "पदों" के साथ 20 "ब्लॉग" और प्रत्येक पोस्ट पर 10 "टिप्पणियों" का चयन करने से 20000 परिणाम पंक्तियों में आएंगे। NHibernate में "बैच-आकार" (माता-पिता आईडी पर खंड के साथ बच्चों का चयन करें) या "सबसेलेक्ट" की तरह वर्कअराउंड है।
एरिक हार्ट

14

1 क्वेरी जारी करने के लिए यह बहुत तेज़ है जो 100 प्रश्न वापस करने की तुलना में 100 परिणाम देता है जो प्रत्येक 1 परिणाम देता है।


13

मेरी राय में हाइबरनेट पिटफॉल में लिखा गया लेख : व्हाई रिलेशनशिप चाहिए बी आलसी वास्तविक एन + 1 मुद्दे के बिल्कुल विपरीत है।

यदि आपको सही स्पष्टीकरण की आवश्यकता है तो कृपया हाइबरनेट - अध्याय 19: प्रदर्शन में सुधार - रणनीतियाँ प्राप्त करना देखें

चयन करना (डिफ़ॉल्ट) N + 1 की समस्याओं के लिए बेहद असुरक्षित है, इसलिए हम शामिल होने में सक्षम होना चाहते हैं


2
मैंने हाइबरनेट पेज पढ़ा। यह नहीं कहता है कि वास्तव में N + 1 का चयन क्या समस्या है । लेकिन यह कहता है कि आप इसे ठीक करने के लिए जॉइन का उपयोग कर सकते हैं।
इयान बॉयड

3
चुनिंदा विवरणों में कई माता-पिता के लिए बाल वस्तुओं का चयन करने के लिए, चयन करने के लिए बैच-आकार की आवश्यकता होती है। सबसिलेक्शन एक और विकल्प हो सकता है। यदि आपके पास कई पदानुक्रम स्तर हैं और कार्टेशियन उत्पाद बनाया जाता है तो जॉइन वास्तव में खराब हो सकते हैं।
एरिक हार्ट

10

आपूर्ति की गई लिंक में n + 1 समस्या का बहुत सरल उदाहरण है। यदि आप इसे हाइबरनेट पर लागू करते हैं तो यह मूल रूप से उसी चीज के बारे में बात कर रहा है। जब आप किसी ऑब्जेक्ट के लिए क्वेरी करते हैं, तो इकाई लोड हो जाती है लेकिन किसी भी एसोसिएशन (जब तक अन्यथा कॉन्फ़िगर नहीं किया जाता है) आलसी लोड हो जाएगी। इसलिए मूल वस्तुओं के लिए एक क्वेरी और इनमें से प्रत्येक के लिए संघों को लोड करने के लिए एक और क्वेरी। लौटी 100 वस्तुओं का अर्थ है एक प्रारंभिक क्वेरी और फिर प्रत्येक, n + 1 के लिए एसोसिएशन प्राप्त करने के लिए 100 अतिरिक्त क्वेरी।

http://pramatr.com/2009/02/05/sql-n-1-selects-explained/


9

एक करोड़पति के पास एन कारें हैं। आप सभी (4) पहियों को प्राप्त करना चाहते हैं।

एक (1) क्वेरी सभी कारों को लोड करती है, लेकिन प्रत्येक (एन) कार के लिए पहियों को लोड करने के लिए एक अलग क्वेरी प्रस्तुत की जाती है।

लागत:

मान लें कि अनुक्रमित राम में फिट होते हैं।

1 + एन क्वेरी पार्सिंग और प्लानिंग + इंडेक्स खोज और लोडिंग पेलोड के लिए 1 + एन + (एन * 4) प्लेट का उपयोग।

मान लें कि अनुक्रमणिका राम में फिट नहीं है।

लोडिंग इंडेक्स के लिए सबसे खराब स्थिति में 1 + N प्लेट एक्सेस की अतिरिक्त लागत।

सारांश

बॉटल नेक प्लेट प्लेट एक्सेस है (hdd पर 70 बार प्रति सेकंड की रैंडम एक्सेस)। पेलोड के लिए एक उत्सुक जॉइन सिलेक्ट प्लेट 1 + एन + (एन * 4) बार भी एक्सेस करेगा। इसलिए यदि अनुक्रमणिका राम में फिट होती है - कोई समस्या नहीं है, तो इसका तेज पर्याप्त है क्योंकि केवल राम संचालन शामिल हैं।


9

एन + 1 चयनित मुद्दा एक दर्द है, और यह इकाई परीक्षणों में ऐसे मामलों का पता लगाने के लिए समझ में आता है। मैंने एक दिए गए परीक्षण विधि या कोड के एक मनमाने ढंग से ब्लॉक द्वारा निष्पादित प्रश्नों की संख्या की पुष्टि करने के लिए एक छोटी सी लाइब्रेरी विकसित की है - JDBC स्निफर

बस अपने परीक्षण वर्ग में एक विशेष JUnit नियम जोड़ें और अपने परीक्षण विधियों पर अपेक्षित प्रश्नों की संख्या के साथ एनोटेशन लगाएं:

@Rule
public final QueryCounter queryCounter = new QueryCounter();

@Expectation(atMost = 3)
@Test
public void testInvokingDatabase() {
    // your JDBC or JPA code
}

5

जैसा कि दूसरों ने अधिक सुरुचिपूर्ण ढंग से कहा है कि आपके पास OneToMany कॉलम का कार्टेशियन उत्पाद है या आप N + 1 चयन कर रहे हैं। क्रमशः डेटाबेस के साथ या तो संभावित विशाल परिणाम या बातूनी।

मुझे आश्चर्य है कि इसका उल्लेख नहीं किया गया है, लेकिन इस तरह से मैंने इस मुद्दे पर विचार किया है ... मैं एक अर्ध-अस्थायी आईडी तालिका बनाता हूंमैं यह भी करता हूं जब आपके पास IN ()क्लॉज सीमा होती है

यह सभी मामलों के लिए काम नहीं करता है (शायद बहुमत भी नहीं) लेकिन यह विशेष रूप से अच्छी तरह से काम करता है यदि आपके पास बहुत सारी बाल वस्तुएं हैं जैसे कि कार्टेशियन उत्पाद हाथ से निकल जाएगा (यानी बहुत सारे OneToMany कॉलम परिणाम की संख्या एक होंगे कॉलम का गुणन) और नौकरी की तरह इसका बैच।

पहले आप अपने माता-पिता ऑब्जेक्ट आईडी को एक आईडी टेबल में बैच के रूप में डालें। यह बैच_आइड कुछ है जिसे हम अपने ऐप में जनरेट करते हैं और होल्ड करते हैं।

INSERT INTO temp_ids 
    (product_id, batch_id)
    (SELECT p.product_id, ? 
    FROM product p ORDER BY p.product_id
    LIMIT ? OFFSET ?);

अब प्रत्येक OneToManyकॉलम के लिए आप सिर्फ SELECTआइडी टेबल INNER JOINपर बच्चे की मेज पर एक WHERE batch_id=(या इसके विपरीत) के साथ करते हैं। आप सिर्फ यह सुनिश्चित करना चाहते हैं कि आप आईडी कॉलम द्वारा ऑर्डर करें क्योंकि यह विलय के परिणाम कॉलम को आसान बना देगा (अन्यथा आपको पूरे परिणाम सेट के लिए एक HashMap / Table की आवश्यकता होगी जो कि खराब नहीं हो सकता है)।

फिर आप समय-समय पर आईडी टेबल को साफ करें।

यह भी विशेष रूप से अच्छी तरह से काम करता है अगर उपयोगकर्ता कुछ प्रकार के थोक प्रसंस्करण के लिए 100 या इतने अलग आइटम का चयन करता है। अस्थायी तालिका में 100 अलग-अलग आईडी रखें।

अब आपके द्वारा किए जा रहे प्रश्नों की संख्या OneToMany कॉलम की संख्या से है।


1

मैट सोलनिट उदाहरण लें, कल्पना करें कि आप कार और पहियों के बीच एक संबंध को LAZY के रूप में परिभाषित करते हैं और आपको कुछ पहियों के खेतों की आवश्यकता होती है। इसका मतलब यह है कि पहले चयन के बाद, हाइबरनेट पहियों से जहां "car_id =: id" के लिए "सेलेक्ट" किया जा रहा है।

यह प्रत्येक N कार द्वारा पहला चयन और अधिक 1 का चयन करता है, इसीलिए इसे n + 1 समस्या कहा जाता है।

इससे बचने के लिए, एसोसिएशन को उत्सुक बनाएं, ताकि हाइबरनेट एक जुड़ने के साथ डेटा लोड करता है।

लेकिन ध्यान दें, यदि कई बार आप संबंधित पहिए तक नहीं पहुंचते हैं, तो बेहतर है कि इसे LAZY रखें या मानदंड को मानदंड के साथ बदलें।


1
फिर से, जॉन्स एक अच्छा समाधान नहीं है, खासकर जब 2 से अधिक पदानुक्रम स्तर लोड किए जा सकते हैं। इसके बजाय "सबसेलेक्ट" या "बैच-आकार" जांचें; अंतिम बच्चों को पेरेंट आईडी द्वारा "इन" क्लॉज, जैसे "सेलेक्ट ... व्हील्स से जहां (1,3,4,6,7,8,11,13)" में लोड किया जाएगा।
एरिक हार्ट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.