Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Vitesse d'une requête sqlite

+3 votes

J'ai un petit problème avec une requête sqlite. Je la trouve lente. Je ne fais pourtant rien de compliqué, j'update le champ pourcentage d'une base de donnée, en identifiant chaque item avec son id. L'id est un "INTEGER PRIMARY KEY AUTOINCREMENT".

list_id = []  # liste d'id bdd venant d'ailleurs (liste de int)
list_percentages = [] # liste de pourcentages venant d'ailleurs

bdd = sqlite3.connect("debug/test.sqlite")
bdd.row_factory = sqlite3.Row
c = bdd.cursor()

for id_bdd, percentage in zip(list_id, list_percentages):
    c.execute("UPDATE papers SET percentage_match = ? WHERE id = ?", (percentage, id_bdd))

bdd.commit()
c.close()
bdd.close()

Pour 20.000 items, la requête dure environ 4 secondes. Est-ce que cela vous parait normal ?

demandé 17-Mar-2015 par Rififi (532 points)
edité 18-Mar-2015 par Rififi

Il faudrait voir le commentaire de @jc qui me paraît pertinent :)

par curiosité que renvoie select * from sqlite_master where tbl_name = 'papers'; , ainsi que select count(*) from papers; quelle est ta version de python ?

"table" "papers" "papers" "8405" "CREATE TABLE "papers" (
id INTEGER PRIMARY KEY AUTOINCREMENT,
percentage_match REAL,
doi TEXT,
title TEXT,
date TEXT,
journal TEXT,
authors TEXT,
abstract TEXT,
graphical_abstract TEXT,
liked INTEGER,
url TEXT,
verif INTEGER,
new INTEGER,
topic_simple TEXT
)"

et
24962

@rififi et quelle est la version de python ? si tu es en python 2 passe en 2.7.9 si possible et si tu es en python 3 passe en 3.4.3 (c'est pour avoir la version la plus récente de sqlite3)

Je suis déjà en python 3.4.3

1 Réponse

+3 votes

es-tu sûr que c'est la requête qui prend du temps (et non le zip sur 20 000 éléments).

sinon, si mes souvenir sont bon, une bonne partie tu temps d’exécution d'une requête viens du parsing de celle-ci, du coup il est sûrement possible de gagner du temps en utilisant des requêtes préparées:

je ne connais pas bien la lib que tu utilises, mais l'utilisation normale d'une requête préparées serait plutôt de ce type:

list_id = []  # liste d'id bdd venant d'ailleurs (liste de int)
list_percentages = [] # liste de pourcentages venant d'ailleurs

bdd = sqlite3.connect("debug/test.sqlite")
bdd.row_factory = sqlite3.Row
c = bdd.cursor()
c.executemany("UPDATE papers SET percentage_match = ? WHERE id = ?", (zip(list_percentages, list_id)));
bdd.commit()
c.close()
bdd.close()

edit: ajout du context pour clarifier la réponse après le commentaire de jc.

répondu 18-Mar-2015 par yohann (312 points)
edité 19-Mar-2015 par yohann

Oui c'est bien la requête, j'ai testé sans rien dans la boucle, et le temps passé dans la boucle est négligeable. Sinon la lib sqlite, c'est la lib standard sqlite3 de python. Mais si tu as qqch avec PyQt, je prends aussi. J'ai testé ta solution avec executemany, j'ai exactement le même temps qu'avec la boucle for explicite.

De manière générale en sql, il faut éviter les traitements unitaires (une requête par élément) et favoriser les traitements par lots (une requête pour n éléments).

La solution de yohann est de base une meilleure solution que les updates dans la loop ;)

Quand tu dis 'j'ai testé sans rien dans la boucle' : est-ce que tu as bien modifié ton code pour supprimer la boucle ? ex :

list_id = []  # liste d'id bdd venant d'ailleurs (liste de int)
list_percentages = [] # liste de pourcentages venant d'ailleurs

bdd = sqlite3.connect("debug/test.sqlite")
bdd.row_factory = sqlite3.Row
c = bdd.cursor()
# no loop here
c.executemany("UPDATE papers SET percentage_match = ? WHERE id = ?", (zip(list_percentages, list_id)))
bdd.commit()
c.close()
bdd.close()

Comme suggéré par knep, as-tu un index sur papers.id ? Si ce n'est pas le cas, tu seras aussi pénalisé.

...