UPDATE 時主鍵衝突引發的思考

2021-09-03 04:41:35 字數 2958 閱讀 1653

假設有乙個表,結構如下:

mysql> create table `a` (

`id` int(10) unsigned not null auto_increment,

`id2` int(10) unsigned not null default '0',

primary key (`id`)

) engine=myisam;

該表中只有6條記錄,如下:

mysql> select * from a;

+----+---------+

| id | city_id |

+----+---------+

| 2 | 2 |

| 3 | 3 |

| 5 | 5 |

| 4 | 4 |

| 6 | 6 |

| 7 | 7 |

+----+---------+

現在想要把id欄位分別-1,執行以下語句,得到報錯:

mysql> update a set id=id-1;

error 1062 (23000): duplicate entry '4' for key 'primary'

看看更新後的結果,可以看到:

mysql> select * from a;

+----+---------+

| id | city_id |

+----+---------+

| 1 | 2 |

| 2 | 3 |

| 5 | 5 |

| 4 | 4 |

| 6 | 6 |

| 7 | 7 |

+----+---------+

儲存在最前面的2條記錄更新成功了,後面的則失敗,因為第三條記錄如果也要更新,則會引發主鍵衝突。

這個時候,如果我們在更新時增加

order by 的話,則可以順利更新成功。

mysql> update a set id=id-1 order by id;

query ok, 6 rows affected (0.00 sec)

rows matched: 6 changed: 6 warnings: 0

接下來,我們看看把它轉成 innodb 表,結果會是怎樣的。

mysql> alter table a engine = innodb;

query ok, 6 rows affected (0.01 sec)

records: 6 duplicates: 0 warnings: 0

mysql> select * from a;

+----+---------+

| id | city_id |

+----+---------+

| 2 | 2 |

| 3 | 3 |

| 4 | 4 |

| 5 | 5 |

| 6 | 6 |

| 7 | 7 |

+----+---------+

看到變化了吧,行資料按照 id 的順序來顯示了。

清空後,自己重新手工插入記錄,再看看。

mysql> insert into `a` values (2,2),(3,3),(5,5),(4,4),(6,6),(7,7);

query ok, 6 rows affected (0.00 sec)

records: 6 duplicates: 0 warnings: 0

mysql> select * from a;

+----+---------+

| id | city_id |

+----+---------+

| 2 | 2 |

| 3 | 3 |

| 4 | 4 |

| 5 | 5 |

| 6 | 6 |

| 7 | 7 |

+----+---------+

還是按照 id 的順序來顯示,然後我們再次執行之前的 update 語句:

mysql> update a set id = id - 1;

query ok, 6 rows affected (0.00 sec)

rows matched: 6 changed: 6 warnings: 0

可以看到,在 innodb 表的情況下,更新是可以成功的。

現在我們來分析下。

myisam表是堆組織表(heap organize table, hot),它的索引是採用b-tree方式儲存的,資料顯示時是隨機順序,而非按照主鍵的索引順序來顯示。

而innodb表是索引組織表(index organized table, iot),它的索引則是採用clustered index方式,因此主鍵會按照順序儲存,每次有記錄有更新時,會重新整理更新其主鍵。因此無論是直接從 myisam 表轉換過來的,還是後來插入的記錄,顯示時都會按照主鍵的順序。

更新資料時,如果沒有指定排序的字段或索引,則預設以隨機順序更新,所以 myisam 表如果不指定

order by 的話,則採用預設的儲存順序來更新,所以會發生主鍵衝突的情況。

而 innodb 表總是有主鍵(如果沒有定義,則也有預設主鍵),如果更新時沒有指定排序欄位或索引,則按照主鍵順序來更新,在上面的例子中,就是按照主鍵 id 的順序來更新了,因此不會報錯。

本文出自 「mysql中文網」部落格

UPDATE 時主鍵衝突引發的思考

假設有乙個表,結構如下 mysql create table a id int 10 unsigned not null auto increment,id2 int 10 unsigned not null default 0 primary key id engine myisam 該表中只有6...

insert時出現主鍵衝突的處理方法

使用 insert into 語句進行資料庫操作時可能遇到主鍵衝突,使用者需要根據應用場景進行忽略或者覆蓋等操作。總結下,有三種解決方案來避免出錯。測試表 create table device devid mediumint 8 unsigned not null auto increment,s...

選擇的UPDATE 主鍵互換

資料庫 twt001 資料表 aupkey 參考文章 sql中的case when用法 例,有如下更新條件 工資以上的職員,工資減少 工資在到之間的職員,工資增加 很容易考慮的是選擇執行兩次update語句,如下所示 條件 update personnel set salary salary 0.9...