2017-05-08 51 views
0

下面是ma方法(Symfony,Doctrine),用於刪除其中的id不存在於其他表中,其中包含來自多個不同類型的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"; 
} 

任何人都可以幫忙嗎?任何線索我都會很高興。

+0

在迭代之間使用睡眠 – mcklayin

+0

難道這很簡單嗎?現在我無法檢查,但只要我可以,我會檢查它。我相信就是這樣。提前致謝! –

+0

請向我們展示生成的SQL。不要使用'IN(SELECT ...)'它可能比'LEFT JOIN'慢。使用'autocommit',而不是一個大事務。 –

回答

1

這是我發現的第一件事情是這樣的查詢

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) 

查詢計數的ids約等於約300,000與排除這條線全部執行非常快(約幾秒鐘),但如果我添加行AND 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 線使得查詢無法完成......

而在最後一個clearification: 當我第一次從問題執行腳本我查詢執行後不想等太久,所以我輸入^ C。每當下一次出現超時錯誤時。只有第一次沒有錯誤,沒有效果,更沒有結果。

就這樣。