为什么我的`delete where id in`语句会删除表中的所有记录?

时间:2019-11-12 10:28:15

标签: mysql sql sql-delete

我正在使用MySQL 5.7.24版本。我想删除回复表中具有相同ex_id和ex_type的记录:

CREATE TABLE `reply` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `content` varchar(1024) NOT NULL,
  `ex_id` bigint(20) DEFAULT '0',
  `ex_type` int(11) DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `idx_ex_id_type` (`ex_id`,`ex_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

具有以下数据:

+----+-------------------+-------+---------+
| id | content           | ex_id | ex_type |
+----+-------------------+-------+---------+
|  1 | this is a content |     1 |       1 |
|  2 | this a test       |     2 |       1 |
|  3 | this a contet     |     1 |       1 |
|  4 | the 4th content   |     3 |       1 |
+----+-------------------+-------+---------+

记录1和3共享相同的ex_id和ex_type,我想删除ID较小的记录(记录1),所以我编写了以下查询:

delete from reply where id in (
    select id from (
        select min(id) from reply group by ex_type and ex_id having count(1) > 1
    ) tmp
)
-- Query OK, 4 rows affected

这应该删除一条记录,但是所有记录都将被删除。

实际上此SQL中有一个错误,内部SQL select min(id) from reply group by ex_type and ex_id having count(1) > 1返回仅包含字段的结果:'min(id)',外部SQL select id from () tmp选择一个不存在的字段ID,从而导致错误,但MySQL仍执行此sql并删除所有记录。

我想知道为什么会这样。

3 个答案:

答案 0 :(得分:1)

您的查询在逻辑上是错误的。
这部分:

select min(id) from reply group by ex_type and ex_id having count(1) > 1

ex_type and ex_id而不是ex_type, ex_id分组。
最重要的是,它不会返回名为/别名为id的列。
这样:

select id from...

实际上是引用该表的id并返回该表的id all ,结果是所有行均被删除。
您可以看到此行为here
我相信这就是您想要做的:

delete from reply where id in 
(
    select id from (
        select min(id) id from reply group by ex_type, ex_id having count(*) > 1
    ) tmp
); 

答案 1 :(得分:1)

  

...外部sql select id from () tmp选择不存在的字段ID   导致错误,但是mysql执行此sql并删除所有   记录。

     

我想知道为什么会这样。

此子查询将不会单独运行:

select id from (
    select min(id) from reply group by ex_type and ex_id having count(1) > 1
) tmp
/* SQL Error (1054): Unknown column 'id' in 'field list' */

但是当它在子查询中运行时,根据作用域解析规则, id列解析为外部查询的id列,因为FROM子句中不存在所请求的列 。查询本质上是这样的:

delete from reply where id in (
    select reply.id from (
        select min(id) from reply group by ex_type and ex_id having count(1) > 1
    ) tmp
)
/* Affected rows: 4  Found rows: 0  Warnings: 0  Duration for 1 query: 0.031 sec. */

由于1 IN(1),2 IN(2),3 IN(3)...都为真,因此所有行的条件均为true。修正拼写错误(group by ex_type and ex_id)不会解决问题,请将查询更改为此:

delete from reply where id in (
    select tmp.id from (
        select min(id) as id from reply group by ex_type, ex_id having count(1) > 1
    ) tmp
)

答案 2 :(得分:0)

有趣-这似乎是一个错误,因为在这种情况下DELETE应该失败!

无论如何,只要在min(id)上附加一个别名(并在GROUP BY之前删除那个奇怪的'and'),一切都会好起来的...(尽管我不会这样写查询) / p>

DROP TABLE IF EXISTS reply;

CREATE TABLE `reply` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `content` varchar(1024) NOT NULL,
  `ex_id` bigint(20) DEFAULT '0',
  `ex_type` int(11) DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `idx_ex_id_type` (`ex_id`,`ex_type`)
);

INSERT INTO reply VALUES
(1,'this is a content',1,1),
(2,'this a test',2,1),
(3,'this a contet',1,1),
(4,'the 4th content',3,1);


delete from reply where id in 
(
    select id from (
        select min(id) id from reply group by ex_type, ex_id having count(1) > 1
    ) tmp
);

SELECT * FROM reply;
+----+-----------------+-------+---------+
| id | content         | ex_id | ex_type |
+----+-----------------+-------+---------+
|  2 | this a test     |     2 |       1 |
|  3 | this a contet   |     1 |       1 |
|  4 | the 4th content |     3 |       1 |
+----+-----------------+-------+---------+

FWIW,对于较小的数据集,我可能会以这种方式编写该查询...

DELETE r 
  FROM reply r
  JOIN 
     ( SELECT MIN(id) id 
         FROM reply 
        GROUP 
           BY ex_type
            , ex_id 
       HAVING COUNT(0) > 1
     ) x
    ON x.id = r.id