Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/279.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 删除重复记录而不创建临时表_Php_Mysql - Fatal编程技术网

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,然后应用条件,但是支持
    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']}");
    
        }
    }