Jak zrzucić dane niektórych tabel SQLite3?


183

Jak zrzucić dane i tylko dane, a nie schemat, niektórych tabel SQLite3 bazy danych (nie wszystkich tabel)? Zrzut powinien mieć format SQL, ponieważ powinien być później łatwo ponownie wprowadzony do bazy danych i powinien zostać wykonany z wiersza poleceń. Coś jak

sqlite3 db .dump

ale bez zrzucania schematu i wybierania tabel, które należy zrzucić.


Do jakiego formatu? Coś konkretnego, a może szukasz kopii zapasowej czytelnej dla człowieka? Proszę sprecyzuj.
dmckee --- były kot moderator

1
Chcę zrzucić do formatu SQL, aby móc go łatwo przywrócić. Dodałem tę informację do głównego pytania.
pupeno

Odpowiedzi:


214

Nie mówisz, co chcesz zrobić z plikiem zrzuconym.

Korzystam z poniższych, aby uzyskać plik CSV, który mogę zaimportować do prawie wszystkiego

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

Jeśli chcesz ponownie wstawić do innej bazy danych SQLite:

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

Czy istnieje sposób, aby to zrobić programowo przy użyciu instrukcji SQL? Widzę, jak to zrobić za pomocą interpretera, ale co, jeśli chcę napisać skrypt?
coleifer 10.10.2014

4
Możesz umieścić swoje instrukcje w pliku (np. Sample.txt), a następnie wywołać je za pomocą: sqlite3 db.sq3 <sample.txt
CyberFonic

Lub użyj polecenia .once zamiast .output, a dane wyjściowe zostaną przekierowane tylko dla pojedynczego następnego polecenia przed powrotem do konsoli. Użyj .output bez argumentów, aby ponownie rozpocząć zapisywanie na standardowe wyjście.Dokumenty SQLite
ruffin

156

Można to zrobić, uzyskując różnicę w poleceniach .schema i .dump. na przykład z grep:

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

data.sql plik będzie zawierał tylko dane bez schematu, coś takiego:

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

Mam nadzieję, że to Ci pomoże.


5
@anurageldorado to zwykły sql. po prostu biegnijsqlite3 some.db < data.sql
meduza

Dla niektórych rasson nie działa dla mnie. Potrzebuję zastosowań w okolicy. sqlite3 storage/db/jobs.s3db .schema jobs > schema.sqlnie działa, ale echo '.schema' jobs | sqlite3 storage/db/jobs.s3db > schema.sqldziała dobrze
abkrim 27.04.16

2
Wydawało się, że to dobre rozwiązanie, ale w moim przypadku większość linii jest usuwana przez grep. Komenda .schema generuje schemat każdej tabeli w wielu wierszach, więc linia zawiera tylko );, a grep usuwa wszystkie linie zawierające );Dodanie -xopcji grep rozwiązuje ten problem.
Sunder

38

Nie najlepszy sposób, ale w dzierżawie nie potrzebuje zewnętrznych narzędzi (oprócz grep, który i tak jest standardem w * nix boxach)

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

ale musisz wykonać to polecenie dla każdej tabeli, której szukasz.

Pamiętaj, że nie obejmuje to schematu.


1
Użyłemsqlite3 Database.s3db .dump
Jader Dias

3
To się zepsuje, jeśli te wstawki mają nowe linie w wartościach. Lepsze wykorzystanie, grep -v '^CREATE'jak sugerowano w jednej z pozostałych odpowiedzi
Dequis

1
użycie grep -v '^CREATE;spowoduje przerwanie, jeśli CREATEinstrukcje zawierają podział linii (co czasem robią). Najlepiej, IMO, wcale nie polega na automatycznym usuwaniu CREATEinstrukcji, ale na ręcznym ich edytowaniu. Wystarczy użyć dowolnego edytora tekstu, a następnie wyszukać CREATEi ręcznie usunąć te instrukcje. Tak długo, jak baza danych nie jest ogromna (a ponieważ używasz narzędzia sqlite, zgaduję, że jest to notatka), jest to dość proste.
Dan Jones

ale grep kreacji również pobierze kreację z widoków. jak mogę to usunąć?
Silve2611

35

Możesz podać jeden lub więcej argumentów tabeli dla specjalnego polecenia .dump, np sqlite3 db ".dump 'table1' 'table2'".


4
gdy dodam wiele nazw tabel, jak wspomniałeś, daje mi to następujący wynik: Zastosowanie: .dump? - preserve-rowids? ? JAK WZÓR?
mwm

1
@mwm Obserwuję ten sam problem w sqlite3 3.31.1 (2020/01/27). Dziennik zmian nic o tym nie mówi. (Nawiasem mówiąc, --preserve-rowidsdziała, ale nie jest to udokumentowane w ogóle.)
ynn

11

Każda odpowiedź, która sugeruje użycie grep do wykluczenia CREATElinii lub po prostu uchwycenia INSERTlinii z sqlite3 $DB .dumpwyjścia, zawiedzie. Na CREATE TABLEliście Polecenia jedną kolumnę w jednej linii (a więc z wyłączeniem CREATEnie będzie to wszystko), a wartości na INSERTlinii może mieć wbudowane znaki nowej linii (więc nie można złapać tylko te INSERTlinie).

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

Testowane na sqlite3 wersja 3.6.20.

Jeśli chcesz wykluczyć niektóre tabele, możesz je przefiltrować $(sqlite $DB .tables | grep -v -e one -e two -e three), lub jeśli chcesz uzyskać określony podzbiór, zastąp go one two three.


9

Jako ulepszenie odpowiedzi Paula Egana można to osiągnąć w następujący sposób:

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

--lub--

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

Zastrzeżeniem jest oczywiście to, że musisz mieć grep.


1
Ten mi się podoba. Jako dodatkowy bonus, nadal działa, jeśli masz zawieszony plik SQL, po prostu cat database.sql | grep '^INSERT' > database_inserts.sql(to samo dla schematu, zamień nagrep '^CREATE'
trisweb

2
@trisweb, oczywiście masz na myśli, grep '^INSERT' < database.sql > database_inserts.sqlże catjest to zbyteczne
Sebastian

1
Nie ma w tym nic zbędnego. catKosztuje w zasadzie nic do wykonania i sprawia, że łańcuch z wejścia do wyjścia znacznie jaśniejsze. Oczywiście możesz również pisać, < database.sql grep '^INSERT' ...ale wyraźny potok jest znacznie łatwiejszy do odczytania.
rjh

1
gdy dodam wiele nazw tabel, jak wspomniałeś, daje mi to następujący wynik: Zastosowanie: .dump? - preserve-rowids? ? JAK WZÓR?
mwm

-1: Wyszukiwanie linii za pomocą CREATE jest bezużytecznym pomysłem. Prawie każdy widok lub wyzwalacz, szczególnie jeśli zawiera komentarze, wymaga więcej niż jednego wiersza.
ceving,

6

W Pythonie, Javie lub innym języku wysokiego poziomu zrzut nie działa. Musimy ręcznie zakodować konwersję do CSV. Podaję przykład w języku Python. Inne, przykłady będą mile widziane:

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

Jeśli masz dane panelu, innymi słowy, wiele pojedynczych wpisów z identyfikatorami dodaje to do wyglądu, a także zrzuca statystyki podsumowujące:

        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

Zgodnie z dokumentacją SQLite dla powłoki wiersza poleceń Dla SQLite możesz wyeksportować tabelę SQLite (lub jej część) jako CSV, po prostu ustawiając „tryb” na „csv”, a następnie uruchom zapytanie, aby wyodrębnić żądane wiersze stół:

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

Następnie użyj polecenia „.import”, aby zaimportować dane CSV (wartości oddzielone przecinkami) do tabeli SQLite:

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

Przeczytaj dalszą dokumentację na temat dwóch rozważanych przypadków: (1) Tabela „tab1” nie istnieje wcześniej i (2) tabela „tab1” już istnieje.


3

Najlepszą metodą byłoby pobranie kodu, który wykonałby zrzut bazy danych sqlite3, z wyłączeniem części schematu.

Przykład pseudo kodu:

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

Zobacz: src/shell.c:838 (dla sqlite-3.5.9) dla faktycznego kodu

Możesz nawet wziąć tę powłokę i skomentować części schematu i użyć tego.


3

Przegląd innych możliwych rozwiązań

Uwzględnij tylko WSTAWKI

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

Łatwy do wdrożenia, ale zawiedzie, jeśli którakolwiek z kolumn zawiera nowe wiersze

Tryb wstawiania SQLite

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

To miłe i konfigurowalne rozwiązanie, ale nie działa, jeśli kolumny mają obiekty typu blob, takie jak „Geometry” w spatialite

Zróżnicuj zrzut ze schematem

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

Nie jestem pewien, dlaczego, ale nie działa dla mnie

Kolejne (nowe) możliwe rozwiązanie

Prawdopodobnie nie ma najlepszej odpowiedzi na to pytanie, ale dla mnie działające jest grep wstawki, biorąc pod uwagę, że są to nowe wiersze w wartościach kolumny z takim wyrażeniem jak to

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

Aby wybrać tabele, które należy zrzucić, .dumpprzyjmuje argument LIKE, aby dopasować nazwy tabel, ale jeśli to nie wystarczy, prawdopodobnie prosty skrypt jest lepszą opcją

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

lub coś bardziej rozbudowanego, aby uszanować klucze obce i zawrzeć cały zrzut tylko w jednej transakcji

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

Weź pod uwagę, że wyrażenie grep zakończy się niepowodzeniem, jeśli );ciąg znaków znajduje się w jednej z kolumn

Aby go przywrócić (w bazie danych z tabelami już utworzonymi)

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

2

Ta wersja działa dobrze z nowymi liniami wewnątrz wstawek:

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

W praktyce wyklucza wszystkie wiersze, od CREATEktórych mniej prawdopodobne jest, aby zawierały nowe wiersze


0

Odpowiedź retracile powinna być najbliższa, ale nie działa w moim przypadku. Jedno zapytanie wstawiania właśnie pękło w środku, a eksport został zatrzymany. Nie jestem pewien, jaki jest powód. Jednak działa dobrze podczas .dump.

W końcu napisałem narzędzie do podziału SQL wygenerowanego z .dump:

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


-3

Możesz dokonać wyboru w tabelach wstawiając przecinki po każdym polu, aby utworzyć plik csv, lub użyć narzędzia GUI, aby zwrócić wszystkie dane i zapisać je w pliku csv.


2
Moim zamiarem było stworzenie pliku SQL, który można łatwo ponownie dodać do bazy danych.
pupeno
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.