À l'aide de plusieurs curseurs dans une boucle imbriquée dans sqlite3 à partir de python 2.7

J'ai eu des problèmes à l'aide de plusieurs curseurs sur une base de données sqlite dans une boucle imbriquée. J'ai trouvé une solution qui fonctionne pour moi, mais il est limité et je n'ai pas vu ce problème spécifique documenté en ligne. Je suis l'affichage de cette sorte:
-- Claire du problème/solution est disponible
-- Pour voir si il y a une meilleure solution
- Peut-être que j'ai trouvé un défaut dans le sqlite3 module python

Voici la situation:
Mon Python application est de stocker des relations sociales données sqlite. L'ensemble de données comprend un un-à-plusieurs relation entre deux tables: myConnections et sharedConnections. Le premier a une ligne pour chaque connexion. Le sharedConnections tableau a 0:N lignes, en fonction du nombre de connexions sont partagés. Pour construire la structure, j'utilise une boucle imbriquée. À l'extérieur de la boucle que je visite chaque ligne dans myConnections. Dans l'intérieur de la boucle, j'remplir le sharedConnections table. Le code ressemble à ceci:

curOuter = db.cursor()  
for row in curOuter.execute('SELECT * FROM myConnections'):    
    id  = row[0]  
    curInner = db.cursor()  
    scList = retrieve_shared_connections(id)  
    for sc in scList:  
        curInner.execute('''INSERT INTO sharedConnections(IdConnectedToMe, IdShared) VALUES (?,?)''', (id,sc))  
db.commit()  

Le résultat est impair. Le sharedConnections obtient le tableau des entrées en double pour les deux premiers enregistrements dans myConnections. Ils sont un peu assemblés. Une connexions de l', B connexions, suivi par Un, puis de B à nouveau. Après la période initiale de bégayer, le traitement est correct! Exemple:

myConnections
-------------
a   
b  
c  
d  

sharedConnections
-------------
a->b  
a->c  
b->c  
b->d  
a->b  
a->c  
b->c  
b->d  

La solution est imparfaite. Au lieu d'utiliser l'itérateur de l'extérieur de la boucle du curseur, je SELECT, puis fetchall() et boucle à travers la liste. Depuis mon dataset est assez petit, c'est OK.

curOuter = db.cursor()
curOuter.execute('SELECT * FROM myConnections'):
rows = curOuter.fetchall()
for row in rows:    
    id  = row[0]
    curInner = db.cursor()
    scList = retrieve_shared_connections(id)
    for sc in scList:
        curInner.execute('''INSERT INTO sharedConnections(IdConnectedToMe, IdShared) VALUES (?,?)''', (id,sc))
db.commit()

Là vous l'avez. À l'aide de deux curseurs à l'encontre de différentes tables dans la même base de données sqlite dans une boucle imbriquée ne semble pas fonctionner. Qui plus est, il n'a pas d'échec, il donne des résultats bizarres.

Donc:
-- Est-ce vraiment la meilleure solution?
-- Est-il une meilleure solution?
-- Est-ce un défaut qui doit être adressée?

RÉPONSE:
À ce stade, les questions ont été posées, il y a eu quelques discussions et je pense que nous sommes assez bien complète. Voici comment il me semble:

  1. Vous ne pouvez pas utiliser fiable de plusieurs curseurs de boucles imbriquées avec le module sqlite3 à partir de python. Nous n'avons pas eu confirmation explicite malheureusement.
  2. Il y a une meilleure solution que celle que j'ai posté.
    2a: Réduire le sélectionner pour les champs dont vous avez besoin (j'ai été en utilisant *).
    2b: Structure de la boucle donc la plus petite empreinte de passe en mémoire. Pour ce cas, sharedConnections <= connexions (c'est un sous-ensemble). Il faut donc utiliser le curseur de la souris sur les connexions et d'accumuler des sharedConnections dans la mémoire.
    2c: à l'Aide .executemany sur le cumul de la liste de sharedConnections devrait être plus efficace que l' .exécuter à l'intérieur de la boucle.
  3. Est-ce un défaut? Nous n'avons pas obtenu de réponse. Je suppose que c'est la vie!

Merci à tous pour votre intérêt et de vos suggestions.

Meilleures salutations,
-Jim

Ce n' retrieve_shared_connections() faire? Elle affecte la DB?
retrieve_shared_connections(id) n'implique pas la base de données. C'est une fonction qui utilise un webservice pour retourner une liste des connexions partagées, donné un id. La boucle immédiate en dessous de cet appel Insère chaque connexion partagée dans la base de données.
Je n'ai pas regardé de trop près à votre code, mais serait un INSERT INTO ... SELECT FROM déclaration de travaux? Les instructions INSERT dans SQLite ne permettre les valeurs au sein d'une instruction SELECT.
Ah je vois ce que vous dites maintenant. Il a été un moment depuis que j'ai utilisé SQLite alors peut-être que quelqu'un avec plus d'expérience récente peut commentaire. J'aurais pensé que, parce que vous êtes à la sélection et à la mise à jour de différentes tables il ne devrait pas. Mais il semble que la loi de faire des insertions est de confondre le générateur de méthode pour votre curseur extérieur. Donc, utiliser fetchall() est probablement un bon pari pour l'instant à obtenir autour de cela. Cependant, il semble que vous êtes seulement en utilisant le id colonne de myConnections de sorte que vous pouvez économiser beaucoup en utilisant SELECT id from myConnections au lieu de toutes les colonnes.
Wow. Vraiment? C'est vraiment boiteux. Personne ne sait si c'est une limitation de l'sqlite ou le python interface? Dans mon cas, le outter boucle a ~6 millions de lignes. Je ne peux pas tirer de tout cela dans la mémoire. Je peux venir avec une sorte de contourner. Peut-être une entreprise DB est le chemin à parcourir (SQL Server, Postgres, MySQL, etc).

OriginalL'auteur tjim | 2012-11-05