3

我想删除数据库中的一行,我有 2 个选项;一是用普通列删除行,二是主键?

我知道主键更好,但为什么呢?

4

4 回答 4

5

在 MySql 上,当使用非主键列删除/更新行时,您可能会在多用户环境中遇到奇怪的锁定行为。
这是一个示例 - 两个会话试图删除行(自动提交已禁用)。

C:\mysql\bin>mysql
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.5.29-log MySQL Community Server (GPL)

Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.

mysql> create table test(
    ->   id int primary key,
    ->   val int
    -> );
Query OK, 0 rows affected (0.02 sec)

......

mysql> select * from test;
+----+------+
| id | val  |
+----+------+
|  1 |    1 |
|  2 |    2 |
|  3 |    3 |
|  4 |    4 |
|  5 |    5 |
|  6 |    6 |
+----+------+
6 rows in set (0.00 sec)



现在在会话 1 中,我们将使用主键删除第 5 行

mysql> delete from test where id = 5;
Query OK, 1 row affected (0.00 sec)

然后在会话 2 中,我们也使用 PK 删除第 2 行

mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

mysql> delete from test where id = 2;
Query OK, 1 row affected (0.00 sec)

一切看起来都不错——第 5 行被会话 1 删除,第 2 行在会话 2 中被删除


现在看看当我们尝试使用非主键删除行时会发生什么:
会话 1

mysql> rollback;
Query OK, 0 rows affected (0.00 sec)

mysql> delete from test where val = 5;
Query OK, 1 row affected (0.00 sec)

和会话 2

mysql> rollback;
Query OK, 0 rows affected (0.00 sec)

mysql> delete from test where val = 2;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql>

会话 2 中的删除命令“挂起”,大约一分钟后它会引发错误:锁定等待超时
让我们尝试删除其他行:

mysql> delete from test where val = 4;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> delete from test where val = 6;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql>

会话 1 仅删除第 5 行,并且从逻辑上讲,应该只在要删除的第 5 记录上放置一个锁,但是正如您在这些示例中看到的,当不使用主键时,MySql对整个表的所有行都加了锁. 因此,仅使用主键(至少在 MySql 上)删除行更安全。

于 2013-07-13T22:52:35.243 回答
2

主键更好,因为您确定要删除哪一行:虽然从技术上讲您可以更新主键列,但这样做不是正常做法。但是,其他列是可变的,这可能导致如下情况:

  • 您有一个带有 aPK和另一个唯一标识符的表,例如,email
  • 您阅读了一行电子邮件sample_email@gmail.com,并决定将其删除
  • 该行被同时修改,电子邮件更新为simple_email@gmail.com
  • 你执行DELETE USER WHERE email='sample_email@gmail.com'

DELETE命令不会删除任何内容,因为在您设法运行命令之前,电子邮件已被更改。由于PK不应该改变,这种情况在正常情况下是不可能的。当然,您的代码可以检测到没有发生删除,重做读取,然后重新发出命令,但与使用主键相比,这是很多工作。

于 2013-07-13T21:30:21.383 回答
1

我假设您的意思是这样的声明:

delete from table
    where column = value

当列是主键时,它会自动有一个唯一索引(至少在我知道的数据库中)。这样可以快速找到要删除的记录。

具有索引的另一列几乎一样快,因为它可以使用索引查找。

没有索引的列会慢得多,因为查询必须进行全表扫描。

于 2013-07-13T21:28:27.490 回答
0

行由superkeys唯一标识,包括候选键。主键是一个候选键,但不一定是唯一一个。

没有根本原因为什么主键必须始终是指定更新、删除或其他操作的“更好”方式。使用最能表达预期更新的属性,尤其要记住某些属性值可能会发生变化。经常选择主键是因为它是一个被认为不太可能改变的候选键,或者因为它是更新的“首选”标识符。

假设一个表有两个键:j 和 k,其中 k 被指定为主键。如果用户实际上想要基于 j 的值执行更新:DELETE ... WHERE j=123;那么根据有效的事务隔离级别和一属性的稳定性,更新可能表达与基于相应 k 值的类似更新完全不同的意图某个时间点。无论哪个属性发生变化,都是如此。因此,如果您担心更改键值的影响,那么您应该考虑选择哪种键最能表达用户的预期更新。假设候选键值更改很少见,那么为所有更新使用指定的主键通常是“默认”假设,因为始终使用相同的键使编码更简单。

于 2013-07-14T08:35:17.320 回答