2017-09-06 2 views
0

J'ai un script de console (dans l'application yii2) pour changer les noms d'utilisateur dans DB (postgreSQL) et écrire les données changelog dans le fichier csv. J'utilise pour la boucle pour faire des changements dans les vracs de 100 utilisateurs par offset.Le script de la console PHP (yii2) arrête de fonctionner au milieu du processus d'exécution

public function actionTest() 
{ 
    $query = User::find()->where(['username' => '']); 
    $total = $query->count(); // SQL variant - SELECT COUNT(*) FROM user WHERE username = '' 
    $data = []; 
    $filePath = '/path/to/folder/log.csv'; 
    for ($offset = 0; $offset <= $total; $offset += 100) { 
     /** @var User[] $users */ 
     $users = $query->orderBy(['id' => SORT_ASC])->limit(100)->offset($offset)->all(); // SQL variant - SELECT * FROM user WHERE username = '' ORDER BY id ASC OFFSET 0 LIMIT 100 
     foreach ($users as $user) { 
      User::updateAll(['username' => 'newUsername'], ['id' => $user->id]); // SQL variant - UPDATE user SET username = 'newUsername' WHERE id = 1 
      $data[] = ['username' => 'newUsername']; // collect data to generate csv-file in the future 
     } 
     $csvObj = new CSV(); // "mnshankar/csv": "1.8" 
     $csvObj->with($data, false, 'a+')->put($filePath, 'a+'); 
     $data = []; 
    } 
} 

Le problème est que ce script cesse d'obtenir des données de droite DB au milieu du montant total des utilisateurs, donc j'obtenir 0 articles dans tableau $ users. Par exemple, si j'ai $ total = 15000, il arrête de fonctionner après itération avec $ offset = 7500, si $ total = 7500, il arrête de fonctionner après itération avec $ offset = 3800, si $ total = 3800, il cesse de fonctionner après itteration avec $ offset = 1900 etc.

J'ai essayé d'écrire simple test pour cette boucle avec la fonction pg_ * et il fonctionne correctement:

public function actionPgTest() 
{ 
    $dbConnection = pg_connect("host=localhost port=8080 dbname=user_db user=some_guy password=some_pass"); 
    $total = pg_query($dbConnection,'SELECT COUNT(*) FROM user WHERE username = \'\''); 
    $total = pg_fetch_array($total)['count']; 

    for ($offset = 0; $offset <= $total; $offset += 100) { 
     $query = 'SELECT * FROM user WHERE username = \'\' ORDER BY id ASC LIMIT 100 OFFSET ' . $offset; 
     $users = pg_query($dbConnection,$query); 
     $users = pg_fetch_all($users); 
     sleep(3); 
    } 
    pg_close(); 
} 

aussi, j'ai essayé de le faire avec bash -script et cela fonctionne aussi correctement:

#!/bin/bash count_query="select count(*) FROM \"user\" WHERE username = ''" count=$(echo $count_query | psql -U user -Atq user_db) query_base="select id FROM \"user\" WHERE username = '' LIMIT 100 OFFSET " for offset in $(seq 0 100 $count); do echo $query_base$offset| psql -U user -Atq user_db sleep 3; done; 

En outre, j'ai essayé d'exécuter le script sans générer de fichier CSV et j'ai eu le même problème juste au milieu.

Répondre

0

Il continue et retournera des données vides, du fait que le décalage est défini comme le nombre total d'enregistrements.

ici si offset signifie de postgreSQL docs:

OFFSET dit ignorer que de nombreuses lignes avant de commencer à renvoyer les lignes. OFFSET 0 équivaut à omettre la clause OFFSET. Si OFFSET et LIMIT apparaissent, les lignes OFFSET sont ignorées avant de commencer à compter les lignes LIMIT qui sont renvoyées.

Lisez aussi d'ici: https://www.postgresql.org/docs/8.0/static/queries-limit.html

+0

Pas vraiment. J'ai ajouté des données de décalage à stdout pour le voir dans la console à chaque itération et toujours moins que le nombre total d'utilisateurs jusqu'à la fin du script. J'ai également ajouté les données de compte des utilisateurs à stdout et chaque fois quelque chose comme ça: Nombre total d'utilisateurs = 15000, nombre d'utilisateurs actuel = 100, décalage actuel = 100 Nombre total d'utilisateurs = 15000, nombre d'utilisateurs actuel = 100, décalage actuel = 200 ... Nombre total d'utilisateurs = 15000, nombre d'utilisateurs actuel = 100, décalage actuel = 7500 Nombre total d'utilisateurs = 15000, nombre d'utilisateurs actuel = 0, décalage actuel = 7600 et à partir de ce moment, il n'y a pas de données dans la requête – Aksafan

0

Résolu! Le problème était dans OFFSET et LIMIT (le commentaire de devprashant aide beaucoup). Par exemple, nous avons table avec 6 éléments avec moins dans tous:

  1. -
  2. -
  3. -
  4. -
  5. -
  6. -

Pour la 1ère Itteration nous avons OFFSET = 0 et LIMIT = 2 et modifions le 1er 2 moins pour les plus pour obtenir:

  1. +
  2. +
  3. -
  4. -
  5. -
  6. -

Le 2ème itteration sera avec OFFSET = 2 et LIMIT = 2 et nous obtenons la items avec id = 5 et id = 6. Merci de décaler le résultat de la requête commence à partir de l'id = 5 et les limites pour 2 éléments. Nous obtenons donc:

  1. +
  2. +
  3. -
  4. -
  5. +
  6. +

Voilà comment nous obtenons la progression arithmétique et nous sommes sur des objets jusqu'à la milieu du montant total des articles.

Solution de travail:

public function actionTest(){ 
    $query = User::find()->where(['username' => '']); 
    $idsQuery = clone $query; 
    $userIds = $idsQuery->select(['id'])->limit(1000000)->asArray(true)->indexBy('id')->all(); 
    $userIds = array_keys($userIds); 
    asort($userIds); 
    $total = count($userIds); 
    $data = []; 
    $filePath = '/path/to/folder/log.csv'; 
for ($offset = 0; $offset <= $total; $offset += 100) { 
    $query = User::find()->where(['id' => array_slice($userIds , $offset, 100)]); 
    $users = $query->all(); 
    foreach ($users as $user) { 
     User::updateAll(['username' => 'newUsername'], ['id' => $user->id]); // SQL variant - UPDATE user SET username = 'newUsername' WHERE id = 1 
     $data[] = ['username' => 'newUsername']; // collect data to generate csv-file in the future 
    } 
    $csvObj = new CSV(); // "mnshankar/csv": "1.8" 
    $csvObj->with($data, false, 'a+')->put($filePath, 'a+'); 
    $data = []; 
}} 
+0

si ma réponse m'a aidé, vous pouvez marquer ma réponse comme acceptée et l'augmenter. – devprashant

+0

@devprashant mis à jour, mais n'a pas marqué comme cause acceptée la réponse principale est fausse et le lien vers la documentation a aidé. Donc, pour ne pas tromper les autres, je n'ai pas fait ça. Et merci de votre aide! – Aksafan