2010-11-15 15 views
6

j'applique une classe qui ressemble à une table de base de données typique:Comment mettre en œuvre la table de style de base de données en Python

  • a nommé des colonnes et des lignes sans nom
  • a une clé primaire par laquelle je peux me référer à la lignes
  • prend en charge la récupération et l'affectation par clé primaire et le titre de la colonne
  • peut être invité à ajouter un index unique ou non unique pour l'une des colonnes, ce qui permet une récupération rapide d'une ligne (ou un ensemble de lignes) qui ont une donnée valeur dans cette colonne
  • enlèvement d'une ligne est rapide et est mis en œuvre comme "soft-delete": la ligne est conservée physiquement, mais est marqué pour suppression et n'apparaîtra pas dans toutes les opérations de récupération ultérieures
  • lignes sont rarement ajoutées
  • colonnes sont rarement supprimées

j'ai décidé de mettre en œuvre la classe directement plutôt que d'utiliser un wrapper autour SQLite.

Quelle serait une bonne structure de données à utiliser?


Juste à titre d'exemple, une approche à laquelle je pensais est un dictionnaire. Ses clés sont les valeurs de la colonne clé primaire de la table; ses valeurs sont les lignes implémentées de l'une des manières suivantes:

  1. Comme listes. Les numéros de colonne sont mappés dans les titres de colonne (en utilisant une liste pour une direction et une carte pour l'autre). Ici, une opération de récupération convertit d'abord le titre de colonne en numéro de colonne, puis trouve l'élément correspondant dans la liste.

  2. Comme dictionnaires. Les titres de colonnes sont les clés de ce dictionnaire.

Vous n'êtes pas sûr des avantages/inconvénients des deux.


Les raisons pour lesquelles je veux écrire mon propre code sont:

  • J'ai besoin de suivre les suppressions de lignes. C'est-à-dire, à tout moment, je veux être en mesure de signaler quelles lignes ont été supprimées et pour quelle raison (la "raison" est passée à ma méthode de suppression).
  • J'ai besoin d'information lors de l'indexation (par exemple, alors qu'un index non unique est en cours de construction, je veux vérifier certaines conditions et signaler si elles sont violées)
+0

Pourquoi le faire, plutôt que d'utiliser un SGBD existant? – delnan

+1

En particulier, pourquoi ne pas utiliser un wrapper autour de 'sqlite'? – katrielalex

+0

@delnan @katrielalex: viens d'éditer ma question pour donner quelques raisons. Peut-être y a-t-il un moyen de le faire avec un wrapper sqlite? – max

Répondre

2

Vous voudrez peut-être envisager la création d'une classe qui utilise une mémoire de la table sqlite sous le capot:

import sqlite3 

class MyTable(object): 
    def __init__(self): 
     self.conn=sqlite3.connect(':memory:') 
     self.cursor=self.conn.cursor() 
     sql='''\ 
      CREATE TABLE foo ... 
     ''' 
     self.execute(sql) 
    def execute(self,sql,args): 
     self.cursor.execute(sql,args) 
    def delete(self,id,reason): 
     sql='UPDATE table SET softdelete = 1, reason = %s where tableid = %s' 
     self.cursor.execute(sql,(reason,id,)) 
    def verify(self): 
     # Check that certain conditions are true 
     # Report (or raise exception?) if violated 
    def build_index(self): 
     self.verify() 
     ... 

Soft-suppression peut être mis en œuvre en ayant une colonne softdelete (de bool type). De même, vous pouvez avoir une colonne pour stocker la raison de la suppression. Undeleting impliquerait simplement la mise à jour de la ligne et la modification de la valeur softdelete. La sélection des lignes qui n'ont pas été supprimées peut être réalisée avec la condition SQL WHERE softdelete != 1.

Vous pouvez écrire une méthode verify pour vérifier que les conditions sur vos données sont satisfaites. Et vous pouvez appeler cette méthode à partir de votre méthode build_index.

Une autre alternative consiste à utiliser un tableau masqué structuré numpy.

Il est difficile de dire ce qui serait le plus rapide. Peut-être que le seul moyen sûr de le dire serait d'écrire du code pour chacun et un benchmark sur les données du monde réel avec timeit.

+0

J'aime l'idée de colonne 'softdelete'. Je ne pense pas que je puisse faire la méthode 'verify' puisque mes conditions étaient vérifiées pendant la construction de l'index (rangée par rangée); mais il peut être un petit prix à payer si je peux compter sur sqlite au lieu d'une classe personnalisée. Et j'étais intéressé à en apprendre plus sur 'MaskedArray', je n'en avais jamais entendu parler auparavant. – max

2

Je considérerais la construction d'un dictionnaire avec des clés sont des tuples ou des listes. Par exemple: my_dict(("col_2", "row_24")) vous obtiendriez cet élément. A partir de là, il serait assez facile (sinon extrêmement rapide pour les très grandes bases de données) d'écrire les méthodes 'get_col' et 'get_row', ainsi que 'get_row_slice' et 'get_col_slice' des 2 précédentes pour accéder à votre méthodes

Utiliser un dictionnaire entier comme ça aura deux avantages.1) Obtenir un seul élément sera plus rapide que vos 2 méthodes proposées; 2) Si vous voulez avoir un nombre différent d'éléments (ou d'éléments manquants) dans vos colonnes, cela le rendra extrêmement facile et efficace.

Juste une pensée :) Je serai curieux de voir quels paquets les gens vont suggérer!

Vive

+0

Intéressant. Cela me semble également plus rapide, mais je ne suis pas sûr: il est possible que l'obtention d'un élément de liste soit aussi rapide que le travail supplémentaire nécessaire pour calculer le hachage d'un n-uplet plutôt qu'une seule valeur. Btw, je suis seulement préoccupé par le temps, pas la mémoire, l'efficacité. – max

+0

Ensuite, un dictionnaire sera certainement plus rapide lorsque la table s'agrandit. Dans une liste d'un million d'éléments, vous devez rechercher en moyenne un demi-million d'éléments avant d'arriver au bon. Avec une table de hachage, donc un dictionnaire, vous devez rechercher un maximum de 20 éléments pour trouver le bon. À votre santé! – Morlock

+0

Je suis d'accord, mais quand j'ai dit "list", je voulais utiliser un dictionnaire pour convertir le nom de la colonne en index de liste pour l'accès O (1). Il est plus facile d'utiliser un dictionnaire pour commencer. – max

0

Vous devriez vraiment utiliser SQLite. Pour votre première raison (raisons de suppression de suivi), vous pouvez facilement implémenter ceci en ayant une deuxième table vers laquelle vous "déplacez" les lignes lors de la suppression. La raison peut être suivie dans une colonne supplémentaire dans cette table ou dans une autre table que vous pouvez rejoindre. Si une raison de suppression n'est pas toujours requise, vous pouvez même utiliser des déclencheurs sur votre table source pour copier les lignes sur le point d'être supprimées et/ou avoir une fonction définie par l'utilisateur qui peut obtenir la raison.

La raison d'indexation est quelque peu couverte par des contraintes, mais je ne peux pas y répondre directement sans plus de détails.