SQlite Datensätze kopieren

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
ErwBer
User
Beiträge: 7
Registriert: Samstag 4. April 2015, 20:50

Hallo,

wie kann ich einen Datensatz aus einer SQlite-Datenbank in eine andere SQLite-Datenbank kopieren?
Ich weiß zwar, wie man in einzelnen Datenbanken Daten einfügt/Daten ausliest, mein Problem liegt jedoch in der "Verbindung" der beiden Datenbanken. Wie können die ausgelesenen Daten von SQlite-DB1 zwischengespeichert werden und dann beim INSERT in SQLite-DB2 verwendet werden?

Vielen Dank
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@ErwBer:
Mit einer 2. Datenbankverbindung. Wie öffnest Du denn die erste?
BlackJack

@ErwBer: Wenn Du weisst wie man Daten abfragt und wie man Daten schreibt, dann fehlt mir jetzt die konkrete Frage‽ Die ”Verbindung” zwischen den beiden Datenbanken ist Dein Programm.
ErwBer
User
Beiträge: 7
Registriert: Samstag 4. April 2015, 20:50

Mit folgendem Code gebe ich z.B. Daten aus:

Code: Alles auswählen

import sqlite3

conn=sqlite3.connect('Messergebnisse.sqlite')
curs=conn.cursor()
for row in curs.execute("SELECT * FROM Messergebnisse WHERE Abgeholt='2'"):
    print row 
conn.close()
Mein Problem ist jetzt, die mit "SELECT * FROM Messergebnisse WHERE Abgeholt='2'" gewonnenen Daten in eine zweite Datenbank einzufügen.
Kann diese geöffnet werden, bevor die DB1 geschlossen wird? Wie werden die gewonnenen Daten "zwischengespeichert"?
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@ErwBer: Warum sollte man keine zweite Datenbank öffnen können? Und die Daten werden von Dir doch schon in row "zwischengespeichert".
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

ErwBer hat geschrieben:Kann diese geöffnet werden, bevor die DB1 geschlossen wird?
Warum probierst Du das nicht einfach? (Hinweis: ja es geht)
ErwBer hat geschrieben:Wie werden die gewonnenen Daten "zwischengespeichert"?
Hmm, wenn die Frage ernst gemeint ist, antworte ich mal mit einer Gegenfrage: Was glaubst Du, ist `row` in `print row`?
ErwBer
User
Beiträge: 7
Registriert: Samstag 4. April 2015, 20:50

Hier mal die Datenkopier-Funktion, erklärt mein (Verständnis)Problem vielleicht:

Code: Alles auswählen

#!/usr/bin/env python
import sqlite3

conn=sqlite3.connect('/var/www/Messergebnisse.sqlite')
curs=conn.cursor()
for row in curs.execute("SELECT * FROM Messergebnisse WHERE Abgeholt='2'"):

	conn=sqlite3.connect('/var/www/Messergebnisse2.sqlite')
	curs=conn.cursor()
	curs.execute("INSERT row INTO Messergebnisse")
	conn.close()
conn.close()
Der Befehl "INSERT row INTO Messergebnisse" ist mein Problem.
Wie muss er lauten, damit die in "row" zwischengespeicherten Daten in Datenbank2 eingefügt werden?
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@ErwBer: wie soll auf magische Weise row in einem String mit der Variable row verknüpft werden können?
Wie sieht denn ein INSERT Befehl bei SQL aus? Diesem kannst Du dann row als Argument übergeben, wie Du das bei Deinem SELECT auch hättest tun sollen (Stichwort Platzhalter).
ErwBer
User
Beiträge: 7
Registriert: Samstag 4. April 2015, 20:50

Sieht jetzt so aus, es gibt keine Fehlermeldungen, aber die Daten werden auch nicht übernommen:

Code: Alles auswählen

#!/usr/bin/env python
import sqlite3
conn=sqlite3.connect('/var/www/Messergebnisse.sqlite')
curs=conn.cursor()
for row in curs.execute("SELECT * FROM Messergebnisse WHERE Abgeholt='0'"):
	conn=sqlite3.connect('/var/www/Messergebnisse2.sqlite')
	curs=conn.cursor()
	curs.execute("INSERT INTO Messergebnisse VALUES (?,?,?,?)", row)
	conn.close()
conn.close()
Was ist noch beim SELECT-Befehl falsch? Ich vermute da gehört muss da auch was mit (?,?,?,?) hin, weiß aber grad nicht wie genau.
BlackJack

@ErwBer: Es fehlt ein `commit()`-Aufruf und Du kannst nicht die selben Namen einfach so wiederverwenden. Du schliesst die zweite Verbindung zweimal und die erste gar nicht.
bfm
User
Beiträge: 88
Registriert: Donnerstag 14. März 2013, 09:42

@ErwBer: Bei dem jetzigen Code würde ich jetzt mal sagen, das bei jedem row aus select die Datenbank geöffnet, ein Datensatz geschrieben und wieder geschlossen wird.
Neben cursor.execute() gibt es noch cursor.executemany(). Hiermit lässt sich eine Sequenz von Werten in einem Rutsch in die Datenbank schreiben, ohne dass man selber über die Sequenz iteriert werden muss.

Wie wäre es hiermit (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python
import sqlite3
conn=sqlite3.connect('/var/www/Messergebnisse.sqlite')
curs=conn.cursor()

#Ergebnis aus dem select in ergebnis zwischenspeichern
ergebnis = conn.fetchall()   

#zur Sicherheit die Verbindung schließen, bevor unter dem gleichen Namen nochmals eine Verbindung zu einer anderen Datenbank geöffnet wird
conn.close() 

conn=sqlite3.connect('/var/www/Messergebnisse2.sqlite')
curs=conn.cursor()

#curs.execute() durch curs.executemany() ersetzen, damit man nicht selber über ergebnis iterieren muss
curs.executemany("INSERT INTO Messergebnisse VALUES (?,?,?,?)", ergebnis)

conn.commit()
conn.close()
BlackJack

In Python sieht das so kurz aus. :-) In Vala irgendwie länger und umständlicher:

Code: Alles auswählen

using GLib;

static const string SELECT_STRING =
    "SELECT * FROM Messergebnisse WHERE Abgeholt=:abgeholt;";
static const string INSERT_TEMPLATE =
    "INSERT INTO Messergebnisse (%s) VALUES (%s);";


static inline void print_db_error (string text, Sqlite.Database db)
{
    stderr.printf ("%s: %d: %s\n", text, db.errcode (), db.errmsg ());
}


static inline int create_insert_statement (
    Sqlite.Statement select_statement,
    Sqlite.Database target_db,
    out Sqlite.Statement insert_statement
) {
    var column_count = select_statement.column_count ();
    var column_names = new string[column_count];
    var placeholders = new string[column_count];
    for (int i = 0; i < column_count; i++) {
        column_names[i] = select_statement.column_name (i);
        placeholders[i] = "?";
    }
    var sql = INSERT_TEMPLATE.printf (
        string.joinv (",", column_names), string.joinv (",", placeholders)
    );
    return target_db.prepare_v2 (sql, sql.length, out insert_statement);
}


static inline int copy (
    string source_db_name, string target_db_name, int abgeholt_value
) {

    Sqlite.Database source_db;
    var rc = Sqlite.Database.open_v2 (
        source_db_name, out source_db, Sqlite.OPEN_READONLY
    );
    if (rc != Sqlite.OK) {
        print_db_error ("Can't open source database", source_db);
        return 1;
    }
    Sqlite.Statement select_statement;
    rc = source_db.prepare_v2 (
        SELECT_STRING, SELECT_STRING.length, out select_statement
    );
    if (rc != Sqlite.OK) {
        print_db_error ("Can't prepare select statement", source_db);
        return 1;
    }
    select_statement.bind_int (
        select_statement.bind_parameter_index (":abgeholt"), abgeholt_value
    );

    Sqlite.Database target_db;
    rc = Sqlite.Database.open (target_db_name, out target_db);
    if (rc != Sqlite.OK) {
        print_db_error ("Can't open target database", target_db);
        return 1;
    }

    Sqlite.Statement insert_statement;
    rc = create_insert_statement (
        select_statement, target_db, out insert_statement
    );
    if (rc != Sqlite.OK) {
        print_db_error ("Can't pepare insert statement", target_db);
        return 1;
    }
    var column_count = select_statement.column_count ();
    assert (column_count == insert_statement.bind_parameter_count ());
    while (select_statement.step () == Sqlite.ROW) {
        for (int i = 0; i < column_count; i++) {
            insert_statement.bind_value (
                i + 1, select_statement.column_value (i)
            );
        }
        rc = insert_statement.step ();
        if (rc != Sqlite.DONE) {
            print_db_error("Can't insert data", target_db);
            return 1;
        }
        insert_statement.reset ();
    }

    select_statement.reset ();

    return 0;
}


static int main (string[] args) {
    if (args.length != 4) {
        print ("Usage %s source_db target_db abgeholt_value\n", args[0]);
        return 1;
    } else {
        return copy(args[1], args[2], int.parse(args[3]));
    }
}
ErwBer
User
Beiträge: 7
Registriert: Samstag 4. April 2015, 20:50

@bfm: Vielen Dank für den Code, leider ergibt sich folgender Fehler:
File "/home/pi/scripts/datenbank.py", line 7, in <module>
ergebnis = conn.fetchall()
AttributeError: 'sqlite3.Connection' object has no attribute 'fetchall'
Ich bin neu in Python und weiß leider nicht, wie ich das Problem beheben kann
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@ErwBer: Deine Code war doch gar nicht mehr so weit von einer Lösung entfernt. Was Du machen mußt ist, die beiden Connections an verschiedene Variablen zu binden, nicht bei jedem Datensatz eine neue aufzumachen und regelmäßig zu commiten.
ErwBer
User
Beiträge: 7
Registriert: Samstag 4. April 2015, 20:50

Ich habe es jetzt hinbekommen, der Code sieht (mit zusätzlichen UPDATE-Befehlen) jetzt folgendermaßen aus:

Code: Alles auswählen

#!/usr/bin/env python
import sqlite3

#Daten von Datenbank1 auslesen
conn=sqlite3.connect('/var/www/Messergebnisse.sqlite')
curs=conn.cursor()
curs.execute("SELECT * FROM Messergebnisse WHERE Abgeholt=0")
ergebnis = curs.fetchall()	#Ergebnis aus dem select in ergebnis zwischenspeichern
conn.commit()	#Aenderungen festschreiben
curs.execute("DELETE FROM Messergebnisse WHERE Abgeholt=2")
curs.execute("UPDATE Messergebnisse SET Abgeholt=2 WHERE Abgeholt=1")
curs.execute("UPDATE Messergebnisse SET Abgeholt=1 WHERE Abgeholt=0")
conn.commit()	#Aenderungen festschreiben
conn.close()	#Verbindung schliessen
 

#Daten in Datenbank2 eintragen
conn=sqlite3.connect('/var/www/Messergebnisse2.sqlite')
curs=conn.cursor()
curs.executemany("INSERT INTO Messergebnisse VALUES (?,?,?,?)", ergebnis)
conn.commit()
conn.close()
Vielen Dank für alle Tipps und anregenden Fragen!!!
BlackJack

@ErwBer: Ich würde die Datensätze in der ersten Datenbank ja erst Löschen/Verändern wenn die erfolgreich in der zweiten Datenbank eingetragen wurden. Sonst können Daten verloren gehen wenn das nicht klappt.

Für das UPDATE reicht *ein* Aufruf wenn man die Datenbank den Feldwert ausrechnen lässt: ``UPDATE Messergebnisse SET Abgeholt=Abgeholt + 1``. Wobei das Ändern aller Datensätze sicher nicht besonders effizient ist.

Liegen die Datenbankdateien tatsächlich im Document-Root des Webservers? Das würde ich mir auch überlegen.
ErwBer
User
Beiträge: 7
Registriert: Samstag 4. April 2015, 20:50

Habs geändert, danke für die Hinweise!
Antworten