Bienvenue sur IndexError.

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

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

Chercher dans une liste ou SQL query ?

+3 votes

Je développe actuellement un soft pour gérer des articles de journaux. Ma question est d'ordre général et concerne la performance.

J'utilise sqlite pour stocker les articles en base de donnée sqlite. J'ai besoin de vérifier pour chaque article si les données stockées en base de donnée sont complètes. Pour l'instant je fais comme ça:

def listDoi(self, journal_abb):

    """Function to get the doi from the database.
    Also returns a list of booleans to check if the data are complete"""

    list_doi = []
    list_ok = []

    query = QtSql.QSqlQuery(self.bdd)
    query.prepare("SELECT * FROM papers WHERE journal=?")
    query.addBindValue(journal_abb)
    query.exec_()

    while query.next():
        record = query.record()
        list_doi.append(record.value('doi'))

        if record.value('graphical_abstract') != "Empty":
            list_ok.append(True)
        else:
            list_ok.append(False)

    return list_doi, list_ok

Je construis deux listes: listdoi, qui contient les id des articles, et listok, qui contient un booléen pour chaque article. J'ai besoin des deux listes:

  • Quand un article n'est ni dans listdoi, ni dans listok -> action a
  • Quand un article est dans listdoi mais pas dans listok -> action b
  • Quand il est dans les deux listes -> action c

Les listes peuvent contenir ~2000 items chacune. La question est:
Est-ce que c'est plus rapide/plus économique/plus mieux de construire deux listes, et à chaque fois qu'on veut vérifier un article, on le cherche dans list_doi; ou est-ce que c'est mieux de réécrire la fonction pour qu'elle cherche en bdd à chaque fois qu'on vérifie un article, et retourne deux booléens ?

Dans le deuxième cas par exemple, on ne charge pas deux listes en RAM, mais on fait une requête SQL à chaque fois.

Votre avis ? Sachant que la taille des listes peut grandement varier.

demandé 21-Sep-2015 par Rififi (532 points)

1 Réponse

+5 votes
 
Meilleure réponse

A ta place, j'utiliserais un dictionnaire plutôt que 2 listes.
C'est plus clair et plus rapide pour faire des recherches dedans.

Par exemple :

def listDoi(self, journal_abb):
    query = QtSql.QSqlQuery(self.bdd)
    query.prepare("SELECT * FROM papers WHERE journal=?")
    query.addBindValue(journal_abb)
    query.exec_()

    result = dict()
    while query.next():
        record = query.record()
        doi = record.value('doi')
        not_empty = record.value('graphical_abstract') != "Empty"
        result[doi] = not_empty

    return result

A l'usage :

papers = truc.listDoi(abb)
if doi not in papers:
    action_a()
elif papers[doi]:
    action_c()
else:
    action_b()
répondu 21-Sep-2015 par bubulle (2,256 points)
sélectionné 22-Sep-2015 par Rififi

Ça a l'air vraiment cool runsnakerun. Mais ça ne marche pas avec python 3 non ?

Apparemment non (je dois reconnaître que je ne suis pas encore passer à python 3).

En cherchant, on trouve SnakeViz qui revendique l'héritage de runsnake et qui est plus moderne. (pas tester)

@bubulle

je dois reconnaître que je ne suis pas encore passer à python 3

bouhhh!!

Tu devrais relire l'article de S&M ;P

@Rififi: Difficile de quantifier, mais une approche basée sur des accès disque et une approche reposant sur la RAM ont souvent plusieurs ordres de grandeur de différence (même si ton OS derrière le capot bosse dur pour limiter la casse). Et c'est de plus en plus visible lorsque les données sont de grande taille. C'est d'ailleurs pour cette raison que beaucoup de SGBD orientés performance (par exemple Redis) travaillent le plus possible en RAM.

Ceci étant, tout dépend du contexte d'utilisation, et il est parfois utile de faire des mesures, en particulier si un choix particulier d'architecture risque d'avoir un impact par la suite sur le projet. (par exemple si tu risque de devoir gérer un grosse quantité d'articles, et qu'en revanche le nombre de requêtes de ce type reste limité, il semble plus logique de déporter ces requêtes à la DB plutôt que de saturer la RAM ; parce que tu as peut-être mieux à faire avec ta RAM...).

Concernant ton ancien code :

if doi in self.list_doi and self.list_ok[self.list_doi.index(doi)]:

il faut bien voir deux choses :

  1. l'opérateur in appliqué sur une liste est de complexité O(n), c-a-d que plus ta liste est grande plus ça prendra du temps.
  2. pareil pour la méthode index() appliquée sur une liste.

Ça n'a pas forcément un impact monstrueux sur une liste de 2000 items, mais le code ne passera pas à l'échelle, c-a-d que si tu as par exemple 2000000 d'items ce sera très long. Pire encore, si tu veux réaliser le traitement sur m articles, tu passe à une complexité de O(n*m), ce qui peut très vite devenir insoutenable.

Bref, un dict est clairement un choix beaucoup plus adapté dans ton cas.

Ok. Donc oui clairement, je vais utiliser la RAM, parce que ce traitement est effectué sur chaque article.

...