2012-05-21 9 views
8

मुझे Django का उपयोग MySQL 5.5.22 के साथ निम्न समस्या है।MySQL अद्यतन एकाधिक कॉलम बदलना गैर-परमाणु है?

id a11 a12 a21 a22 level 
324 3  2  5  3  2 

को देखते हुए एक क्वेरीसमूह qs, मैं निम्नलिखित अद्यतन करना:

देखते हुए कॉलम आईडी, स्तर और एक 2x2 मैट्रिक्स A11 के रूप में जमा, A12, A21, A22 के साथ एक मेज, मैं इस पंक्ति है

UPDATE `storage` 
SET 
`a21` = (3 * `storage`.`a11`) + (-1 * `storage`.`a21`), 
`a22` = (3 * `storage`.`a12`) + (-1 * `storage`.`a22`), 
`level` = `storage`.`level` - -1, 
`a11` = (2 * `storage`.`a11`) + (-1 * `storage`.`a21`), 
`a12` = (2 * `storage`.`a12`) + (-1 * `storage`.`a22`) 
:
qs.update(
    a11=(b12 * a21 - b11 * a22) * F('a11') + (b11 * a12 - b12 * a11) * F('a21'), 
    a12=(b12 * a21 - b11 * a22) * F('a12') + (b11 * a12 - b12 * a11) * F('a22'), 
    a21=(b22 * a21 - b21 * a22) * F('a11') + (b21 * a12 - b22 * a11) * F('a21'), 
    a22=(b22 * a21 - b21 * a22) * F('a12') + (b21 * a12 - b22 * a11) * F('a22'), 
    level=(F('level') - 1) 
    ) 

जो के लिए Django निम्न क्वेरी उत्पन्न करता है (db.connection.queries से समझ में आ गया है, जहां संक्षिप्तता के लिए खंड निकालने के लिए) 63,210

और मेरी पंक्ति उस के बाद इस तरह दिखता है:

id a11 a12 a21 a22 level 
324 2  1  4  3  1 

किसी भी पंक्ति के लिए, a12*a21 - a11*a22 = 1 सच माना जाता है, और कहा कि के अनुसार, पंक्ति होना चाहिए था:

id a11 a12 a21 a22 level 
324 1  1  4  3  1 

यह वह जगह है मैं SQLite पर क्या प्राप्त करता हूं, Django एक ही क्वेरी उत्पन्न करता है, और मुझे यह समझने में काफी समय लगा कि MySQL कुछ अलग कर रहा था। क्वेरी से, ऐसा लगता है कि इंटरडिपेंट एकाधिक पंक्तियों को अपडेट करते समय, MySQL इसे एक परमाणु ऑपरेशन के रूप में नहीं मानता है, और कॉलम अपडेट होने पर, वे उन पर निर्भर मानों को प्रभावित करते हैं। मैं इस बात की पुष्टि इस होना करने के लिए क्या अजगर शीघ्र पर निम्न कोड से होता है लगता है:

>>> a11, a12, a21, a22 = (3, 2, 5, 3) 
>>> (2 * a11) + (-1 * a21),\ 
... (2 * a12) + (-1 * a22),\ 
... (3 * a11) + (-1 * a21),\ 
... (3 * a12) + (-1 * a22) 
(1, 1, 4, 3) 

कॉलम एक समय में एक अपडेट किया जाता है, तो क्वेरी द्वारा दिए गए एक ही क्रम में,:

>>> a11, a12, a21, a22 = (3, 2, 5, 3) 
>>> a21 = (3*a11) + (-1*a21) 
>>> a22 = (3*a12) + (-1*a22) 
>>> a11 = (2*a11) + (-1*a21) 
>>> a12 = (2*a12) + (-1*a22) 
>>> (a11, a12, a21, a22) 
(2, 1, 4, 3) 

यह वास्तव में डरावना व्यवहार है, क्योंकि यह एक पुस्तकालय है जिसका उपयोग क्रॉस-प्लेटफॉर्म का उपयोग किया जाना है। मेरे प्रश्न हैं:

  1. कौन सा गलत कर रहा है, MySQL या SQLite? क्या इसे एक बग माना जा सकता है?
  2. मैं अन्य प्रमुख डेटाबेस (ओरेकल, पोस्टग्रेएसक्यूएल और एसक्यूएलसेवर) से क्या उम्मीद कर सकता हूं?
  3. इस व्यवहार को सामान्य करने के लिए मैं Django ORM (कोई कच्चे प्रश्न) के साथ क्या कर सकता हूं?

संपादित

समस्या स्पष्ट है, लेकिन मैं अभी भी एक समाधान के लिए देख रहा हूँ। सभी मूल्यों को खींचकर और उन्हें वापस धक्का देना इस विशेष एप्लिकेशन के लिए एक स्वीकार्य समाधान नहीं है।

+1

यह एक दिलचस्प सवाल है। मैंने इसके साथ [sqlfiddle] (http://sqlfiddle.com/#!2/7f14b/2) पर खेला और ऐसा लगता है कि MySQL ही एकमात्र ऐसा है जो इस तरह से व्यवहार करता है। – Chad

+0

संबंधित/डुप्लिकेट: http://stackoverflow.com/questions/2203202/sql-update-order-of- मूल्यांकन – pilcrow

+0

नीचे मेरा अद्यतन उत्तर देखें। – eggyal

उत्तर

10

रूप MySQL manual में कहा गया है:

निम्नलिखित बयान में दूसरा काम वर्तमान (अद्यतन) col1 मूल्य, नहीं मूल col1 मूल्य करने के लिए सेट col2। नतीजा यह है कि col1 और col2 समान मूल्य है। यह व्यवहार मानक एसक्यूएल से अलग है।

UPDATE t1 SET col1 = col1 + 1, col2 = col1;

इसलिए, आपके मामले में, मूल्य a21 के लिए जब, अभिव्यक्ति `a11` = (2 * `storage`.`a11`) + (-1 * `storage`.`a21`) नए, अपडेट, 4 की बजाय मैनुअल के रूप में 5 के मूल मूल्य मूल्य का कहना है का मूल्यांकन इस प्रयोग किया जा रहा व्यवहार मानक एसक्यूएल से अलग है।

आप के बजाय लेकिन मैं नहीं जानता कि कुछ इस तरह Django ORM का उपयोग कर लागू किया जा सकता है कि क्या, कई तालिका UPDATE वाक्य रचना के साथ एक स्वयं में शामिल होने के इस्तेमाल कर सकते हैं:

UPDATE storage AS old 
    JOIN storage AS new USING (id) 
SET 
    new.a21 = (3 * old.a11) + (-1 * old.a21), 
    new.a22 = (3 * old.a12) + (-1 * old.a22), 
    new.level = old.level - -1, 
    new.a11 = (2 * old.a11) + (-1 * old.a21), 
    new.a12 = (2 * old.a12) + (-1 * old.a22); 

sqlfiddle पर यह देखें।

मेरा एकमात्र अन्य विचार (जिसे निश्चित रूप से डीजेगो के भीतर लागू किया जाना चाहिए) अद्यतन को अलग-अलग हिस्सों में विभाजित करना है, जो उन क्षेत्रों के नए (पुराने के बजाय) मूल्यों के संबंध में बाद के हिस्सों में अपडेट किए गए फ़ील्ड को परिभाषित करना है। पहले भागों में अद्यतन किया गया:

UPDATE storage 
SET a21 = (3 * a11) + (-1 * a21), 
     a22 = (3 * a12) + (-1 * a22), 
     level = level - -1; 

UPDATE storage 
SET a11 = (2 * a11) + (-1 * (3*a11 - a21)), 
     a12 = (2 * a12) + (-1 * (3*a12 - a22)); 

संगामिति समस्याओं को रोकने के लिए, आप इन दो अद्यतन (यदि आरडीबीएमएस द्वारा समर्थित) एक सौदे के भीतर प्रदर्शन करने के लिए चाहिए।

+0

संदर्भ के लिए धन्यवाद। यह स्पष्ट करता है। मैं उम्मीद कर रहा था कि इस व्यवहार को बदलने की सेटिंग हो सकती है। –

+0

@PedroWerneck: क्या यह अद्यतन उत्तर बिल्कुल मदद नहीं करता है? – eggyal

+0

अच्छी तरह से देखा समाधान! उदाहरण के लिए, SQL सर्वर, 'डाले गए' और 'हटाए गए' छद्म-तालिकाओं के समान ही काम करता है। –

12

पोस्टग्रेएसक्यूएल, ओरेकल, और एसक्यूएल सर्वर सभी इसे परमाणु ऑपरेशन के रूप में देखते हैं। See the following SQL Fiddle, and switch the server to see the behavior of the following SQL:

CREATE TABLE Swap (
    a CHAR(1), 
    b CHAR(1) 
); 

INSERT INTO Swap (a, b) VALUES ('a', 'b'); 

UPDATE Swap SET a = b, b = a; 

SELECT * FROM Swap; 

MySQL केवल RBDMS कि दोनों अपडेट के बाद एक ही मूल्य वाले कॉलम के साथ इस लागू करता था।

जहां तक ​​आप इसे हल करेंगे, मैं इसके बजाय डेटाबेस से मूल्यों को खींचूंगा, अपने आवेदन के अंदर की गणना (आपके अपडेट स्टेटमेंट के बजाय), और फिर गणना किए गए मानों के साथ डेटाबेस अपडेट करें। इस तरह आप गारंटी दे सकते हैं कि गणना लगातार तरीके से की जाएगी।

+1

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

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