2012-06-18 19 views
5

मेरे पास दो टेबल हैं;MySQL फिक्सिंग ऑटोइनक्रिकमेंट अंतराल दो टेबलों में

id_image foo bar 
1   3  5 
2   8  1 
3   17  88 
7   14  23 
8   12  9 


id_image bar foo 
1   2  3 
1   5  6 
2   18  11 
2   10  12 
3   8  21 
3   17  81 
7   29  50 
7   1  14 
8   10  26 
8   27  34 

पहले तालिका में autoincremented id_image में एक अंतराल नहीं है। दूसरी तालिका में, id_image पहली तालिका में id_image को संदर्भित करता है, और वहां प्रत्येक आईडी में से दो हैं।

नोटिस: यह तालिका सैद्धांतिक है। मुझे नहीं पता है, जहां अंतर बिल्कुल ठीक है, या एकाधिक अंतराल भी हैं या नहीं। मुझे पता है कि पहला मान 1 है और अंतिम मूल्य कुल पंक्ति गणना से अधिक है।

अब, मैं इस अंतर को ठीक करना चाहता हूं।

इससे पहले कि आप कहें कि अंतराल कोई फर्क नहीं पड़ता और यदि वे करते हैं, तो यह खराब डेटाबेस डिज़ाइन है, मैं आपको बताता हूं; मैं आपसे सहमत हूं।

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

तो अब मुझे क्या करना है;

  1. पहली तालिका में id_image कॉलम में अंतर को ठीक करें, ताकि अंतिम मान पंक्ति गणना के साथ मेल खा सके।
  2. दूसरी तालिका में id_image कॉलम संपादित करें ताकि उसका मान उसी पंक्ति से मेल खाए जो अंतराल के ठीक पहले से मेल खाता है।

मैं यह कैसे शुरू करूं? मैं समझता हूं कि यह MySQL क्वेरी भाषा की क्षमताओं से बाहर हो सकता है, इसलिए PHP उत्तर भी स्वीकार्य हैं। धन्यवाद! :)

+0

क्या यह innodb इंजन है? – Sebas

+0

हां यह है। हालांकि अंतराल इग्नोर बग द्वारा नहीं, डेटा के एक हिस्से को फिर से दर्ज करने के कारण अंतर था। :) –

+0

क्या आप तालिका को लॉक कर सकते हैं और उसके बाद छवि_आईडी एएससी द्वारा 'चयन छवि_आईडी फॉर्म तालिका ऑर्डर' के साथ इसे फिर से भर सकते हैं और अंतराल ढूंढ सकते हैं? –

उत्तर

1

मूलभूत विचार यह निर्धारित करने के लिए पहले सभी अंतराल को ढूंढना है कि आपको प्रत्येक आईडी को कितना कम करने की आवश्यकता है। फिर, आपको दोनों तालिकाओं के माध्यम से पुनरावृत्ति करना होगा और कमी लागू करना होगा।

try { 
    $pdo = new PDO('mysql:host=HOST;dbname=DB', 'user', 'pass'); 

    $pdo->beginTransaction(); 

    // Iterate through all id's in the first table 
    $stmt = $pdo->exec('SELECT image_id FROM TableOne ORDER BY image_id ASC'); 
    $stmt->bindColumn('image_id', $id); 

    if(!$stmt->fetch(PDO::FETCH_BOUND)) { 
     throw Exception('No rows in table'); 
    } 

    $lastId = $id; 
    $gaps = array(); 

    // Find all the gaps 
    while($stmt->fetch(PDO::FETCH_BOUND)) { 
     if($id != ($lastId + 1)) { 
      $gaps[] = $id; 
     } 

     $lastId = $id; 
    } 


    if(!isset($gaps[0])) { 
     throw new Exception('No gaps found'); 
    } 

    // For each gap, update the range from the last gap to that gap by subtracting 
    // the number of gaps there has been from the id 
    $lastGap = $gaps[0]; 

    for($i = 1; $i < count($gaps); $i++) { 
     $stmt = $pdo->prepare('UPDATE TableOne SET image_id = image_id - :i WHERE image_id BETWEEN :lastGap AND :gap'); 
     $stmt->execute(array(
      ':i' => $i, 
      ':lastGap' => $lastGap, 
      ':gap' => $gaps[$i] 
     )); 

     $stmt = $pdo->prepare('UPDATE TableTwo SET image_id = image_id - :i WHERE image_id BETWEEN :lastGap AND :gap'); 
     $stmt->execute(array(
      ':i' => $i, 
      ':lastGap' => $lastGap, 
      ':gap' => $gaps[$i] 
     )); 

     $lastGap = $gaps[$i]; 
    } 

    // Finally, fix the gap between the last found gap and the end of the table 
    $stmt = $pdo->prepare('UPDATE TableOne SET image_id = image_id - :i WHERE image_id BETWEEN :lastGap AND :gap'); 
    $stmt->execute(array(
     ':i' => $i, 
     ':lastGap' => $lastGap, 
     ':gap' => $gaps[$i] 
    )); 

    $stmt = $pdo->prepare('UPDATE TableTwo SET image_id = image_id - :i WHERE image_id BETWEEN :lastGap AND :lastId'); 
    $stmt->execute(array(
     ':i' => $i, 
     ':lastGap' => $lastGap, 
     ':lastId' => $lastId 
    )); 

    // Verify everything is correct 
    $stmt = $pdo->exec('SELECT image_id FROM TableOne ORDER BY image_id ASC'); 
    $stmt->bindColumn('image_id', $id); 

    if(!$stmt->fetch(PDO::FETCH_BOUND)) { 
     throw new Exception('No rows'); // Should never be thrown 
    } 

    $lastId = $id; 

    while($stmt->fetch(PDO::FETCH_BOUND)) { 
     if($id != ($lastId + 1)) { 
      throw new Exception('There was an error between ids ' . $lastId . ' and '. $id); 
     } 

     $lastId = $id; 
    } 

    $stmt = $pdo->exec('SELECT image_id FROM TableTwo ORDER BY image_id ASC'); 
    $stmt->bindColumn('image_id', $id); 

    if(!$stmt->fetch(PDO::FETCH_BOUND)) { 
     throw new Exception('No rows in table two'); // Shouldn't hit this 
    } 

    $lastId = $id; 
    $ids = array($id); 

    while($stmt->fetch(PDO::FETCH_BOUND)) { 
     $ids[] = $id; 

     if(count($ids) == 2) { 
      if($ids[0] !== $ids[1]) { 
       throw new Exception('Table two error on ids '); 
      } 

      if($ids[0] !== $lastId) { 
       throw new Exception('Table two error on id gapfix'); 
      } 

      $lastId = $ids[0]; 
      $ids = array(); 
     } 
    } 

    $pdo->commit(); 
} catch(Exception $e) { 
    $pdo->rollBack(); 

    var_dump($e); 
} 

महत्वपूर्ण: (और वास्तविक तालिका नाम मेजबान, डाटाबेस, उपयोगकर्ता, गुजरती हैं, आप जोड़ने की आवश्यकता होगी): आप CLI के माध्यम से एक फ़ाइल में इस फेंक और चलाने के लिए चाहते हो सकता है: php -f gapfix.php और $pdo->commit() से पहले एक क्वेरी शामिल करें जो सभी आईडी की एक सूची लौटाती है ताकि आप अपेक्षित कार्यवाही को सत्यापित कर सकें। यदि ऐसा नहीं होता है, तो आप इसे वापस रोल कर सकते हैं जैसे कुछ भी नहीं हुआ।कोड पहली बार सही क्रम में है तो कोड स्वयं के लिए जांचता है। हालांकि यह अभी तक दूसरी तालिका की जांच नहीं करता है।सभी जांच लागू की गई है!

+0

आप दूसरी टेबल की जांच करने पर भी काम कर रहे हैं? :) –

+0

@EmphramStavanger इसे जोड़ने के रूप में हम ... –

+0

@EmphramStavanger कार्यान्वित! –

3
ALTER TABLE table2 
ADD FOREIGN KEY FK_IMAGE (id_image) 
REFERENCES table1 (id_image) 
ON DELETE CASCADE 
ON UPDATE CASCADE; 

SET @currentRow = 0; 

UPDATE table1 INNER JOIN (
    SELECT @currentRow := @currentRow + 1 AS id_image_new, id_image AS id_image_old 
    FROM table1 
    ORDER BY id_image ASC) t on t.id_image_old = table1.id_image 
SET table1.id_image = t.id_image_new; 

ALTER TABLE table1 AUTO_INCREMENT = 1; 

एफके स्वचालित रूप से तदनुसार आपकी दूसरी तालिका के आईडी अपडेट करेगा।

मुझे यकीन नहीं है कि mysql के कुछ पुराने संस्करणों में, एक तालिका को अद्यतन करें जिसे आप अद्यतन के सबक्वायरी में संदर्भित कर रहे हैं, क्रैश हो सकता है। यदि ऐसा है, तो बस एक दूसरी तालिका बनाएं और इसे भरें (सम्मिलित करें), फिर पुराने को हटाएं और नया नाम बदलें।

+2

+1 ऐसा करना था एक रास्ता तय करना। एनबी हालांकि, पहली तालिका में पहचान को संशोधित करने की आवश्यकता है, अन्य बुद्धिमान अगले सम्मिलन एक अंतर छोड़ देंगे। –

+1

धन्यवाद। मैं इसे – Sebas

+0

# 1452 ठीक कर दूंगा - एक बाल पंक्ति को जोड़ या अपडेट नहीं कर सकता: एक विदेशी कुंजी बाधा विफल हो जाती है ('table2', CONSTRAINT' table2_ibfk_1' विदेशी कुंजी ('id_image') संदर्भ 'तालिका 1' (' table1') हटाएं CASCADE पर अद्यतन कैस्केड पर) - यहां क्या समस्या है? –

1

दर्दनाक यह।

पहले की तरह एक तालिका बनाएं, Id_Image पर कोई पहचान नहीं है और एक अतिरिक्त पूर्णांक स्तंभ rownumber

कहा जाता है यह पॉप्युलेट करने के लिए छद्म ROW_NUMBER चाल का उपयोग करें,

Insert into NewTable 
Select id_image,foo,bar,@RowNumber := @RowNumber + 1 order by id_image. 

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

आपको पता है कि आपको यह बकवास करना जारी रखना होगा?

यदि आप परस्पर अपडेट हैं, लेकिन निष्पादन योजना पर सावधानीपूर्वक ध्यान दें, तो यह सब एक बार में ऐसा करने का एक मजेदार तरीका है। RowNumber चाल केवल तभी काम करती है जब आईडी_इमेज ऑर्डर में चीजें की जाती हैं। यदि MySQL निर्णय लेता है कि क्वेरी करने का एक और अधिक प्रभावी तरीका है ....

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