IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Extraire une base de données SQLite vers un CSV - Votre avis pour progresser


Sujet :

Python

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2011
    Messages
    65
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2011
    Messages : 65
    Points : 65
    Points
    65
    Par défaut Extraire une base de données SQLite vers un CSV - Votre avis pour progresser
    Bonjour à tous,

    Je vais commencer par des remerciements, à tous ceux qui font vivre le site. C'est pour beaucoup (dont moi) une vraie mine d'informations. Je sais que je trouve souvent réponses à mes questions sur le forum.

    Je suis en train de réaliser un petit programme (mon quatrième). ça fonctionne (et ça, c'est déjà chouette ).
    Etant auto-didacte, j'aurai toutefois bien voulu avoir votre avis sur mon programme de manière à pouvoir l'améliorer. Toutes les remarques sont le bienvenues (syntaxe, construction, fonctionnalités manquantes ou à améliorer, astuces, informations sur les conventions en vigueur en programmation...)

    Pour le moment, je réfléchi pour voir si je peux améliorer la gestion des erreurs et comment conserver dans un fichier la structure de ma base de données (pour conserver le type de chaque colonne - entier, text, ...) par exemple, sous forme d'un fichier .ini ou peut-être simplement ajouter une fonction pour dump la base de données.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
     
     
    #!/usr/bin/env python3
    # -*- coding : utf-8 -*-
    # Python 3
     
    import sqlite3
    import os
    import csv
    import configparser
    import sys
     
     
    class SQLiteCsvConvert:
        '''
        This class takes a database file and extract into CSV files.
        One table equal one csv file
        '''
     
        def __init__(self, SQLiteDBfileName, myDialect='excel'):
            '''
            Initialise SQLiteloader Class
            :param SQLiteDBfileName:
            :param myDialect:
            :return:
            '''
     
            self.myDialect = myDialect
            self.__registerDialect()
     
            if not os.path.isfile(SQLiteDBfileName):
                # Créer un fichier base de données
                raise IOError('No database exists')
                sys.exit()
     
            self.connect = sqlite3.connect(SQLiteDBfileName)
            self.cursor = self.connect.cursor()
            self.cursor.execute('PRAGMA encoding="UTF-8";')
     
        def __registerDialect(self):
            '''
            Add other dialects for CSV Writer.
            'escaped', 'singlequote', 'excel-fr', 'unix-pwd'
            By default CSV Writer have excel, excel-tabs, and unix dialects
            :param:
            :return:
            '''
            csv.register_dialect('escaped',
                         escapechar='\\',
                         doublequote=False,
                         quoting=csv.QUOTE_NONE)
     
            csv.register_dialect('singlequote',
                         quotechar="'",
                         quoting=csv.QUOTE_ALL)
     
            csv.register_dialect('excel-fr',
                         delimiter=';', quotechar='"', doublequote = 1,
                         escapechar=None, skipinitialspace = 0, quoting=csv.QUOTE_ALL,
                         lineterminator='\r\n')
     
            csv.register_dialect('unix-pwd', delimiter=':',
                         quotechar=None, lineterminator='\r')
     
        def executeRequest(self, statement):
            '''
            Use generator to request datas
            :param statement:
            :return: Generator
            '''
            try:
                result = self.cursor.execute(statement)
                for i in result:
                    yield i
            except:
                self.connect.rollback()
                print("Erreur")
            self.connect.commit()
     
     
        def executeCommand(self, statement):
            '''
            Execute any SQL Command as CREATE/DROP/ALTER TABLE or DELETE... on
            database and close connection
            :param statement:
            :return: None
             '''
            try:
                self.cursor.execute(statement)
            except:
                self.connect.rollback()
                print("Erreur")
            self.connect.commit()
     
        def extractDBtable(self):
            '''
            Extract all Table's name from database
            :param:
            :return:
            '''
            try:
                statement = 'SELECT name FROM sqlite_master WHERE type="table";'
                tableList = tuple()
                for r in self.executeRequest(statement):
                    tableList += r
                return tableList
            except Exception as e:
                raise e
     
        def extractFields(self, tableName, detail=False):
            '''
            Extract fields in a selected table from database
            :param tableName:
            :param tableName:
            :return fieldsList:
            '''
            statement = 'PRAGMA TABLE_INFO({})'.format(tableName)
            fieldsList = []
            r = self.executeRequest(statement)
            for i in r:
                if detail:
                    fieldsList += i
                    # Return all informations about fields (name, type, ...)
                    fieldsList.append(i[1:])
                else:
                    # Return only the name of fields
                    fieldsList.append(i[1])
            return fieldsList
     
     
        def extractTabletoCSV(self, tableName, myFile):
            '''
            Extract one table from database to CSV file
            :param tableName:
            :param myFile:
            :return:
            '''
            statement = 'SELECT * FROM %s' % (tableName)
            #myTable = self.cursor.execute(statement)
            try:
                f = open(myFile, 'w', encoding="utf-8")
                writer = csv.writer(f, self.myDialect)
     
                temp = self.extractFields(tableName, detail=False)
                writer.writerow(temp[1:])
     
                for temp in self.executeRequest(statement):
                    writer.writerow(temp[1:])
                f.close()
            except FileExistsError:
                print("Le fichier existe déjà")
     
        def extractDBtoCSV(self):
            '''
            Extract all tables from database to CSV files
            :return:
            '''
            extract = self.extractDBtable()
            for table in extract:
                self.extractTabletoCSV(table, str(table)+'.csv', self.myDialect)
     
        def close(self):
            self.connect.close()
            print('Connection Closed')
    Ci-dessous, des exemples pour savoir comment utiliser la chose:
    Le fonctionnement est inspiré du module suivant https://pypi.python.org/pypi/CSVtoSQLite/ qui fait l'inverse (et que je suis aussi en train de mamailler)


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    #!/usr/bin/env python3
     
    import converter, os, csv
     
    currentDirectory = os.getcwd()
    DBdirectory = os.path.join(currentDirectory, 'MyDatabase.db')
     
    # To specified a dialect
    csv.register_dialect('newDialect', delimiter='*', quotechar='"', quoting=csv.QUOTE_ALL)
     
    # Load a database
    db = converter.SQLiteCsvConvert('MyDatabase.db', 'newDialect')
     
    # Create a new table - Use executeCommand
    db.executeCommand('CREATE TABLE IF NOT EXISTS NEWTABLE (ID INT PRIMARY KEY NOT NULL, NAME TEXT NOT NULL, AGE INT NOT NULL, ADDRESS CHAR(50), COMPANY);')
     
    # Or delete one - Use executeCommand
    db.executeCommand('DROP TABLE COMPANY;')
     
    # Find tables in a database
    tables = db.extractDBtable()
    print(tables)
     
    # Get fields in a table
    fields = db.extractFields(tables[0])
    print(fields)
     
    # If you want more details, add parameter detail = True
    fields = db.extractFields(tables[0], detail=True)
    print(fields)
     
    # Get fields in all tables
    for fields in tables:
        print(db.extractFields(fields))
    # same with comprehension list
    print([(db.extractFields(fields)) for fields in tables])
     
    # Execute a SQLite request on a database
    # This command return a generator - Use executeRequest
    for result in db.executeRequest('SELECT * FROM {}'.format(tables[0])):
        print(result)
     
    # Extract an SQLite table using dialect define at the beginning
    db.extractTabletoCSV("CUA_AMOUNT", 'C:\\Users\\Damien\\OneDrive\\Python\\Projets\\report_maker\\MyFile.csv')
     
     
    # Close database connection
    db.close()
    Voilà voilà, merci d'avance pour vos remarques.

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 285
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 285
    Points : 36 773
    Points
    36 773
    Par défaut
    Salut,

    Tout d'abord vous avez déjà un outil qui s'appelle SQLite Administrator qui fait aussi ce genre de choses.

    Pour le code, moi, j'ai deux remarques
    - la méthode __registerDialect: ses instructions pourraient être exécutées au chargement du module plutôt qu'à la création de chaque instance.
    Les méthodes comme:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
        def executeRequest(self, statement):
            '''
            Use generator to request datas
            :param statement:
            :return: Generator
            '''
            try:
                result = self.cursor.execute(statement)
                for i in result:
                    yield i
            except:
                self.connect.rollback()
                print("Erreur")
            self.connect.commit()
    result retourné par self.cursor.execute est déjà une liste de... qu'apporte de retourner ses éléments 1 a 1 à travers un yield?
    Vous pourriez simplifiez:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        def executeRequest(self, statement):
             with self.connect:
                return self.cursor.execute(statement)
    voir les context managers avec sqlite3 dans la documentation.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    A partir d'une table d'une base sqlite, voilà comment on peut avoir les infos de chaque champs (nom, type, et valeur par défaut):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def listechamps(cnx, table):
        """Retourne la liste des [champ, type, défaut] de la table 'table' de
           la base de données ouverte cnx
           types: "text", "integer", "real", "blob", "null"
        """
        liste = []
        cur = cnx.cursor()
        cur.execute("PRAGMA table_info(%s);" % (table,))
        res = cur.fetchall()
        for elem in res:
            liste.append([elem[1], elem[2], elem[4]])
        cur.close()
        return liste
    Et si on ne veut que la liste des types:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    def listetypechamps(cnx, table):
        """Retourne la liste des types de champ de la table 'table' de la base
           de données ouverte cnx
           types sqlite3: "integer", "real", "text", "blob" ou "null"
        """
        return [typ for _, typ, _ in listechamps(cnx, table)]
    Attention tout de même: sqlite est très "souple", au point qu'on peut enregistrer sans erreur par exemple une chaine de caractère dans un champs déclaré comme integer! Ce qui peut poser des problèmes quand une conversion est engagée à partir du type déclarée.

    Sinon, enregistrer des données extraites d'une table (ou d'un "select" multitables!) dans un fichier csv est simple puisque la sortie de l'un et l'entrée de l'autre sont des listes de listes.
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  4. #4
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2011
    Messages
    65
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2011
    Messages : 65
    Points : 65
    Points
    65
    Par défaut
    Bonjour Wiztrick, bonjour tyrtamos

    Merci pour vos réponses.
    Concernant SQLite Administrator, je m'en sers...pour vérifier que ce que je fais, ça marche bien ^^.

    Pour register dialect, je vais faire la modif de ce pas...

    Et pour l'utilisation du yield, au début, j'avais bien utilisé un return.
    Et puis, j'ai cru comprendre que sur des grosses bases de données, ça pouvez être problématique (pour le moment, les bases que j'utilise font entre 2 000 à 5 000 lignes (pour le moment, je gère ça en Excel et je veux automatiser des traitements, d'où l'idée du petit module).
    Du coup, comme j'ai découvert le concept de générateur et que j'ai trouvé cela très très bien je me suis dit "Utilisons-le"...

    Et pour la doc sqlite, je vais la potasser davantage, c'est vrai que je suis parti sur pleins de tests et l'idée de sortir qqch rapidement.

    Enfin, je vais me pencher sur la souplesse de SQLite afin d'être sur de ne pas enregistrer un type de données erronés.

    Voilà de quoi m'occuper déjà quelques jours en tant que débutant...

  5. #5
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 285
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 285
    Points : 36 773
    Points
    36 773
    Par défaut
    Salut,

    Citation Envoyé par austin57 Voir le message
    Et pour l'utilisation du yield, au début, j'avais bien utilisé un return.
    Et puis, j'ai cru comprendre que sur des grosses bases de données, ça pouvez être problématique (pour le moment, les bases que j'utilise font entre 2 000 à 5 000 lignes (pour le moment, je gère ça en Excel et je veux automatiser des traitements, d'où l'idée du petit module).
    Du coup, comme j'ai découvert le concept de générateur et que j'ai trouvé cela très très bien je me suis dit "Utilisons-le"...
    Si vous voulez récupérer les 5000 lignes en plusieurs fois "yield" n'aide pas. Par contre SQL peut aider. Avec sqlite, c'est la clause LIMIT - SELECT * FROM table LIMIT 100, 1000 retourne les lignes 1001 a 1100 -. Dans ce cas, effectivement, il y a matière à faire des "yield" ou des iterators ("__iter__").

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  6. #6
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2011
    Messages
    65
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2011
    Messages : 65
    Points : 65
    Points
    65
    Par défaut
    Merci à tous les deux pour vos réponses.
    J'ai déjà intégré vos remarques

    Pour le reste, je vais regarder de plus prés l'utilisation de SQLite pour m'éviter de réinventer la roue.

    Encore mille fois merci.

Discussions similaires

  1. Réponses: 0
    Dernier message: 18/05/2011, 20h29
  2. [ASA]Migrer une base de données Sybase vers Oracle
    Par madina dans le forum Sybase
    Réponses: 2
    Dernier message: 12/04/2006, 12h40
  3. Extraire une Base de donnée Excel vers Mysql ??
    Par Arvulis dans le forum SQL Procédural
    Réponses: 3
    Dernier message: 09/01/2006, 22h58
  4. migration d'une base de données access vers oracle
    Par narjisovish dans le forum Migration
    Réponses: 2
    Dernier message: 08/09/2005, 10h27
  5. Réponses: 5
    Dernier message: 08/07/2005, 13h10

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo