2016-12-14 1 views
0

J'ai une table avec les employés et pour chaque employé ont peu de factures. Je déclare 2 curseurs, un pour tous les employés (distinct) et un second curseur pour toutes les factures pour un employé. Maintenant, ouvrez le 1er curseur avec tous les employés, récupérez-en un, ouvrez le deuxième curseur (basé sur l'employé du 1er curseur) avec toutes les factures pour l'employé. Pour réutiliser un deuxième curseur pour tous les employés, j'ouvre et ferme le deuxième curseur pour chaque employé. Cette chose passe beaucoup de temps. Comment réutiliser un deuxième curseur à la place rouvrir ou n'importe quelle bonne idée?Ré-utiliser un curseur à la place ré-ouvrir

Une partie du code dans Pro * C:

struct sforc1 { 
long nis_rad[ROWS_FETCHED_C1]; 
long sec_nis[ROWS_FETCHED_C1]; 
/*char f_fact[9]; 
long sec_rec;*/ 
}forc1; 

struct sforc2 { 
long nis_rad[ROWS_FETCHED_C2]; 
long sec_nis[ROWS_FETCHED_C2]; 
char f_fact[ROWS_FETCHED_C2][9]; 
long sec_rec[ROWS_FETCHED_C2]; 
char f_p_camb_est[ROWS_FETCHED_C2][9]; 
char op_cambest[ROWS_FETCHED_C2][9]; 
}forc2; 

void main (void) 
{ 
exec sql declare c1 cursor for 
     select distinct nis_rad, sec_nis 
     from recibos 
     where ((imp_tot_rec - imp_cta)>0) and f_p_camb_est = '29991231'; 

exec sql declare c2 cursor for 
     select nis_rad, sec_nis, f_fact, sec_rec, f_p_camb_est, op_cambest 
     from recibos 
    where ((imp_tot_rec - imp_cta)>0) and f_p_camb_est = '29991231' and nis_rad = :forc1.nis_rad[i] and sec_nis=:forc1.sec_nis[i]; 

exec sql open c1; 

while(1){ 
    exec sql fetch c1 into :forc1; 
    rows_this_time1 = sqlca.sqlerrd[2]-rows_before1; 
    rows_before1 = sqlca.sqlerrd[2]; 

    if (rows_this_time1==0){ 
     break; 
    } 

    for(i=0;i<rows_this_time1;++i){ 

     exec sql open c2; 
     rows_before2 = 0; 

     while(1){ 

      exec sql fetch c2 into :forc2; 
      rows_this_time2 = sqlca.sqlerrd[2]-rows_before2; 
      rows_before2=sqlca.sqlerrd[2]; 
      if(rows_this_time2==0){ 
       break; 
      } 

      for(j=0;j<rows_this_time2;++j){ 
       strcpy(forc2.f_p_camb_est[j], "20161212"); 
       strcpy(forc2.op_cambest[j], "SIMD0943"); 

      } 

      EXEC SQL 
       update recibos 
         set f_p_camb_est = :forc2.f_p_camb_est, 
          op_cambest = :forc2.op_cambest 
         where nis_rad = :forc2.nis_rad 
         and sec_nis = :forc2.sec_nis 
         and f_fact = :forc2.f_fact 
         and sec_rec = :forc2.sec_rec; 

     } 

     exec sql close c2; 

    } 

    exec sql close c1; 
    exec sql commit; 
    exec sql open c1; 
    rows_before1 = 0; 
} 

exec sql close c1; 

}

nis_rad et sec_nis est un employee_id (clé primaire). Chaque nis_rad ont quelques factures f_fact (factures)

Pour le traitement de 10000 de nis_rad passent 30 minutes et 28-29 min est pour Rouvrir deuxième curseur (c2)

UP. Supprimé précédemment Exemple

+1

Où est-ce être exécuté à partir? Aussi, pourquoi avoir deux curseurs et les joindre manuellement (en utilisant une jointure croisée, par l'apparence des choses - cela cause probablement votre lenteur!)? Pourquoi ne pas avoir une seule déclaration SQL qui fait la jointure pour vous? (Bien que dans votre exemple, vous choisissez de la même table deux fois, vraiment, vous avez juste besoin de la deuxième requête.) – Boneist

+0

Quel langage de programmation est-ce? Ce n'est pas valide PL/SQL. Et pourquoi n'utilisez-vous pas un JOIN pour cela? Un curseur est la pire façon de le faire. –

+0

Cela ressemble à 'Pro * C'. – XING

Répondre

0

J'ai modifié une réponse.

Vous ne pouvez pas réutiliser la date du curseur sans l'ouvrir à nouveau, sauf si vous mettez en cache tous les résultats en mémoire. Le curseur est comme un pointeur sur les données lorsque vous lisez un enregistrement qui pointe vers le suivant pour que vous ne puissiez pas revenir.

Problème dans votre code utilise SQL impérativement. Vous ne devez pas extraire tous les enregistrements de votre application, puis appliquer une logique. Pensez à écrire une requête qui ne retournera que les enregistrements que vous devez traiter. Il peut être encore plus rapide d'exécuter 2-3 requêtes qui retourneront des enregistrements pour chaque section de votre code que de tirer toutes les données et ensuite vérifier la logique dans l'application.

+0

déclaration 'prev_employee_id employee_id% type;' est faux. Devrait être quelque chose comme 'prev_employee_id employee.employee_id% type' – XING

+0

@XING recherche sur le code OP est un code pseudo pas réel PL/SQL il est donc pas tort dans le sens de pseudocode. – Kacper

+0

J'ai besoin d'un deuxième curseur pour traiter toutes les factures pour un employé. Par exemple, si toutes les factures d'employés sont payées, faites quelque chose, et si les factures ne sont pas payées, faites autre chose. –

0

je dirais enplace d'ouverture cursor explicite, que l'utilisation d'Oracle implicit curseur et utiliser des boucles simples pour atteindre vos besoins:

declare 

begin 
for rec in (select employee_id 
      from employees) 
    Loop     
      for rcrd in (Select bill from employee_bill 
         where employee_id = rec.employee_id) 
      loop 

      /***Process your bills***/ 

      end loop;       

    end loop; 

end; 
+0

cela peut-il fonctionner avec mon cas? –

+0

Je dirais que vous devez utiliser des boucles simples lorsque vous codez dans Pro * c parce qu'il a ses propres limites. – XING

0

Le problème avec votre code est que cela fait énormément de changement de contexte, il n'est donc pas étonnant que ce soit lent.

D'après ce que je peux dire, votre code fait quelque chose comme:

  1. Dans Pro * C, allez à la base de données
  2. Dans la base de données, ouvrez le premier curseur
  3. Retour en Pro * C, demandez à la base de données pour une ligne à partir du curseur
  4. la base de données renvoie une ligne
  5. Dans Pro * C, allez à la base de données
  6. Dans la base de données, ouvrez le seconde d curseur, en passant dans les valeurs du premier curseur
  7. Dans Pro * C, demander à la base de données pour une ligne de la deuxième curseur
  8. La base de données renvoie une ligne
  9. Dans Pro * C, demander à la base de données mettre à jour la ou les lignes en fonction des résultats du deuxième curseur
  10. La base de données met à jour les lignes.
  11. Répétez les étapes 7-10 jusqu'à ce qu'il n'y a plus de lignes à récupérer à partir du curseur 2
  12. Répétez les étapes 3.11 jusqu'à ce qu'il n'y a plus de lignes à récupérer à partir du curseur 1

Je pense que vous pouvez voir qu'il ya une Il y a énormément de va-et-vient inutile ... et en fait, il est probable que c'est là que passe la majorité de votre temps.

Y avait-il pas pour les exigences de votre mentor (que je suppose sont à des fins d'enseignement seulement), vous pouvez le faire dans une déclaration de mise à jour unique, comme suit:

update recibos 
set f_p_camb_est = '20161212', 
     op_cambest = 'SIMD0943' 
where (imp_tot_rec - imp_cta) > 0 
and f_p_camb_est = '29991231'; 

(NB si f_p_camb_est est de DATE type de données , la chaîne littéraux doivent être convertis en DATEs utilisant DATE ou to_date(), par exemple DATE '29991231', to_date('20161212', 'yyyymmdd'), à moins que vous allez passer un paramètre qui est déjà de DATE type de données.)

en procédant ainsi, signifie que vous pouvez branchez la déclaration de mise à jour dans votre app Pro * C et il fera:

  1. Dans Pro * C, demandez la base de données pour exécuter l'instruction de mise à jour
  2. La base de données exécute l'instruction de mise à jour
  3. Control est retourné à Pro * C.

C'est 2 commutations de contexte au total - une réduction massive par rapport à votre méthode d'origine!

Cependant, votre mentor a demandé (à mon humble avis) la façon daft de mettre à jour les lignes en le faisant un nis_rad à la fois. Dans ce cas, il est toujours préférable de faire le gros du travail dans la base de données - il n'y a aucun point à envoyer des lignes à votre application à partir de la base de données uniquement pour que votre application ne fasse rien les renvoyer dans la base de données. La façon dont vous le faire sur le côté de la base de données est en PL/SQL, comme ceci:

begin 
    for rec in (select distinct nis_rad, sec_nis 
       from recibos 
       where (imp_tot_rec - imp_cta) > 0 
       and f_p_camb_est = '29991231') 
    loop 
    update recibos 
    set f_p_camb_est = '20161212', 
      op_cambest = 'SIMD0943' 
    where nis_rad = rec.nis_rad 
    and (imp_tot_rec - imp_cta) > 0 
    and f_p_camb_est = '29991231'; 
    end loop; 
end; 
/

Vous pouvez soit appeler directement à partir de Pro * C comme un bloc anonyme ou vous pouvez créer comme une procédure dans la base de données (potentiellement avec des paramètres), puis appelez la procédure à partir de Pro * C.

De toute façon, le flux global ressemble:

  1. Dans Pro * C, demandez la base de données pour exécuter le PL/SQL
  2. La base de données exécute le PL/SQL
  3. Control est de retour retourné à Pro * C.

Je sais que cela ressemble à deux changements de contexte, mais il faut aussi tenir compte du changement de contexte au sein de la base de données, quand il passe entre les moteurs PL/SQL et SQL, qui ressemble à quelque chose comme:

  1. le moteur PL/SQL demande un curseur
  2. le moteur SQL ouvre le curseur
  3. le moteur PL/SQL demande une rangée à partir du curseur
  4. le moteur SQL passe une rangée arrière
  5. Le moteur PL/SQL stocké les informations contenues dans le rec enregistrement
  6. Le moteur PL/SQL demande au moteur SQL pour exécuter l'instruction de mise à jour, en fonction de la valeur nis_rad du rec enregistrement
  7. Le moteur SQL exécute la mise à jour déclaration
  8. Répétez les étapes: 1 - 7 jusqu'à plus de lignes sont retournées

qui, comme je suis sûr que vous pouvez voir est beaucoup plus que les 2 changements de contexte que nous avions ensemble si vous aviez exécuter le seul mise à jour à la place.

Espérons que les choses efface un peu pour vous?

+0

oui, plus clairement. essayer d'améliorer mon code –

+0

Bonne chance * {:-) – Boneist