2009-09-22 10 views
11

तो मुझे एक बड़ा डेटाबेस मिला है जिसे मैं एक बार में स्मृति में नहीं रख सकता। मुझे टेबल में प्रत्येक आइटम पर लूप करना है, इसे संसाधित करना है, और संसाधित डेटा को तालिका में किसी अन्य कॉलम में रखना है।क्या मुझे एक रिकॉर्डसेट पर लूप करने के लिए एकाधिक कर्सर ऑब्जेक्ट्स चाहिए और एक ही समय में अपडेट करें?

जबकि मैं अपने कर्सर पर लूपिंग कर रहा हूं, अगर मैं एक अद्यतन कथन चलाने की कोशिश करता हूं तो यह रिकॉर्डसेट को छोटा करता है (मुझे विश्वास है क्योंकि यह कर्सर ऑब्जेक्ट का पुन: उद्देश्य कर रहा है)।

सवाल:

अद्यतन बयान मुझे मूल का चयन करें बयान के ऊपर पाशन जारी रखने की अनुमति को चलाने के लिए एक दूसरे कर्सर वस्तु बनाने होगा?

क्या मुझे दूसरा कर्सर ऑब्जेक्ट रखने के लिए डेटाबेस से दूसरे कनेक्शन की आवश्यकता है, जो मुझे ऐसा करने की अनुमति देगा?

डेटाबेस के दो कनेक्शन, तालिका से एक पढ़ने, अन्य लेखन के जवाब देने के लिए sqlite कैसे प्रतिक्रिया देगा?

मेरे कोड (सरलीकृत):

select = """ 
      SELECT id, unprocessed_content 
      FROM data_table 
      WHERE processed_content IS NULL 
      LIMIT 1000 
     """ 

तो मैं क्या करना होगा:

recordset = data_manager.cursor.execute(select) 
while recordset: 
    #do update stuff... 
    recordset = data_manager.cursor.execute(select) 

import sqlite3 

class DataManager(): 
    """ Manages database (used below). 
     I cut this class way down to avoid confusion in the question. 
    """ 
    def __init__(self, db_path): 
     self.connection = sqlite3.connect(db_path) 
     self.connection.text_factory = str 
     self.cursor = self.connection.cursor() 

    def genRecordset(self, str_sql, subs=tuple()): 
     """ Generate records as tuples, for str_sql. 
     """ 
     self.cursor.execute(str_sql, subs) 
     for row in self.cursor: 
      yield row 

select = """ 
      SELECT id, unprocessed_content 
      FROM data_table 
      WHERE processed_content IS NULL 
     """ 

update = """ 
      UPDATE data_table 
      SET processed_content = ? 
      WHERE id = ? 
     """ 
data_manager = DataManager(r'C:\myDatabase.db') 
subs = [] 
for row in data_manager.genRecordset(str_sql): 
    id, unprocessed_content = row 
    processed_content = processContent(unprocessed_content) 
    subs.append((processed_content, id)) 

    #every n records update the database (whenever I run out of memory) 
    if len(subs) >= 1000: 
     data_manager.cursor.executemany(update, subs) 
     data_manager.connection.commit() 
     subs = [] 
#update remaining records 
if subs: 
    data_manager.cursor.executemany(update, subs) 
    data_manager.connection.commit() 

अन्य विधि मैंने कोशिश की होने के लिए मेरी चयन कथन को संशोधित करने के लिए था

मेरे साथ समस्या यह थी कि मेरा वास्तविक चयन कथन में इसमें शामिल है और इसमें कुछ समय लगता है, इसलिए जॉइन को निष्पादित करना कि कई बार बहुत समय गहन है। मैं प्रक्रिया को तेज़ी से चुनने की कोशिश कर रहा हूं, फिर एक जनरेटर का उपयोग करके, इसलिए मुझे इसे स्मृति में रखना नहीं है।

समाधान:

ठीक है, तो मेरे पहले दो सवालों का जवाब है "नहीं" मेरे तीसरे प्रश्न के लिए, एक बार डेटाबेस में कनेक्शन बनने के बाद, यह पूरे डेटाबेस को लॉक कर देता है, इसलिए दूसरा कनेक्शन तब तक कुछ भी करने में सक्षम नहीं होगा जब तक कि पहला कनेक्शन बंद न हो जाए।

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

मेरा समाधान एक अस्थायी डेटाबेस बनाना है जिसे मैं ided के साथ processed_content चिपकाता हूं, ताकि मेरे पास प्रति कनेक्शन/कर्सर ऑब्जेक्ट हो और डेटाबेस के समय-समय पर अस्थायी डेटाबेस में डालने के दौरान चयनित रिकॉर्डसेट पर लूपिंग जारी रख सके। एक बार जब मैं अपने चुने हुए रिकॉर्डसेट के अंत तक पहुंच जाता हूं तो मैं डेटा को अस्थायी डेटाबेस में वापस मूल में स्थानांतरित करता हूं।

यदि कोई कनेक्शन/कर्सर ऑब्जेक्ट्स के बारे में निश्चित रूप से जानता है, तो मुझे एक टिप्पणी में बताएं।

उत्तर

3

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

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

तो, मैं, कहेंगे इसके लिए जाना -!)

+0

मैंने डेटाबेस के साथ अपने दूसरे कर्सर ऑब्जेक्ट के साथ दूसरा कनेक्शन बनाने का प्रयास किया, लेकिन यह मिला, "sqlite3.OperationalError डेटाबेस बंद है "। क्या यह अस्थायी तालिका बनाकर बस मिल सकता है? – tgray

+0

कारण मैंने पहले कनेक्शन/कर्सर की कोशिश की है क्योंकि अस्थायी तालिका 12 जीबी के डेटा के करीब होगी (प्रभावी रूप से मेरे डेटाबेस के आकार को प्रभावी ढंग से दोगुना कर देती है)। – tgray

+0

अरघ, आप सही हैं - स्क्लाइट का लॉक डीबी-चौड़ा है! - (अस्थायी तालिका केवल एक बार और सभी के लिए शामिल होने में मदद करेगी, लेकिन फिर आपको चयन/सीमा का उपयोग करना होगा (दोनों अस्थायी अद्यतन करना और वास्तविक तालिका, या वास्तविक वृद्धि को अद्यतन करना और उसी वृद्धि से अस्थायी रूप से हटाना) - तेजी से नहीं लगता है, लेकिन मुझे विकल्प नहीं दिखते हैं। –

1

कई कारणों से कर्सर खराब बुरे बुरे हैं।

मैं सुझाव दूंगा (और बहुत से अन्य निश्चित रूप से इसमें शामिल होंगे) कि आप CURSOR मार्ग जाने के बजाय एक एकल अद्यतन कथन का उपयोग करते हैं।

अपने Processed_Content किसी एक क्वेरी इसलिए की तरह आधारित संचालन निर्धारित करता है कि एक पैरामीटर के रूप में भेजा जा सकता है:

UPDATE data_table 
SET processed_content = ? 
WHERE processed_content IS NULL 
LIMIT 1000 

प्रतिक्रियाओं के आधार पर संपादित:

के बाद से हर पंक्ति के लिए एक अनूठा मूल्य है Processed_Content, आपके पास रिकॉर्डसेट और लूप का उपयोग करने के अलावा कोई विकल्प नहीं है। मैंने कई बार कई मौकों पर ऐसा किया है। आप जो सुझाव दे रहे हैं उसे प्रभावी ढंग से काम करना चाहिए। , "कर्सर" "पुराने एसक्यूएल हाथ" को भ्रमित करेंगे के मामले में यह पेश क्योंकि वे DECLARE foo CURSOR, FETCH FROM CURSOR, WHERE CURRENT OF CURSOR के साथ जुड़े हुए कई मुद्दों के बारे में सोच रहे होंगे, और -

+0

दुर्भाग्य से नहीं, क्योंकि यह तालिका में प्रत्येक रिकॉर्ड को एक ही चीज़ में सेट करेगा। Unprocessed_content की प्रत्येक पंक्ति/रिकॉर्ड अलग है (हालांकि तालिका पर कोई विशिष्ट बाधा नहीं है)। – tgray

+1

मैं चिमिंग कर रहा हूं ;-) हां, कई कार्यों को एक साधारण या अपेक्षाकृत सरल एसक्यूएल क्वेरी, अन्य मामलों के लिए आरक्षित आरक्षित व्यक्त किया जा सकता है। बड़े डेटाबेस के साथ, आपको 50 छोटी नौकरियों में 5 मिलियन रिकॉर्ड के अपडेट के "खंड" की आवश्यकता हो सकती है, लेकिन यह अभी भी कम दर्दनाक और निश्चित रूप से कर्सर की तुलना में तेज़ होगा। – mjv

+1

बहुत अधिक सहमत होंगे। यदि आप कर्सर आधारित दृष्टिकोण से बच सकते हैं, तो मैं निश्चित रूप से इसके लिए जाऊंगा। अधिकांश कर्सर आधारित एसक्यूएल को सेट आधारित फैशन में किसी भी तरह से फिर से लिखा जा सकता है। – Paddy

2

यह एक डीबी समारोह है कि आपकी सामग्री पर कार्रवाई करेंगे बनाने के लिए संभव है? यदि ऐसा है, तो आप एक अपडेट स्टेटमेंट लिखने में सक्षम होना चाहिए और डेटाबेस को सभी काम करने दें। उदाहरण के लिए,

Update data_table 
set processed_col = Process_Column(col_to_be_processed) 
+0

दुर्भाग्यवश, मेरा Process_Column फ़ंक्शन HTML को पार करता है और मुझे SQL में ऐसा करने का कोई अच्छा (आसान) तरीका नहीं पता है। क्या आप? अच्छा जवाब अगर मेरी प्रसंस्करण थोड़ा कम जटिल थी। – tgray