Comment vider les données de certaines tables SQLite3?


183

Comment puis-je vider les données, et uniquement les données, et non le schéma, de certaines tables SQLite3 d'une base de données (pas toutes les tables)? Le vidage doit être au format SQL, car il doit être facilement réintroduit dans la base de données ultérieurement et doit être effectué à partir de la ligne de commande. Quelque chose comme

sqlite3 db .dump

mais sans vider le schéma et sans sélectionner les tables à vider.


Dans quel format? Quelque chose en particulier, ou recherchez-vous simplement une sauvegarde lisible par l'homme? Veuillez préciser.
dmckee --- ex-moderator chaton

1
Je veux vider au format SQL, afin de pouvoir le restaurer facilement. J'ai ajouté cette information à la question principale.
pupeno

Réponses:


214

Vous ne dites pas ce que vous souhaitez faire avec le fichier vidé.

J'utiliserais ce qui suit pour obtenir un fichier CSV, que je peux importer dans presque tout

.mode csv 
-- use '.separator SOME_STRING' for something other than a comma.
.headers on 
.out file.csv 
select * from MyTable;

Si vous souhaitez réinsérer dans une autre base de données SQLite, alors:

.mode insert <target_table_name>
.out file.sql 
select * from MyTable;

Existe-t-il un moyen de le faire par programme à l'aide d'instructions SQL? Je peux voir comment le faire à l'aide de l'interpréteur, mais que faire si je voulais écrire un script?
coleifer

4
Vous pouvez mettre vos déclarations dans un fichier (par exemple sample.txt) et ensuite l'invoquer en utilisant: sqlite3 db.sq3 <sample.txt
CyberFonic

" Ou utilisez la commande .once au lieu de .output et la sortie ne sera redirigée que pour la commande suivante avant de revenir à la console. Utilisez .output sans argument pour recommencer à écrire dans la sortie standard. " SQLite docs
ruffin

156

Vous pouvez faire cela en faisant la différence entre les commandes .schema et .dump. par exemple avec grep:

sqlite3 some.db .schema > schema.sql
sqlite3 some.db .dump > dump.sql
grep -vx -f schema.sql dump.sql > data.sql

data.sql Le fichier ne contiendra que des données sans schéma, quelque chose comme ceci:

BEGIN TRANSACTION;
INSERT INTO "table1" VALUES ...;
...
INSERT INTO "table2" VALUES ...;
...
COMMIT;

J'espère que ceci vous aide.


5
@anurageldorado c'est du simple SQL. just runsqlite3 some.db < data.sql
jellyfish

Pour certains rasson ne fonctionne pas pour moi. J'ai besoin d'utilisations autour. sqlite3 storage/db/jobs.s3db .schema jobs > schema.sqlne fonctionne pas, mais echo '.schema' jobs | sqlite3 storage/db/jobs.s3db > schema.sqlfonctionne bien
abkrim

2
Cela semblait être une bonne solution, mais dans mon cas, la plupart des lignes sont en fait supprimées par grep. La commande .schema génère le schéma de chaque table sur plusieurs lignes, il y a donc une ligne contenant uniquement );, et le grep supprime toutes les lignes contenant );L'ajout de l' -xoption à grep résout ce problème.
Sunder

38

Pas le meilleur moyen, mais au bail n'a pas besoin d'outils externes (sauf grep, qui est de toute façon standard sur les boîtiers * nix)

sqlite3 database.db3 .dump | grep '^INSERT INTO "tablename"'

mais vous devez exécuter cette commande pour chaque table que vous recherchez.

Notez que cela n'inclut pas le schéma.


1
J'ai utilisésqlite3 Database.s3db .dump
Jader Dias

3
Cela se cassera si ces insertions ont des nouvelles lignes dans les valeurs. Mieux utiliser grep -v '^CREATE'comme suggéré dans l'une des autres réponses
dequis

1
en utilisant grep -v '^CREATE;va casser si les CREATEinstructions ont des sauts de ligne en eux (ce qu'ils font parfois). Le mieux, l'OMI, n'est pas du tout de supprimer automatiquement les CREATEdéclarations, mais de les éditer manuellement. Utilisez simplement l'éditeur de texte dont vous avez besoin, recherchez CREATEet supprimez manuellement ces instructions. Tant que la base de données n'est pas énorme (et que vous utilisez sqlite, je suppose que c'est noté), alors c'est assez simple.
Dan Jones

mais le grep de la création prendra également la création des vues. comment puis-je supprimer cela?
Silve2611

35

Vous pouvez spécifier un ou plusieurs arguments de table dans la commande spéciale .dump, par exemple sqlite3 db ".dump 'table1' 'table2'".


4
quand j'ajoute plusieurs noms de table comme vous l'avez mentionné, cela me donne cette sortie: Utilisation: .dump? - preserve-rowids? ? LIKE-PATTERN?
mwm

1
@mwm J'observe le même problème dans sqlite3 3.31.1 (2020/01/27). Le journal des modifications ne dit rien à ce sujet. (À propos, --preserve-rowidsfonctionne mais n'est pas du tout documenté.)
ynn

11

Toute réponse suggérant d'utiliser grep pour exclure les CREATElignes ou simplement récupérer les INSERTlignes de la sqlite3 $DB .dumpsortie échouera mal. Les CREATE TABLEcommandes listent une colonne par ligne (donc exclure CREATEne l'obtiendra pas entièrement), et les valeurs sur les INSERTlignes peuvent avoir des retours à la ligne intégrés (vous ne pouvez donc pas saisir uniquement les INSERTlignes).

for t in $(sqlite3 $DB .tables); do
    echo -e ".mode insert $t\nselect * from $t;"
done | sqlite3 $DB > backup.sql

Testé sur sqlite3 version 3.6.20.

Si vous souhaitez exclure certaines tables, vous pouvez les filtrer avec $(sqlite $DB .tables | grep -v -e one -e two -e three), ou si vous souhaitez obtenir un sous-ensemble spécifique, remplacez-le par one two three.


9

Pour améliorer la réponse de Paul Egan, ceci peut être accompli comme suit:

sqlite3 database.db3 '.dump "table1" "table2"' | grep '^INSERT'

--ou--

sqlite3 database.db3 '.dump "table1" "table2"' | grep -v '^CREATE'

La mise en garde, bien sûr, est que vous devez avoir installé grep.


1
J'aime celui la. En prime, cela fonctionne toujours si vous avez un fichier SQL sauvegardé qui traîne, juste cat database.sql | grep '^INSERT' > database_inserts.sql(même chose pour le schéma, remplacez pargrep '^CREATE'
trisweb

2
@trisweb, bien sûr, vous voulez dire grep '^INSERT' < database.sql > database_inserts.sqlque catc'est superflu
Sebastian

1
Rien de superflu à ce sujet. L' catexécution ne coûte pratiquement rien et rend la chaîne d'entrée-sortie beaucoup plus claire. Bien sûr, vous pouvez également écrire < database.sql grep '^INSERT' ...mais un tube explicite est beaucoup plus facile à lire.
rjh

1
quand j'ajoute plusieurs noms de table comme vous l'avez mentionné, cela me donne cette sortie: Utilisation: .dump? - preserve-rowids? ? LIKE-PATTERN?
mwm

-1: La recherche de lignes avec CREATE est une idée inutile. Presque chaque vue ou déclencheur, en particulier, s'il contient des commentaires, nécessite plus d'une ligne.
ceving

6

En Python ou Java ou dans tout autre langage de haut niveau, le .dump ne fonctionne pas. Nous devons coder la conversion en CSV à la main. Je donne un exemple Python. D'autres, des exemples seraient appréciés:

from os import path   
import csv 

def convert_to_csv(directory, db_name):
    conn = sqlite3.connect(path.join(directory, db_name + '.db'))
    cursor = conn.cursor()
    cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
    tables = cursor.fetchall()
    for table in tables:
        table = table[0]
        cursor.execute('SELECT * FROM ' + table)
        column_names = [column_name[0] for column_name in cursor.description]
        with open(path.join(directory, table + '.csv'), 'w') as csv_file:
            csv_writer = csv.writer(csv_file)
            csv_writer.writerow(column_names)
            while True:
                try:
                    csv_writer.writerow(cursor.fetchone())
                except csv.Error:
                    break

Si vous avez des données de panneau, en d'autres termes, de nombreuses entrées individuelles avec des identifiants ajoutent ceci à avec look et il vide également les statistiques récapitulatives:

        if 'id' in column_names:
            with open(path.join(directory, table + '_aggregate.csv'), 'w') as csv_file:
                csv_writer = csv.writer(csv_file)
                column_names.remove('id')
                column_names.remove('round')
                sum_string = ','.join('sum(%s)' % item for item in column_names)
                cursor.execute('SELECT round, ' + sum_string +' FROM ' + table + ' GROUP BY round;')
                csv_writer.writerow(['round'] + column_names)
                while True:
                    try:
                        csv_writer.writerow(cursor.fetchone())
                    except csv.Error:
                        break 

4

Selon la documentation SQLite pour le shell de ligne de commande pour SQLite, vous pouvez exporter une table SQLite (ou une partie d'une table) au format CSV, simplement en définissant le "mode" sur "csv", puis exécutez une requête pour extraire les lignes souhaitées de la table:

sqlite> .header on
sqlite> .mode csv
sqlite> .once c:/work/dataout.csv
sqlite> SELECT * FROM tab1;
sqlite> .exit

Ensuite, utilisez la commande ".import" pour importer des données CSV (valeurs séparées par des virgules) dans une table SQLite:

sqlite> .mode csv
sqlite> .import C:/work/dataout.csv tab1
sqlite> .exit

Veuillez lire la documentation supplémentaire sur les deux cas à considérer: (1) La table "tab1" n'existe pas auparavant et (2) la table "tab1" existe déjà.


3

La meilleure méthode serait de prendre le code que le db dump sqlite3 ferait, à l'exclusion des parties de schéma.

Exemple de pseudo code:

SELECT 'INSERT INTO ' || tableName || ' VALUES( ' || 
  {for each value} ' quote(' || value || ')'     (+ commas until final)
|| ')' FROM 'tableName' ORDER BY rowid DESC

Voir: src/shell.c:838 (pour sqlite-3.5.9) pour le code réel

Vous pouvez même simplement prendre ce shell et commenter les parties du schéma et l'utiliser.


3

Examen des autres solutions possibles

Inclure uniquement les INSERTs

sqlite3 database.db3 .dump | grep '^INSERT INTO "tablename"'

Facile à mettre en œuvre mais échouera si l'une de vos colonnes inclut de nouvelles lignes

Mode d'insertion SQLite

for t in $(sqlite3 $DB .tables); do
    echo -e ".mode insert $t\nselect * from $t;"
done | sqlite3 $DB > backup.sql

C'est une solution agréable et personnalisable, mais cela ne fonctionne pas si vos colonnes ont des objets blob comme le type 'Geometry' dans spatialite

Différer le vidage avec le schéma

sqlite3 some.db .schema > schema.sql
sqlite3 some.db .dump > dump.sql
grep -v -f schema.sql dump > data.sql

Je ne sais pas pourquoi, mais ne fonctionne pas pour moi

Une autre (nouvelle) solution possible

Il n'y a probablement pas de meilleure réponse à cette question, mais celle qui fonctionne pour moi est de grep les insertions en tenant compte du fait qu'il y a de nouvelles lignes dans les valeurs de colonne avec une expression comme celle-ci

grep -Pzo "(?s)^INSERT.*\);[ \t]*$"

Pour sélectionner les tables à vider, .dumpadmet un argument LIKE pour correspondre aux noms de table, mais si cela ne suffit pas, un simple script est probablement la meilleure option

TABLES='table1 table2 table3'

echo '' > /tmp/backup.sql
for t in $TABLES ; do
    echo -e ".dump ${t}" | sqlite3 database.db3 | grep -Pzo "(?s)^INSERT.*?\);$" >> /tmp/backup.sql
done

ou quelque chose de plus élaboré pour respecter les clés étrangères et encapsuler tout le vidage en une seule transaction

TABLES='table1 table2 table3'

echo 'BEGIN TRANSACTION;' > /tmp/backup.sql
echo '' >> /tmp/backup.sql
for t in $TABLES ; do
    echo -e ".dump ${t}" | sqlite3 $1 | grep -Pzo "(?s)^INSERT.*?\);$" | grep -v -e 'PRAGMA foreign_keys=OFF;' -e 'BEGIN TRANSACTION;' -e 'COMMIT;' >> /tmp/backup.sql
done

echo '' >> /tmp/backup.sql
echo 'COMMIT;' >> /tmp/backup.sql

Tenez compte du fait que l'expression grep échouera si );une chaîne est présente dans l'une des colonnes

Pour le restaurer (dans une base de données avec les tables déjà créées)

sqlite3 -bail database.db3 < /tmp/backup.sql

2

Cette version fonctionne bien avec les nouvelles lignes à l'intérieur des inserts:

sqlite3 database.sqlite3 .dump | grep -v '^CREATE'

En pratique, exclut toutes les lignes commençant par CREATElesquelles sont moins susceptibles de contenir des nouvelles lignes


0

La réponse par retracile devrait être la plus proche, mais cela ne fonctionne pas pour mon cas. Une requête d'insertion s'est interrompue au milieu et l'exportation s'est arrêtée. Je ne sais pas quelle est la raison. Cependant, cela fonctionne bien pendant .dump.

Enfin, j'ai écrit un outil pour diviser le SQL généré à partir de .dump:

https://github.com/motherapp/sqlite_sql_parser/


-3

Vous pouvez faire une sélection sur les tables en insérant des virgules après chaque champ pour produire un csv, ou utiliser un outil GUI pour renvoyer toutes les données et les enregistrer dans un csv.


2
Mon intention était de produire un fichier SQL qui pourrait facilement être ré-ajouté à la base de données.
pupeno
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.