2009-02-10 10 views
11

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

क्या यह खराब अभ्यास है या क्या अद्यतन अद्यतन में "कहां IN (1,2,3,4, ..... 10000)" का उपयोग करने से इस अद्यतन को करने का एक बेहतर तरीका है?

क्या प्रत्येक रिकॉर्ड के लिए अलग-अलग अपडेट स्टेटमेंट्स का उपयोग करना और उन्हें एक ही लेनदेन में रखना अधिक समझदारी होगी? अभी मैं SQL सर्वर और एक्सेस के साथ काम कर रहा हूं, लेकिन यदि संभव हो, तो मैं किसी भी प्रकार के रिलेशनल डेटाबेस में अधिक व्यापक सर्वोत्तम अभ्यास समाधान सुनना चाहता हूं।

+0

हम किस तरह के "बहुत बड़े सेट" के बारे में बात कर रहे हैं? क्या यह हजारों, लाखों या लाखों मूल्यों है? – Tooony

+0

हाँ, हजारों। मेरे नमूने में पिछले सूचकांक के रूप में "10000" है। मुझे लगता है कि कम से कम अल्प अवधि में - अधिकतम संख्या होगी। – Karim

उत्तर

6

मैं हमेशा

WHERE id IN (1,2,3,4,.....10000) 

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

संपादित करें: उदाहरण के लिए, रेल इस एक बहुत दृश्यों

यह निश्चित रूप से एक एकल लेनदेन में अलग अद्यतन बयान करना बेहतर नहीं होगा पीछे है।

13

एक और विकल्प उन नंबरों को एक टेम्पलेट टेबल में स्टोर करना और अद्यतन करने के लिए शामिल होने में इसका उपयोग करना है। यदि आप एक अपडेट स्टेटमेंट निष्पादित करने में सक्षम हैं तो प्रति रिकॉर्ड एक कथन निष्पादित करने से निश्चित रूप से बेहतर है।

+0

मुझे यह विचार बहुत पसंद है लेकिन मुझे स्पष्ट नहीं है कि यह एक्सेस पर समर्थित होगा या नहीं। अफसोस की बात है, हम पहुंच का समर्थन करने के लिए जंजीर हैं। – Karim

+0

एक्सेस यह करेगा। मैं अक्सर एक अस्थायी तालिका का उपयोग करता हूं - यहां तक ​​कि एक स्थायी रूप से परिभाषित एक यूआई नियंत्रण के लिए बाध्य। तो जब उपयोगकर्ता संसाधित किए जाने वाले आइटमों का चयन कर रहा है, तो वे आइटम फ़्लैग किए जा रहे हैं या अस्थायी तालिका में डाले जा रहे हैं। जब उपयोगकर्ता ठीक क्लिक करता है, तो एक अद्यतन क्वेरी temp तालिका में शामिल हो जाती है। –

+1

बस यह सुनिश्चित करें कि यदि आप जिस तालिका को अपडेट कर रहे हैं वह SQL सर्वर पर एक लिंक की गई तालिका है जो अस्थायी तालिका भी है। अन्यथा, एक्सेस को SQL सर्वर पर वापस भेजने के लिए अद्यतन करने के लिए संपूर्ण तालिका को स्मृति में खींचने का प्रयास कर सकते हैं। आप एक लेनदेन पहचानकर्ता –

2

ओरेकल में मूल्यों की एक सीमा है जिसे आप एक इन क्लॉज में डाल सकते हैं। तो आप बेहतर रूप से एक OR, x = 1 या x = 2 का उपयोग करते हैं ... जहां तक ​​मुझे पता है, वे सीमित नहीं हैं।

+0

1000 एक सेट के लिए ओरेकल में सीमा है लेकिन आप ओरेकल में कर सकते हैं: जहां आईडी (1,2, ..., 1000) या आईडी (1001, ..., 2000) या आईडी में (... ।) ... हालांकि यह jimmyorr के समाधान का उपयोग करने के लिए बेहतर है। – tuinstoel

2

मैं एक टेबल-चर/टेम्प-टेबल का उपयोग करूंगा; इसमें मूल्य डालें, और इसमें शामिल हों। फिर आप एक ही सेट को कई बार उपयोग कर सकते हैं। यह विशेष रूप से अच्छी तरह से काम करता है यदि आप (उदाहरण के लिए) वर्चर्स के रूप में आईडी के सीएसवी को पास कर रहे हैं। एक SQL सर्वर उदाहरण के रूप में:

DECLARE @ids TABLE (id int NOT NULL) 

INSERT @ids 
SELECT value 
FROM dbo.SplitCsv(@arg) // need to define separately 

UPDATE t 
SET t. // etc 
FROM [TABLE] t 
INNER JOIN @ids #i ON #i.id = t.id 
+0

एक यूडीएफ है जिसका उपयोग स्प्लिटसीएसवी के लिए यहां किया जा सकता है -http: //stackoverflow.com/questions/519769/sql-server-2008-pass-arraylist-or-string-to-sp-for-in/519793#519793 –

+0

@Russ कैम - चीयर्स, आदर्श। –

4

आप इन खंड को कैसे उत्पन्न करते हैं?

अगर वहाँ है एक और SELECT कथन है कि उन मूल्यों को उत्पन्न करता है, तो आप बस प्लग सकता है कि अद्यतन में इतनी तरह:

UPDATE TARGET_TABLE T 
SET 
    SOME_VALUE = 'Whatever' 
WHERE T.ID_NUMBER IN(
        SELECT ID_NUMBER --this SELECT generates your ID #s. 
        FROM SOURCE_TABLE 
        WHERE SOME_CONDITIONS 
        ) 

कुछ RDBMses में, आप वाक्य रचना मौजूद है का उपयोग करके बेहतर प्रदर्शन प्राप्त होगा, इस प्रकार दिखाई देगा जो:

UPDATE TARGET_TABLE T 
SET 
    SOME_VALUE = 'Whatever' 
WHERE EXISTS (
      SELECT ID_NUMBER --this SELECT generates your ID #s. 
      FROM SOURCE_TABLE S 
      WHERE SOME_CONDITIONS 
       AND S.ID_NUMBER = T.ID_NUMBER 
      ) 
+0

मैं निश्चित रूप से भविष्य के प्रश्नों के लिए इसे ध्यान में रख रहा हूं लेकिन, नहीं, आइटम सीधे उपयोगकर्ता इंटरफ़ेस से आ रहे हैं क्योंकि उपयोगकर्ता सूची में एकाधिक आइटम चुनता है। – Karim

+0

आह, यह शर्म की बात है। तब मैं ocdecio द्वारा सुझाए गए अस्थायी तालिका दृष्टिकोण के लिए जाना होगा। बस अस्थायी तालिका में सामान जोड़ें क्योंकि उपयोगकर्ता अपने चयन करता है। – JosephStyons

2

जानते हुए भी क्या एक "बहुत बड़े" आईडी की संख्या हो सकती है के बिना, मैं एक अनुमान उद्यम चाहते हैं। ;-)

चूंकि आप डेटाबेस के रूप में एक्सेस का उपयोग कर रहे हैं, आईडी की संख्या उच्च नहीं हो सकती है। मान लीजिए कि हम 10,000 से कम संख्या के बारे में बात कर रहे हैं और हमें आईडी के पकड़ने के लिए कंटेनरों की सीमाएं जाननी चाहिए (सामने की ओर किस भाषा का उपयोग किया जाता है?), मैं एक UPDATE कथन पर चिपक जाऊंगा; यदि वह बाद में रखरखाव करने के लिए सबसे अधिक पढ़ने योग्य और आसान है। अन्यथा मैं कुछ चतुर तर्क का उपयोग करके उन्हें कई बयान में विभाजित कर दूंगा। कुछ कथन को एक से दस, सौ, हजार ... के साथ कई बयानों में विभाजित करने की तरह है।

फिर, मैं इसे यथासंभव कुशलतापूर्वक कथन निष्पादित करने के लिए डीबी ऑप्टिमाइज़र पर छोड़ दूंगा। मैं यह सुनिश्चित करने के लिए क्वेरी/प्रश्नों पर शायद 'व्याख्या' करूंगा कि यद्यपि मूर्खतापूर्ण कुछ भी नहीं चल रहा है।

लेकिन मेरे अनुभव में, डेटाबेस प्रबंधक के लिए इस तरह के अनुकूलन को छोड़ना अक्सर ठीक है। एक चीज जो अधिक समय लेती है आमतौर पर डेटाबेस से वास्तविक कनेक्शन होती है, इसलिए यदि आप एक ही कनेक्शन के भीतर सभी प्रश्नों को निष्पादित कर सकते हैं तो यह आमतौर पर कोई समस्या नहीं है। सुनिश्चित करें कि आप सभी UPDATE कथन भेज दें, इससे पहले कि आप देखना शुरू करें और किसी भी परिणाम सेट को वापस आने के लिए प्रतीक्षा करें। :-)

+0

एक जेट डेटाबेस का ऑटोनंबर फ़ील्ड जेट लंबे पूर्णांक क्षेत्र का एक विशेष मामला है और इसकी श्रेणी -2,147,483,648 से 2,147,483,647 है - यह बहुत संख्या है। यह मेरे लिए बहुत बड़ी संख्या की तरह लगता है। –

+0

हां, यह एक बड़ी संख्या है (या एक लंबा पूर्णांक ...)। लेकिन क्या आपने एक्सेस डेटाबेस में पंक्तियों की उस राशि के पास कुछ भी उपयोग करने का प्रयास किया है? ;-) यह वही था जो मैं सोच रहा था। – Tooony

1

आम तौर पर विचार करने के लिए कई चीजें हैं।

  1. डीबी में कैश पार्सिंग बयान। प्रत्येक खंड में, इन खंडों में अलग-अलग वस्तुओं के साथ, अलग से पार्स किया जाना चाहिए। आप अक्षर के बजाय बाध्य चर का उपयोग कर रहे हैं, है ना?
  2. कुछ डेटाबेस में इन क्लॉज में आइटम्स की संख्या पर एक सीमा है। ओरेकल के लिए यह 1000 है।
  3. आपको लॉक रिकॉर्ड अपडेट करते समय। यदि आपके पास एकाधिक अलग-अलग अपडेट स्टेटमेंट हैं तो आपके पास डेडलॉक्स हो सकते हैं। इसका मतलब है कि आपको उस आदेश के बारे में सावधान रहना होगा जिसमें आप अपने अपडेट जारी करते हैं।
  4. डेटाबेस के लिए राउंड-ट्रिप विलंबता बहुत तेज कथन के लिए भी उच्च हो सकती है। इसका मतलब यह है कि यात्रा समय बचाने के लिए एक बार में कई रिकॉर्ड्स में हेरफेर करना बेहतर होता है।

हमने हाल ही में इन-क्लॉज के आकार को सीमित करने के लिए हमारी प्रणाली को बदल दिया है और हमेशा बाध्य चर का उपयोग किया है क्योंकि इससे विभिन्न SQL कथनों की संख्या कम हो गई है और इस प्रकार बेहतर प्रदर्शन हुआ है। असल में हम अपने एसक्यूएल स्टेटमेंट जेनरेट करते हैं और इन-क्लॉज एक निश्चित आकार से अधिक होने पर कई कथन निष्पादित करते हैं। हम अपडेट के लिए ऐसा नहीं करते हैं इसलिए हमें लॉकिंग के बारे में चिंता करने की ज़रूरत नहीं है। आप।

एक टेम्प तालिका का उपयोग करके प्रदर्शन में सुधार नहीं हो सकता है क्योंकि आपको आईडी के साथ temp तालिका को पॉप्युलेट करना होगा। प्रयोग और प्रदर्शन परीक्षण आपको यहां जवाब बता सकते हैं।

एक एकल खंड में समझना और बनाए रखना बहुत आसान है। यह शायद आपको पहले के बारे में चिंता करनी चाहिए। यदि आपको लगता है कि प्रश्नों का प्रदर्शन खराब है तो आप एक अलग रणनीति को आजमा सकते हैं और देख सकते हैं कि यह मदद करता है, लेकिन समय-समय पर अनुकूलन न करें। इन-क्लॉज अर्थात् सही है इसलिए इसे तोड़ने पर अकेले छोड़ दें।

1

यदि आप ओरेकल पर थे, तो मैं मार्क ग्रैवेल के पोस्ट के समान टेबल फ़ंक्शंस का उपयोग करने की अनुशंसा करता हूं।

-- first create a user-defined collection type, a table of numbers 
create or replace type tbl_foo as table of number; 

declare 
    temp_foo tbl_foo; 
begin 
    -- this could be passed in as a parameter, for simplicity I am hardcoding it 
    temp_foo := tbl_foo(7369, 7788); 

    -- here I use a table function to treat my temp_foo variable as a table, 
    -- and I join it to the emp table as an alternative to a massive "IN" clause 
    select e.* 
    from emp e, 
     table(temp_foo) foo 
    where e.empno = foo.column_value; 
end; 
+0

वह ओरेकल पर नहीं है। डी 'ओह। –

+1

एचएम। उन्होंने "किसी भी प्रकार के रिलेशनल डेटाबेस में व्यापक सर्वोत्तम अभ्यास समाधान" के लिए कहा। मुझे यकीन नहीं है कि ओरेकल टेबल टेबल के साथ एकमात्र डीबी है, लेकिन यह सलाह किसी भी डीबी पर लागू होगी जो उस तरह की सुविधा का समर्थन करती है। मैंने ओरेकल उदाहरण दिया क्योंकि मैं यही ज्यादातर काम करता हूं। : \ – jimmyorr

+1

+1, ओपी ने वास्तव में अन्य विक्रेताओं के लिए भी एक समाधान पूछा। – tuinstoel

0

मुझे आपकी सूची में मूल्यों के प्रकार नहीं पता हैं। वे 1 से 10,000 मूल्यों के सबसे हैं, तो आप की तरह कुछ पाने के लिए उन्हें कार्रवाई करने के लिए सक्षम हो सकता है:

WHERE MyID BETWEEN 1 AND 10000 AND MyID NOT IN (3,7,4656,987) 

या, यदि सूची में नहीं अभी भी लंबा हो जाएगा, सूची प्रसंस्करण और एक गुच्छा पैदा बयान के बयान:

WHERE MyID BETWEEN 1 AND 343 AND MyID BETWEEN 344 AND 400 ... 

और आगे।

आखिरकार, आपको चिंता करने की ज़रूरत नहीं है कि यदि आप पासथ्रू क्वेरी का उपयोग करते हैं तो जेट एक आईएन क्लॉज को कैसे संसाधित करेगा। आप कोड में ऐसा नहीं कर सकते हैं, लेकिन आपके पास एक सहेजा गया QueryDef हो सकता है जिसे पासथ्रू के रूप में परिभाषित किया गया है और अपनी IN सूची का उपयोग करने के लिए रनटाइम पर कोड में WHERE क्लॉज को बदल सकता है।फिर यह सब SQL सर्वर पर पास हो गया है, और SQL सर्वर यह तय करेगा कि इसे कैसे संसाधित किया जाए।

संबंधित मुद्दे