Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/three.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 如果我有一个MySQL表,其中有多个列值相同,那么如何删除除两个最新项之外的所有项?_Php_Mysql - Fatal编程技术网

Php 如果我有一个MySQL表,其中有多个列值相同,那么如何删除除两个最新项之外的所有项?

Php 如果我有一个MySQL表,其中有多个列值相同,那么如何删除除两个最新项之外的所有项?,php,mysql,Php,Mysql,我知道这听起来像是一些问题的重复,可能是的,但我已经搜索并尝试了我自己实现的几种可能的解决方案,但它们似乎都导致某种形式的无限递归,只消耗100%的CPU,什么也不做。这可能是因为我做错了,或者他们不适合我,我不知道 我有一个MySQL表,其结构如下: +--------+------+-----+-------+--------+--------+----------------+ | id | fid | bid | dec_a | varc_a |

我知道这听起来像是一些问题的重复,可能是的,但我已经搜索并尝试了我自己实现的几种可能的解决方案,但它们似乎都导致某种形式的无限递归,只消耗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个重复项,这些是前六行,第七行显示了“bid”列中存在变化的位置,但显然,这些列中的任何变化都将该行排除在重复项之外

我可以很容易地想象我想做什么:数据库中可能有数百万个条目,我想根据条目id排除最近的两行数据,其中“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);
分组时,请确保只使用重复的列,并且在最后一条语句中,您选择的应该是要保留的记录数,我需要在末尾选择2

是时候为风云人物举杯了

你需要写一篇文章。您可以通过PHP或MySQL直接创建存储过程:

通过PHP创建

$createProc = "DROP PROCEDURE IF EXISTS `remove_dups`;
    CREATE DEFINER=`root`@`localhost` PROCEDURE `remove_dups`( In id varchar(255))
    BEGIN
        ...my code...
    END;";

$conn = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);

//create the stored procedure
$stmt = $conn->prepare($createProc);

$stmt->execute();
通过MySQL GUI创建

只需将create语句放在文本框中并运行它(针对适当的DB):

然后您可以从PHP或MySQL调用此过程


在存储过程中,您需要声明一些变量来存储其中的值,并进行检查以查找具有相同值的行(使用),然后对照前一行的id检查id。如果所有值都相同,请删除到id较低的值。

这可能是您问题的解决方案

但是,由于没有日期时间列,我假设id列是主键。它是
自动增量
。所以我的假设是,数字越大,记录就越新。(除非有一些旧数据转储到表中,否则应该是正确的)

请确保在删除之前备份数据,因为这将导致永久数据丢失。更好的是,您可以将当前表复制到另一个表中,然后处理新表以确保下面的逻辑正确。然后将下面的查询改为从
tbl\u 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);
如果这是一次性清理作业,那么您应该能够从控制台执行查询。但是如果你正在寻找一份招聘工作,你可能应该把这个代码放到一个程序中

注意:这里我使用内存临时表来提高性能。您可能会遇到这样的问题:这是因为您有太多的记录。然后,您可以增加会话的“最大堆大小”值 差不多

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
这将为您提供当前值

SELECT VARIABLES LIKE 'max_heap_table_size';
SELECT VARIABLES LIKE 'tmp_table_size';

您需要一个日期字段我可以添加一个julian时间戳字段,插入时将时间(“U”)与记录一起存储不会有问题,但正如我在问题中所说的,条目的id是自动递增的,数据从源开始按正确的时间顺序进行解析和添加。因此,添加一个时间戳,至少在我的头脑中,是复制ID AUTO_INCREMENT主键值提供的功能。ID可以被视为时间戳。使用时间戳,您可以执行日期操作,如删除的where子句
和datefield
,其中X是当前日期减去某些日期元素。所以你只删除一天前创建的东西,或者其他什么。使用时间戳比使用自动增量容易得多。我指的是一个实际的
datetime
字段,这样你就可以使用MySQL的日期函数。我的问题是,我认为这里没有“XSELECT VARIABLES LIKE 'max_heap_table_size'; SELECT VARIABLES LIKE 'tmp_table_size';