Sắp xếp lại / đặt lại khóa chính tăng tự động


127

Tôi có một bảng MySQL với khóa chính tăng tự động. Tôi đã xóa một số hàng ở giữa bàn. Bây giờ tôi có, ví dụ, một cái gì đó như thế này trong cột ID: 12, 13, 14, 19, 20. Tôi đã xóa các hàng 15, 16, 17 và 18.

Tôi muốn gán lại / đặt lại / sắp xếp lại khóa chính để tôi có tính liên tục, tức là thực hiện 19 a 15, 20 a 16, v.v.

Tôi làm nó như thế nào?

Câu trả lời:


95

Bạn có thể thả cột khóa chính và tạo lại nó. Tất cả các id sau đó nên được sắp xếp lại theo thứ tự.

Tuy nhiên đây có lẽ là một ý tưởng tồi trong hầu hết các tình huống. Nếu bạn có các bảng khác có khóa ngoại đối với bảng này thì chắc chắn nó sẽ không hoạt động.


Tôi có các bảng khác chứa khóa ngoại cho bảng này, nhưng tôi mới bắt đầu dự án nên nó ổn với tôi .. Cảm ơn!
Jonathan

65
Có thể là lâu dài tốt nhất nếu bạn cố gắng và bắt đầu chấp nhận rằng ID của bạn sẽ không phải là tuần tự, nếu không, khi bạn bắt đầu làm việc với các dự án lớn hơn, nó sẽ thực sự phát điên!
Ciaran McN Khoa


356

Mặc dù câu hỏi này có vẻ khá cũ, nhưng sẽ đăng câu trả lời cho người tìm đến đây.

SET @count = 0;
UPDATE `users` SET `users`.`id` = @count:= @count + 1;

Nếu cột được sử dụng làm khóa ngoại trong các bảng khác, hãy đảm bảo bạn sử dụng ON UPDATE CASCADEthay vì mặc định ON UPDATE NO ACTIONcho mối quan hệ khóa ngoại trong các bảng đó.

Hơn nữa, để thiết lập lại AUTO_INCREMENTsố đếm, bạn có thể đưa ra tuyên bố sau ngay lập tức.

ALTER TABLE `users` AUTO_INCREMENT = 1;

Đối với MySQL, nó sẽ đặt lại giá trị MAX(id) + 1.


Điều này với các khóa ngoại là một giải pháp rất hay cho bảng của tôi, nơi có rất nhiều rác được chèn và xóa và tôi muốn tiết kiệm không gian chỉ mục.
mukunda

1
myQuery Doc khuyên bạn nên chống lại điều này: "Theo quy tắc chung, ngoài các câu lệnh SET, bạn không bao giờ nên gán giá trị cho biến người dùng và đọc giá trị trong cùng một câu lệnh. Ví dụ: để tăng một biến, điều này không sao: SET @a = @a + 1; Đối với các câu lệnh khác, chẳng hạn như CHỌN, bạn có thể nhận được kết quả mà bạn mong đợi, nhưng điều này không được đảm bảo. Trong câu lệnh sau, bạn có thể nghĩ rằng MySQL sẽ đánh giá @a trước rồi thực hiện bài tập thứ hai: CHỌN @a, @a: = @ a + 1, ...; Tuy nhiên, thứ tự đánh giá các biểu thức liên quan đến các biến của người dùng là không xác định. "
ReverseEMF

3
@ReverseEMF: Không. Thứ tự gán được cố định trong các biểu thức MySQL. Từ những gì bạn đã trích dẫn, tài liệu MySQL khuyên chống lại việc sử dụng nhiều biến độc lập. Trong trường hợp trên, việc đánh giá biểu thức bị ràng buộc xảy ra theo thứ tự được xác định trước do một biểu thức gán đơn `` users .id` = @count: = @count + 1`. Từ tài liệu: "Giá trị ở phía bên tay phải có thể là giá trị bằng chữ, một biến khác lưu trữ giá trị hoặc bất kỳ biểu thức pháp lý nào mang lại giá trị vô hướng"
Anshul

1
Đây có phải là một tuyên bố rất tốn kém? Làm thế nào nó sẽ thực hiện trong một bảng nhiều gigabyte? Tôi sợ làm nổ tung ibdata1 (giao dịch dài) và chặn bảng quá lâu.
Stefan

1
@Stefan Trên một bảng (5MB) có khóa ngoại tham chiếu một tệp khác có + 2GB dữ liệu, tập lệnh này không mất hơn năm phút. Hệ thống có một ssd, vì vậy tôi cho rằng nó đã giúp rất nhiều. Các fk đã có CASCADE CẬP NHẬT
fernandezr

60

Để đặt lại ID của bảng Người dùng của tôi, tôi sử dụng truy vấn SQL sau. Ở trên đã nói rằng điều này sẽ phá hỏng mọi mối quan hệ bạn có thể có với bất kỳ bảng nào khác.

ALTER TABLE `users` DROP `id`;
ALTER TABLE `users` AUTO_INCREMENT = 1;
ALTER TABLE `users` ADD `id` int UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;

Trên bảng MyISAM có 584k hàng, mất khoảng 7,3 giây.
1278519

3
Nếu tôi có 100 phiếu, tôi sẽ bỏ phiếu này mọi lúc vì nó phá vỡ các tuyên bố sql cho một người không chuyên như tôi giúp hiểu
repzero

1
Dòng thứ hai là không cần thiết hoặc tôi sai? Mặc dù vậy, nó bắt đầu bằng 1 ,,,, đối với InnoDB, điều đó đã làm cho tôi
Thielicy

dòng thứ hai không cần thiết.
KawaiKx

1
@ JorgeAugustoMorêradeMoura thứ tự của các hồ sơ sẽ không được thay đổi
Ryan

31

Bạn chỉ có thể sử dụng truy vấn này

alter table abc auto_increment = 1;

2
Điều này sẽ không làm việc trong trường hợp này. Đối với các bảng ISAM, nó sẽ đặt giá trị autoinc thành max (id) + 1. Đối với InnoDB, nó sẽ không làm gì cả. Xem các tài liệu bảng thay đổi để thay đổi AUTOINCREMENT dev.mysql.com/doc/refman/5.0/en/alter-table.html
lreeder

3
@Ireeder từ 5.6 trở đi hành vi cho innodb tương tự như hành vi của myisam
Anshul

Nếu bạn có các bảng khác có khóa ngoại đối với bảng này thì điều này có phá vỡ chúng không?
Kyle Vassella

15
SET  @num := 0;

UPDATE your_table SET id = @num := (@num+1);

ALTER TABLE your_table AUTO_INCREMENT =1;

Tôi nghĩ rằng điều này sẽ làm điều đó


12

Hoặc, từ PhpMyAdmin, xóa cờ "AutoIncrement", lưu, đặt lại và lưu. Điều này đặt lại nó.


Xin lỗi, tôi không thể kiểm tra với các phiên bản phpmyadmin hiện tại. Câu trả lời của tôi đã khá cũ ... Nếu bạn đánh giá thấp tôi, bạn có thể vui lòng sửa đổi nó không?
lbrutti

3
SELECT * from `user` ORDER BY `user_id`; 

SET @count = 0;

UPDATE `user`  SET `user_id` = @count:= @count + 1;

ALTER TABLE `user_id` AUTO_INCREMENT = 1;

nếu bạn muốn order by


1

trong phpmyadmin

lưu ý: điều này sẽ hoạt động nếu bạn xóa các hàng cuối cùng không phải hàng giữa.

goto bảng của bạn-> nhấp vào menu hoạt động-> tùy chọn bảng goto-> thay đổi AUTO_INCREMENT thành không có từ nơi bạn muốn bắt đầu.

bảng tự động của bạn bắt đầu từ đó không.

thử nó. nhập mô tả hình ảnh ở đây


0

Bạn có thể loại bỏ chức năng tăng tự động khóa chính của cột đó, sau đó mỗi khi bạn cập nhật cột đó sẽ chạy một truy vấn trước khi đếm tất cả các hàng trong bảng, sau đó chạy một vòng lặp lặp qua số hàng đó chèn từng giá trị vào hàng tương ứng và cuối cùng chạy một truy vấn chèn một hàng mới với giá trị của cột đó là tổng số hàng cộng với một. Điều này sẽ làm việc hoàn hảo và là giải pháp tuyệt đối nhất cho ai đó đang cố gắng hoàn thành những gì bạn đang có. Dưới đây là một ví dụ về mã bạn có thể sử dụng cho hàm:

$table_row_count = mysql_result(mysql_query("SELECT COUNT(`field_1`) FROM `table`"), 0);
$viewsrowsdata = mysql_query("
    SELECT `rank`, `field1`, `field2`, `field3`, `field4`
        FROM (SELECT (@rank:=@rank+1) as `rank`, `field1`, `field2`, `field3`, `field4`
            FROM (SELECT * FROM `views`) a
            CROSS JOIN (SELECT @rank:=0) b
            ORDER BY rank ASC) c
");
while ($row = mysql_fetch_assoc($viewsrowsdata)) {
    $data[] = $row;
}
foreach ($data as $row) {
    $new_field_1 = (int)$row['rank'];
    $old_field_1 = (int)$row['field1'];
    mysql_query("UPDATE `table` SET `field_1` = $new_field_1 WHERE `field_1` = $old_field_1");
}
mysql_query("INSERT INTO `table` (`field1`, `field2`, `field3`, `field4`) VALUES ('$table_row_count' + 1, '$field_2_value', 'field_3_value', 'field_4_value')");

Ở đây tôi đã tạo ra một mảng kết hợp mà tôi đã thêm vào một cột thứ hạng với truy vấn trong một truy vấn chọn, nó cung cấp cho mỗi hàng một giá trị xếp hạng bắt đầu bằng 1. Sau đó tôi lặp lại qua mảng kết hợp.

Một tùy chọn khác sẽ có được số đếm hàng, chạy một truy vấn chọn cơ bản, lấy mảng kết hợp và lặp lại nó theo cùng một cách nhưng với một biến được thêm vào cập nhật qua mỗi lần lặp. Điều này kém linh hoạt nhưng sẽ hoàn thành điều tương tự.

$table_row_count = mysql_result(mysql_query("SELECT COUNT(`field_1`) FROM `table`"), 0);
$viewsrowsdata = mysql_query("SELECT * FROM `table`");
$updated_key = 0;
while ($row = mysql_fetch_assoc($viewsrowsdata)) {
    $data[] = $row;
}
foreach ($data as $row) {
    $updated_key = $updated_key + 1;
    mysql_query("UPDATE `table` SET `field_1` = '$updated_key' WHERE `field_1` = '$row['field_1']'");
}
mysql_query("INSERT INTO `table` (`field1`, `field2`, `field3`, `field4`) VALUES ('$table_row_count' + 1, '$field_2_value', 'field_3_value', 'field_4_value')");

0

đối với InnoDB, hãy làm điều này (điều này sẽ xóa tất cả các bản ghi khỏi bảng, tạo một bakcup trước):

SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS ;
SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION ;
SET NAMES utf8 ;
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 ;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 ;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' ;
SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 ;
/* ================================================= */

drop table tablename;
CREATE TABLE `tablename` (
   table structure here!

) ENGINE=InnoDB AUTO_INCREMENT=  ai number to reset  DEFAULT CHARSET= char set here;



/* ================================================= */
SET SQL_MODE=@OLD_SQL_MODE ;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS ;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS ;
SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT ;
SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS ;
SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION ;
SET SQL_NOTES=@OLD_SQL_NOTES ;

0

Tôi có cùng nghi ngờ, nhưng không thể thực hiện bất kỳ thay đổi nào trên bàn, tôi quyết định thực hiện như sau khi thấy ID của tôi không vượt quá số lượng tối đa được giải quyết trong biến @count:

SET @count = 40000000;
UPDATE `users` SET `users`.`id` = @count:= @count + 1;

SET @count = 0;
UPDATE `users` SET `users`.`id` = @count:= @count + 1;

ALTER TABLE `users` AUTO_INCREMENT = 1;

Giải pháp là có, nhưng nó an toàn và nó là cần thiết bởi vì bảng của tôi sở hữu các khóa ngoại có dữ liệu trong một bảng khác.


0

Sự lựa chọn tốt nhất là thay đổi cột và xóa thuộc tính auto_increment. Sau đó đưa ra một tuyên bố thay đổi khác và đặt auto_increment trở lại cột. Điều này sẽ đặt lại số đếm thành tối đa + 1 của các hàng hiện tại và do đó duy trì các tham chiếu khóa ngoại trở lại bảng này, từ các bảng khác trong cơ sở dữ liệu của bạn hoặc bất kỳ cách sử dụng khóa nào khác cho cột đó.


0

Ý kiến ​​của tôi là tạo một cột mới gọi là row_order. sau đó sắp xếp lại cột đó. Tôi không chấp nhận các thay đổi đối với khóa chính. Ví dụ: nếu cột thứ tự là banner_poseition, tôi đã làm một cái gì đó như thế này, Đây là để xóa, cập nhật, tạo cột vị trí banner. Gọi hàm này sắp xếp lại chúng tương ứng.

public function updatePositions(){
    $offers = Offer::select('banner_position')->orderBy('banner_position')->get();
    $offersCount = Offer::max('banner_position');
    $range = range(1, $offersCount);

    $existingBannerPositions = [];
    foreach($offers as $offer){
        $existingBannerPositions[] = $offer->banner_position;
    }
    sort($existingBannerPositions);
    foreach($existingBannerPositions as $key => $position){
        $numbersLessThanPosition = range(1,$position);
        $freshNumbersLessThanPosition = array_diff($numbersLessThanPosition, $existingBannerPositions);
        if(count($freshNumbersLessThanPosition)>0) {
            $existingBannerPositions[$key] = current($freshNumbersLessThanPosition);
            Offer::where('banner_position',$position)->update(array('banner_position'=> current($freshNumbersLessThanPosition)));
        }
    }
}

0

Điều này hoạt động - https://stackoverflow.com/a/5437720/10219008.....nhưng nếu bạn gặp phải vấn đề 'Mã lỗi: 1265. Dữ liệu bị cắt cho cột' id 'ở hàng 1' ... Sau đó chạy sau đây Thêm bỏ qua trên các truy vấn cập nhật.

SET @count = 0;
set sql_mode = 'STRICT_ALL_TABLES';
UPDATE IGNORE web_keyword SET id = @count := (@count+1);

-2

Bạn cũng có thể đơn giản tránh sử dụng ID số làm Khóa chính. Bạn có thể sử dụng mã quốc gia làm id chính nếu bảng chứa thông tin quốc gia hoặc bạn có thể sử dụng permalinks, nếu nó giữ các bài viết chẳng hạn.

Bạn cũng có thể chỉ cần sử dụng ngẫu nhiên hoặc giá trị MD5. Tất cả các tùy chọn này đều có lợi ích riêng, đặc biệt là trên giây CNTT. ID số dễ dàng liệt kê.


1
... Bạn căn cứ điều này trên gì? Có lẽ chỉ cần không tạo các trang như "Complete_user_info_export.php? Userid = 34"? Trong nội bộ sử dụng một chuỗi hoặc giá trị ngẫu nhiên khác làm chỉ mục / định danh là một ý tưởng thực sự tồi tệ. Nó tạo ra nhiều vấn đề hơn nó giải quyết (nếu nó thậm chí còn giải quyết được bất kỳ vấn đề nào)
Rob

Giá trị MD5 là khả năng xấu nhất tuyệt đối vì có thể hai giá trị hoặc bộ dữ liệu khác nhau tạo ra cùng một giá trị MD5. Vì vậy, tôi sẽ không gọi nó là một giải pháp.
David
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.