我知道这听起来像是几个问题的重复,而且很可能是,但我已经搜索并尝试了我自己的几种可能解决方案的实现,但所有这些似乎都会导致某种形式的无限递归只是咀嚼 100% 的 CPU 而什么都不做。这可能是因为我做错了,或者它们不适合我,我不知道。
我有一个 MySQL 表结构如下:
+--------+------+-----+-------+--------+--------+----------------+
| id | fid | bid | dec_a | varc_a | varc_b | dec_b | varc_c |
+--------+------+-----+-------+--------+--------+----------------+
| 106861 | 4192 | 22 | 1.40 | blah | blahbr | 0.2 | blahca |
| 108620 | 4192 | 22 | 1.55 | blah | blahbe | 0.2 | blahca |
| 108621 | 4192 | 22 | 1.55 | blah | blahbq | 0.2 | blahca |
| 108622 | 4192 | 22 | 1.55 | blah | blahbw | 0.2 | blahca |
| 108623 | 4192 | 22 | 1.55 | blah | blahbe | 0.2 | blahca |
| 108624 | 4192 | 22 | 1.55 | blah | blahbf | 0.2 | blahca |
| 106863 | 4192 | 33 | 1.40 | blah | blahba | 0.2 | blahca |
+--------+------+-----+-------+--------+--------+-------+--------+
“id”值是一个 BIGINT 自动递增值,数据是按正确的时间顺序从源中添加的,因此我将其视为时间戳。
为了确定哪些数据是重复的,我使用了“fid”、“bid”、“varc_a”、“dec_b”和“varc_c”列。从上面的示例中,您可以看到基于这些列有 6 个重复项,这些是前六行,第七行显示“出价”列中存在变化的位置,但显然这些列中的任何变化都排除了该行作为副本。
我可以很容易地想象出我想做什么:数据库中可能有数百万个条目,我想根据“fid”、“bid”、“varc_a", "dec_b"和 "varc_c"列值相同,然后清除剩下的。
我这辈子都不知道如何只使用 MySQL 来做到这一点,正如我所说,我看过的所有问题和答案似乎都不是我想做的或者我不明白提议的内容。
我知道我可以使用 PHP+MySQL 通过拖网数据并删除重复项来做到这一点,但考虑到我可以很容易地以这种非常低效的方式做到这一点,我认为我遗漏了一些明显的东西,我应该可以单独使用 MySQL 来完成吗?
:注意:
Mike 的回答非常好,并且根据我的问题的上下文进行了一些调整,恰好满足了我的需要。我最终使用的是这个:
DROP TEMPORARY TABLE IF EXISTS keepers1, keepers2, keepers_all;
CREATE TEMPORARY TABLE keepers1 (KEY(id)) ENGINE=MEMORY AS
SELECT fid, bid, varc_a, dec_b, var_c, MAX(id) AS id
FROM market_prices
GROUP BY fid, bid, varc_a, dec_b, varc_c;
CREATE TEMPORARY TABLE keepers2 AS
SELECT fid, bid, varc_a, dec_b, varc_c, MAX(id) AS id
FROM market_prices AS k
WHERE NOT EXISTS (SELECT 1 FROM keepers1 WHERE id = k.id)
GROUP BY fid, bid, varc_a, dec_b, varc_c;
CREATE TEMPORARY TABLE keepers_all (KEY(id)) ENGINE=MEMORY AS
SELECT id FROM keepers1
UNION ALL
SELECT id FROM keepers2;
DELETE k.* FROM market_prices AS k WHERE NOT EXISTS (SELECT 2 FROM keepers_all WHERE id = k.id);
分组时一定要只使用重复的列,在最后一条语句中,您的 SELECT 应该是您要保留的记录数,我需要在末尾有一个 SELECT 2。
是时候为风云人物举杯了!
最佳答案
这可能是您问题的解决方案。
但是,由于没有日期时间列,我假设 id 列是主键。它是 Auto_increment
。所以我的假设是数字越大,记录越新。 (这应该是真的,除非你有一些旧数据转储到表中)
请确保在删除之前备份您的数据,因为这会导致您的数据永久丢失。更好的是,您可以将当前表复制到另一个表中,然后在新表上工作以确保下面的逻辑是正确的。然后将下面的查询更改为从 tbl_new
而不是 tbl
你可以通过类似的方式复制你的表格
CREATE TABLE tbl_new LIKE tbl;
我已经为每个查询留下了评论
DROP TEMPORARY TABLE IF EXISTS keepers1, keepers2, keepers_all;
-- get the #1 top records
CREATE TEMPORARY TABLE keepers1 (KEY(id)) ENGINE=MEMORY AS
SELECT fid, bid, dec_a, varc_a, varc_b, dec_b, varc_c, MAX(id) AS id
FROM tbl
GROUP BY fid, bid, dec_a, varc_a, varc_b, dec_b, varc_c;
-- get the #2 top records
CREATE TEMPORARY TABLE keepers2 AS
SELECT fid, bid, dec_a, varc_a, varc_b, dec_b, varc_c, MAX(id) AS id
FROM tbl AS k
WHERE NOT EXISTS (SELECT 1 FROM keepers1 WHERE id = k.id)
GROUP BY fid, bid, dec_a, varc_a, varc_b, dec_b, varc_c;
-- create a temp table where you have all he ids that you want to keep
CREATE TEMPORARY TABLE keepers_all (KEY(id)) ENGINE=MEMORY AS
SELECT id FROM keepers1
UNION ALL
SELECT id FROM keepers2;
-- delete all records that you don't want to keep
DELETE k.* FROM tbl AS k WHERE NOT EXISTS (SELECT 1 FROM keepers_all WHERE id = k.id);
如果这是一次性清理工作,那么您应该能够从控制台执行查询。但如果您正在为他们寻找招聘工作,您可能应该采用此代码并将其放入程序中。
注意:这里我使用 MEMORY TEMPORARY 表以获得更好的性能。您可能会遇到一个问题 "Table is Full"这是因为你的记录太多了。然后你可以增加 session 的值 max_heap_table_size 类似
SET SESSION tmp_table_size = 1024 * 1024 * 1024 * 2; -- this will set it to 2G
SET SESSION max_heap_table_size = 1024 * 1024 * 1024 * 2; -- this will set it to 2G
这会给你当前的值(value)
SELECT VARIABLES LIKE 'max_heap_table_size';
SELECT VARIABLES LIKE 'tmp_table_size';
关于php - 如果我有一个 MySQL 表,其中多个列值相同,我该如何删除除两个最近的条目之外的所有条目?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27002850/