Php 从另一个表的大型子集中不存在的大型表中删除记录

Php 从另一个表的大型子集中不存在的大型表中删除记录,php,mysql,symfony,doctrine-orm,bigdata,Php,Mysql,Symfony,Doctrine Orm,Bigdata,下面是ma方法Symfony,删除其ID不存在于其他表中的Etitities的原则,该表包含了几种不同类型的ID列表。精确到6,但重要的是只了解系统的结构 并且,除非使用包含大约300000条记录的实体参数调用该方法,并且辅助表“oId”具有相似数量的记录,否则所有操作都是完美的。因此,该方法应该只删除我之前解释过的几条记录,当所有记录都工作到50000以下的表时,这里我遇到了数据库中的问题: SQLSTATE[HY000]:一般错误:超过1205锁等待超时;尝试重新启动事务 此错误不会与以前的

下面是ma方法Symfony,删除其ID不存在于其他表中的Etitities的原则,该表包含了几种不同类型的ID列表。精确到6,但重要的是只了解系统的结构

并且,除非使用包含大约300000条记录的实体参数调用该方法,并且辅助表“oId”具有相似数量的记录,否则所有操作都是完美的。因此,该方法应该只删除我之前解释过的几条记录,当所有记录都工作到50000以下的表时,这里我遇到了数据库中的问题:

SQLSTATE[HY000]:一般错误:超过1205锁等待超时;尝试重新启动事务

此错误不会与以前的实体一起出现。 我不知道问题出在哪里,因为记录的数量只大十倍,所以当这些查询在几秒钟内执行时,为什么我在20秒后看不到结果。? 这种错误的背后是什么

我甚至重建了我的方法,以循环方式完成任务,正如在一些关于如何从表中删除大量记录的回答中所说的那样。此外,我还将限制降低到1000条记录,所有记录都正常运行,除非它使用前面描述的实体开始运行

这里是整个方法

还有一点解释。不要害怕sql注入。$typeId和$borderId来自数据库,而不是来自用户

/**
 * Deletes records from given entity which are not exists in imported csv file.
 *
 * @param string $entity
 * @param integer $typeId
 * @param int $borderId Additional condition. Eg. 5 means, none of entities below id of value 5 will be delted (id < 5)
 */
public function removeNonExistent($entity, $typeId, $borderId = null)
{
    $subQueryQb = $this->repository->createQueryBuilder('oId');
    $subQueryQb->select('oId.originalId')
        ->andWhere('oId.type = '.$typeId)
        //->andWhere('oId.originalId = e.originalId')
        //Uncomment to have subquery of EXIST rather the IN()
    ;

    $qb = $this->em->createQueryBuilder();
    $qb->select('min(e.id) as minId, max(e.id) as maxId, count(e.id) as countId');
    $qb->from($entity, 'e');
    $tableData = $qb->getQuery()->getArrayResult();

    $queries = 0;
    $limit = 1000;
    $id = $tableData[0]['minId'];
    while ($id < $tableData[0]['maxId']) {

        $deleteQb = $this->em->createQueryBuilder();
        $deleteQb->delete()
            ->from($entity, 'e')
            ->andWhere('e.id BETWEEN '.$id.' AND '.($id + $limit - 1))
            ->andWhere($deleteQb->expr()->notIn('e.originalId', $subQueryQb→getDQL())) //Comment to use subquery as EXIST rather than IN()
            //->andWhere($deleteQb->expr()->not($deleteQb->expr()->exists($subQueryQb->getDQL())))
         //Uncomment to use subquery as EXIST rather than IN()  
        ;
        if ($borderId !== null) {
            $deleteQb->andWhere('e.id > '.$borderId);
        }

        $qResult = $deleteQb->getQuery()->getResult();
        $this->em->flush($entity);
        $queries++;
        echo $queries.'# delete query executed. Rows deleted: '.$qResult."\n";
        $id += $limit;

        if ($tableData[0]['countId'] < $limit) {
            break;
        }
    }

    echo $queries.' delete queries executed.'."\n";
}

有人能帮忙吗?我很乐意得到任何线索。

我发现的第一件事是这个查询的最后一行

DELETE FROM table
WHERE id NOT IN (
        SELECT a.original_id 
        FROM table_ids a
        WHERE a.type = 6           
) 
AND id > 3
如果查询的表单没有最后一行,那么我们仍然在谈论MySQL5.6

DELETE FROM table
WHERE id NOT IN (
        SELECT a.original_id 
        FROM table_ids a
        WHERE a.type = 6           
) 
– (without last this line)
id计数约为30万且此行排除的查询执行速度非常快,约为几秒钟,但如果我添加该行且id>3,则所有查询都会减慢甚至停止。 所以这是解决方案,但我必须有这个条件

现在是解决方案二:

   /**
    * Deletes records from given entity which are not exists in imported csv file.
    *
    * @param string $entity
    * @param integer $typeId
    * @param int $borderId Additional condition. Eg. 5 means, none of entities below id of value 5 will be delted (id < 5)
    */
    public function removeNonExistent($entity, $typeId, $borderId = null)
    {
        $metaData = $this->em->getClassMetadata($entity);
        $mainMetaData = $this->em->getClassMetadata($this->class);

        $connection = $this->em->getConnection();

        $connection->query('BEGIN;');

        $connection->query('CREATE TABLE '.$metaData->getTableName().'_copy LIKE '.$metaData->getTableName().';');

        $connection->query('INSERT INTO '.$metaData->getTableName().'_copy
            SELECT *
            FROM '.$metaData->getTableName().'
            WHERE '.$metaData->getTableName().'.'.$metaData->getColumnName('originalId').' IN (
                SELECT '.$mainMetaData->getColumnName('originalId').' FROM '.$mainMetaData->getTableName().' WHERE '.$mainMetaData->getColumnName('type').' = '.$typeId.'
            )
             ;');

        if ($borderId !== null) {
            $connection->query('INSERT INTO '.$metaData->getTableName().'_copy
                SELECT *
                FROM '.$metaData->getTableName().'
                WHERE '.$metaData->getTableName().'.'.$metaData->getColumnName('id').' <= '.$borderId.';');
        }

        $connection->query('RENAME TABLE '.$metaData->getTableName().' TO '.$metaData->getTableName().'_old, '.$metaData->getTableName().'_copy TO '.$metaData->getTableName().';');

        $connection->query('
            ALTER TABLE '.$metaData->getTableName().' DISABLE KEYS;
            SET FOREIGN_KEY_CHECKS = 0;
            SET UNIQUE_CHECKS = 0;
            SET AUTOCOMMIT = 0;
        ;');
        $connection->query('DROP TABLE '.$metaData->getTableName().'_old;');
        $connection->query('
            SET UNIQUE_CHECKS = 1;
            SET FOREIGN_KEY_CHECKS = 1;
            ALTER TABLE '.$metaData->getTableName().' ENABLE KEYS;
        ;');

        $connection->query('COMMIT;');
    }
简而言之: 我们将数据复制到新的tmp表中,然后更改名称并返回以前的顺序

现在它开始工作了。 唯一的缺点是我必须在不添加外键的情况下重建数据库的模式

我只想知道为什么这条简单的线还有一个条件 id>3 使查询无法完成

最后有一点需要澄清: 当我第一次执行问题中的脚本时,我不想在执行查询时等待太久,所以我输入^C。每次出现超时错误时。只有第一次,并没有错误,也并没有影响,更重要的是结束了


就是这样。

使用迭代之间的睡眠这有这么简单吗?现在我无法检查,但我会尽快检查。我相信就是这样。提前谢谢!请向我们展示结果SQL。不要在选择中使用。。。它可能比LEFT JOIN慢。使用自动提交,不是一个大交易。@McClayin我更改了它并添加了sleep1,但将限制更改为31000,我不知道如果限制为31000,为什么会收到27个查询。表中有大约321000条记录。另一件奇怪的事情是,在登录后的第一次运行脚本时,我进入了27个查询,脚本被挂起,再也没有继续。在第二次运行中,它挂起并在执行开始时超越错误。甚至没有一行喜欢d delete查询。。。这是条令,它生成了查询,所以我认为在代码中,构造查询所需的全部内容都给出了。根据LEFT JOIN的说法,我不知道它应该是什么样子,所以你能给我一些样品吗?根据autocommit,正如doctrine文档中所写,默认情况下,连接以自动提交模式运行,这意味着它是非事务性的,除非您通过beginTransaction显式启动事务,否则我对该行为没有任何更改。我之前的评论中的信息也适用于此评论。谢谢你的帮助!