2012-06-18 37 views
5

Tôi có hai bảng như vậy;MySQL sửa các khoảng trống tự động trong hai bảng

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 

Có khoảng cách trong tự động id_image trong bảng đầu tiên. Trong bảng thứ hai, các id_image đề cập đến các id_image trong bảng đầu tiên, và có hai của mỗi ID trong đó.

Lưu ý: Bảng này là lý thuyết. Tôi không có ý tưởng trong đó khoảng cách chính xác hoặc có hoặc không có nhiều khoảng trống. Tất cả những gì tôi biết là giá trị đầu tiên là 1 và giá trị cuối cùng cao hơn tổng số hàng.

Bây giờ, tôi muốn khắc phục khoảng cách này.

Trước khi bạn nói rằng những khoảng trống không quan trọng và nếu chúng có, đó là thiết kế cơ sở dữ liệu xấu, hãy để tôi nói cho bạn biết; Tôi đồng ý với bạn.

Tuy nhiên, những gì tôi đang xử lý là hệ thống nguồn mở bên thứ ba (vô vọng phía sau) mà tôi cần nhập một lượng lớn dữ liệu hiện có không có ID tham chiếu chéo vào nhiều những cái bàn. Cách duy nhất tôi có thể đảm bảo rằng cùng một dữ liệu nhận được một ID phù hợp trong mỗi bảng trong suốt hệ thống là để nhập nó tuần tự, và điều đó có nghĩa là tôi không thể có khoảng trống.

Vì vậy, những gì tôi cần làm bây giờ là;

  1. Sửa khoảng trống trong cột id_image trong bảng đầu tiên, sao cho giá trị cuối cùng khớp với số hàng.
  2. Chỉnh sửa cột id_image trong bảng thứ hai để giá trị của nó tương ứng với cùng hàng tương ứng với trước khi sửa khoảng cách.

Tôi làm cách nào để bắt đầu thực hiện việc này? Tôi hiểu rằng điều này có thể nằm ngoài khả năng của ngôn ngữ truy vấn MySQL, vì vậy các câu trả lời của PHP cũng được chấp nhận. Cảm ơn! :)

+0

là động cơ innodb? – Sebas

+0

Vâng. Tuy nhiên khoảng cách này là do phải nhập lại một phần dữ liệu, không phải do lỗi INSERT IGNORE. :) –

+0

Bạn có thể khóa bảng và sau đó lặp qua nó với 'SELECT image_id MẪU bảng FORMER BY image_id ASC' và tìm thấy khoảng trống? –

Trả lời

1

Ý tưởng cơ bản ở đây là tìm tất cả khoảng trống trước để xác định số tiền bạn cần giảm mỗi id. Sau đó, bạn phải lặp qua cả hai bảng và áp dụng giảm. (Bạn sẽ cần phải thêm: host, db, người dùng, vượt qua, và tên bảng thực tế)

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); 
} 

Chú ý: Bạn có thể muốn ném này trong một tập tin và chạy qua CLI: php -f gapfix.php và bao gồm truy vấn trước $pdo->commit() trả về danh sách tất cả các id để bạn có thể xác minh hoạt động đã hoạt động như mong đợi. Nếu không, bạn có thể cuộn nó trở lại như thể không có gì xảy ra.Mã bây giờ tự kiểm tra xem bảng đầu tiên có đúng thứ tự hay không. Tuy nhiên nó không kiểm tra bảng thứ hai.Mọi kiểm tra đã được thực hiện!

+0

Bạn đang làm việc trên nó kiểm tra bảng thứ hai là tốt? :) –

+0

@EmphramStavanger Thêm nó khi chúng tôi nói ... –

+0

@EmphramStavanger Đã được triển khai! –

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; 

FK sẽ tự động cập nhật id của bảng thứ 2 cho phù hợp.

Tôi không chắc chắn chút nào nhưng trong một số phiên bản cũ hơn của mysql, cập nhật bảng mà bạn đang tham chiếu trong truy vấn phụ của bản cập nhật có thể gặp sự cố. Nếu vậy, chỉ cần tạo một bảng thứ 2 và điền vào nó (chèn), sau đó xóa cái cũ và đổi tên cái mới.

+2

+1 Phải là một người làm theo cách đó. NB mặc dù, cần phải reseed danh tính trên bảng đầu tiên, chèn tiếp theo khôn ngoan khác sẽ để lại một khoảng cách. –

+1

cảm ơn. Tôi sẽ sửa lỗi này – Sebas

+0

# 1452 - Không thể thêm hoặc cập nhật hàng con: ràng buộc khoá ngoài không thành công ('table2', CONSTRAINT' table2_ibfk_1' FOREIGN KEY ('id_image') TÀI LIỆU THAM KHẢO' table1' ('table1') ON DELETE CASCADE ON UPDATE CASCADE) - Có vấn đề gì ở đây? –

1

Đau đớn này.

Tạo một bảng như là người đầu tiên, không có bản sắc trên Id_Image và một int cột thêm gọi rownumber

Sử dụng các trick giả row_number để cư nó, một số như

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

Nếu bạn có một chìa khóa nước ngoài vào bảng thứ hai thả nó, sau đó nó là một bản cập nhật đơn giản với một tham gia. Thả bảng cũ1, đổi tên bảng mới, thêm danh tính và reseed, và đưa chìa khóa nước ngoài của bạn trở lại nếu bạn có một.

Bạn nhận ra rằng bạn sẽ phải tiếp tục làm điều này?

Có lẽ một cách thú vị để làm tất cả điều này trong một lần, nếu bạn có cập nhật tầng trên, nhưng chú ý cẩn thận đến kế hoạch thực hiện. Thủ thuật RowNumber chỉ hoạt động nếu mọi thứ được thực hiện theo thứ tự Id_Image. Nếu Mysql quyết định có cách hiệu quả hơn để thực hiện truy vấn ....

Các vấn đề liên quan