Php 删除重复记录而不创建临时表
我有一个包含许多重复记录的表:Php 删除重复记录而不创建临时表,php,mysql,Php,Mysql,我有一个包含许多重复记录的表: shop ID tax_id 1 10 1 10 1 11 2 10 2 12 2 10 2 10 我想删除所有重复记录,而不创建临时表。 执行更新查询后,该表应如下所示: shop ID tax_id 1 10 1 11 2 10 2 12 首先,您可以通过在这两个字段上创建一个唯一索引来防止这种情况,以供将来参考 对于解决方案,在m
shop
ID tax_id
1 10
1 10
1 11
2 10
2 12
2 10
2 10
我想删除所有重复记录,而不创建临时表。
执行更新查询后,该表应如下所示:
shop
ID tax_id
1 10
1 11
2 10
2 12
首先,您可以通过在这两个字段上创建一个唯一索引来防止这种情况,以供将来参考 对于解决方案,在mysql中创建一个具有相同结构的新表
shopnew
,或者在生成记录列表时从表中删除所有记录(确保有备份!):
这里有一个就地解决方案(但不是一个班轮) 查找最大id:
select max(id) as maxid
from shop;
记住这个值。假设它等于1000
使用偏移量重新插入唯一值:
insert into shop (id, tax_id)
select distinct id + 1000, tax_id
from shop;
删除旧值:
delete from shop
where id <= 1000;
利润 工作解决方案
//Sql query to find duplicates
SELECT id, tax_id, count(*) - 1 AS cnt
FROM shop
GROUP BY id
HAVING cnt > 1
--- res
+------+--------+-----+
| id | tax_id | cnt |
+------+--------+-----+
| 1 | 10 | 2 |
| 2 | 10 | 3 |
+------+--------+-----+
//Iterate through results with your language of choice
DELETE
FROM shop
WHERE id=<res id>
AND tax_id=<res tax_id>
LIMIT <cnt - 1>
---res (iterated)
+------+--------+
| id | tax_id |
+------+--------+
| 1 | 10 |
| 1 | 11 |
| 2 | 12 |
| 2 | 10 |
+------+--------+
编辑:最近重新讨论了这一点,这里有一个使用临时列的替代解决方案,消除了对脚本语言的需要
ALTER TABLE shop ADD COLUMN place INT;
SET @i = 1
UPDATE shop SET place = @i:= @i + 1;
DELETE FROM shop WHERE place NOT IN (SELECT place FROM items GROUP BY id, tax_id);
ALTER TABLE shop DROP COLUMN place;
实际上,这个问题及其目前的局限性是一个相当棘手的挑战。整个晚上我都在考虑这个解决方案(我知道这个解决方案永远不会有用)。我不会在wild nature中使用该解决方案,我只是想知道是否可以只使用MySQL 我公式中的问题是:是否可以编写一系列DELETE语句,在没有唯一约束的情况下从两列表中删除重复行 问题:
order BY
的DELETE
形式只能有一个WHERE
子句,而不支持HAVING
。即在满足条件后应用订单CREATE TABLE `tablename` (
`a_id` int(10) unsigned NOT NULL,
`b_id` int(10) unsigned NOT NULL,
KEY `Index_1` (`a_id`,`b_id`)
) ENGINE=InnoDB COLLATE utf8_bin;
我添加了一个键(不是唯一的或主要的),以加快查找速度,并希望在分组中使用它
您可以向表中输入一些值:
INSERT INTO tablename (a_id, b_id) VALUES (2, 3), (1, 1), (2, 2), (1,4);
INSERT INTO tablename (a_id, b_id) VALUES (2, 3), (1, 1), (2, 2), (1,4);
INSERT INTO tablename (a_id, b_id) VALUES (2, 3), (1, 1), (2, 2), (1,4);
作为一个副作用,键变成了覆盖率索引,当我们从表中进行选择时,显示的值将被排序,但当我们进行删除时,值将按照插入顺序读取
现在,让我们看一下以下查询:
SELECT @c, @a_id as a, @b_id as b, a_id, b_id
FROM tablename, (SELECT @a_id:=0, @b_id:=0, @c:=0) as init
WHERE (@c:=IF(LEAST(@a_id=(@a_id:=a_id), @b_id=(@b_id:=b_id)), @c+1, 1)) >= 1
;
其结果是:
@c, a, b, a_id, b_id
1, 1, 1, 1, 1
2, 1, 1, 1, 1
3, 1, 1, 1, 1
1, 1, 4, 1, 4
2, 1, 4, 1, 4
3, 1, 4, 1, 4
1, 2, 2, 2, 2
2, 2, 2, 2, 2
3, 2, 2, 2, 2
1, 2, 3, 2, 3
2, 2, 3, 2, 3
3, 2, 3, 2, 3
使用索引1
对结果进行自动排序,重复对(a\u id,b\u id)
在列@c
中枚举。我们现在的任务就是删除@c>1
中的所有行。我们唯一的问题是强制MySQL在删除时使用Index_1
,这在不应用附加条件的情况下相当棘手。但我们可以通过对a\u id
使用相等检查或多个相等检查来实现这一点:
DELETE FROM t
USING tablename t FORCE INDEX (Index_1)
JOIN (SELECT @a_id:=0, @b_id:=0, @c:=0) as init
WHERE a_id IN (1)
AND (@c:=IF(LEAST(@a_id=(@a_id:=a_id), @b_id=(@b_id:=b_id)), @c+1, 1)) > 1;
DELETE FROM t
USING tablename t FORCE INDEX (Index_1)
JOIN (SELECT @a_id:=0, @b_id:=0, @c:=0) as init
WHERE a_id IN (2)
AND (@c:=IF(LEAST(@a_id=(@a_id:=a_id), @b_id=(@b_id:=b_id)), @c+1, 1)) > 1;
SELECT * FROM tablename t;
a_id, b_id
1, 1
1, 4
2, 2
2, 3
我不能将所有可能的a_id
放在in()
中,因为MySQL会理解索引在这种情况下是无用的,查询不会删除所有重复项(仅相邻项),但如果有10个不同的a_id
我可以在两个DELETE语句中删除重复项,每个in将有5个显式id
希望,这可能对某人有用=)也许这会有帮助:
$query="SELECT * FROM shop ORDER BY id";
$rez=$dbh->query($query);
$multi=$rez->fetchAll(PDO::FETCH_ASSOC);
foreach ($multi as $key=>$row){
$rest=array_slice($multi,$key+1);
foreach ($rest as $rest){
if(($row['id']==$rest['id']) && ($row['tax_id']==$rest['tax_id'])){
$dbh->query("DELETE FROM shop WHERE id={$rest['id']} and tax_id= {$rest['tax_id']}");
}
}
}
第一个foreach
迭代每一行,第二个执行比较。
我使用的是PDO,但当然,您可以通过过程的方式来实现。临时表有什么问题?什么是dublicate?如果id/tax_id具有相同的值,或者如果您有多行具有相同的id(id应该是PK,应该是唯一的——或者您的“id”是FK)@SergeiTulentsev:它占用空间。它是临时的。它暂时占用空间。大多数人都可以接受这一点。:-)@Tomalak,我们实际上不知道表中有多少(不同的值/值)。我们不知道是否有索引(我相信没有)。如果表很大,没有索引就很难使用。如果它很小,那么一个临时表将是最简单的解决方案。除了权限之外,我看不到任何不使用它们的理由。在本例中,ID和tax_ID都不是唯一的。每个商店都有多个与之关联的税号。是的,但您可以在这两个商店上创建一个唯一的索引!这就是你要找的。因此,合并的两个字段不可能已经存在。查克:是的,那么?他的解决方案保持了对(id,tax_id)的唯一性(至少,据我所知)。虽然它违反了不创建新表的条件。啊,我看错了你的解决方案。现在有了意义:)第7行缺少一个右括号。对,在SO textarea中编写代码有点困难,应该在我的IDE中这样做:)到目前为止,这个解决方案是唯一一个符合所有条件的解决方案。你应该改为向上投票。:-)复制条目并不是很好的做法或实践,想象一下,在一个包含多个记录的表上实现这一点。INSERT语句不区分tax_id,因此只存储两个(可能是错误的,取决于表是否排序)示例中的条目。这不是每秒运行500次的操作。这是数据损坏修复。即使需要几秒钟,也应该可以接受(考虑到具体情况)。我不知道你在说什么不分青红皂白,但这个解决方案恰恰给出了OP想要的答案。这可能是一个临时表。@TomalakGeret'kal,但它不是。:-)也许用户没有创建表的权限。由于内存限制,我也会选择类似的方式。谢谢Sergei,你的建设性意见,尽管你的评论很不恰当,但给了我必要的动力。
@c, a, b, a_id, b_id
1, 1, 1, 1, 1
2, 1, 1, 1, 1
3, 1, 1, 1, 1
1, 1, 4, 1, 4
2, 1, 4, 1, 4
3, 1, 4, 1, 4
1, 2, 2, 2, 2
2, 2, 2, 2, 2
3, 2, 2, 2, 2
1, 2, 3, 2, 3
2, 2, 3, 2, 3
3, 2, 3, 2, 3
DELETE FROM t
USING tablename t FORCE INDEX (Index_1)
JOIN (SELECT @a_id:=0, @b_id:=0, @c:=0) as init
WHERE a_id IN (1)
AND (@c:=IF(LEAST(@a_id=(@a_id:=a_id), @b_id=(@b_id:=b_id)), @c+1, 1)) > 1;
DELETE FROM t
USING tablename t FORCE INDEX (Index_1)
JOIN (SELECT @a_id:=0, @b_id:=0, @c:=0) as init
WHERE a_id IN (2)
AND (@c:=IF(LEAST(@a_id=(@a_id:=a_id), @b_id=(@b_id:=b_id)), @c+1, 1)) > 1;
SELECT * FROM tablename t;
a_id, b_id
1, 1
1, 4
2, 2
2, 3
$query="SELECT * FROM shop ORDER BY id";
$rez=$dbh->query($query);
$multi=$rez->fetchAll(PDO::FETCH_ASSOC);
foreach ($multi as $key=>$row){
$rest=array_slice($multi,$key+1);
foreach ($rest as $rest){
if(($row['id']==$rest['id']) && ($row['tax_id']==$rest['tax_id'])){
$dbh->query("DELETE FROM shop WHERE id={$rest['id']} and tax_id= {$rest['tax_id']}");
}
}