SQLite Update mit Variable aus SELECT SUM(Spalte)

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
KaKri
User
Beiträge: 2
Registriert: Freitag 10. November 2023, 16:28

Als Python-Anfänger teste ich die Einbindung von Excel und SQLite in ein Python-Programm. Excel funktioniert, auch SQLite arbeitet bis ein ein Problem einwandfrei:

Ich summiere alle Werte einer Tabelle, das Resultat möchte ich in einer andere Tabelle speichern.

01 import sqlite3
02 import pandas as Pd
03
04 Datenbank = "G:\\Python\\Database.db"
05 verbindung = sqlite3.connect(Datenbank)
06 zeiger = verbindung.cursor()
07
08 zeiger.execute("SELECT SUM(Betrag) FROM Buchungen WHERE Bank = 'BA'")
09 results = list(zeiger)
10 fWert = results
11
12 print(fWert)
13
14 Konto = "BA"
15
16 zeiger.execute("UPDATE Konten SET Saldo = ? WHERE Konto = ?", (fWert, cKonto))
17
18 verbindung.commit()

Die Variable "fWert" wird immer mit einem "," am Ende angezeigt: [(16945.230000000338,)] gefolgt von der Fehlermeldung:

"Traceback (most recent call last):
File "g:\Python\KK-Finanzen\Excel-to-SQL.py", line 16, in <module> zeiger.execute("UPDATE Konten SET Saldo = ? WHERE Konto = ?", (fWert, cKonto))
sqlite3.ProgrammingError: Error binding parameter 1: type 'list' is not supported"

Wenn ich die Variable "fWert" mit einer Zahl manuell fülle, funktioniert alles bestens.

Wie kann ich das aus dem SUM resultierende Ergebnis in eine verwertbare Zahl konvertieren? Habe schon alle im Web gefundenen Lösungsvorschläge versucht, bisher leider kein Erfolg. Wahrscheinlich übersehe ich nur eine Kleinigkeit, aber wie schon erwähnt: Bin blutiger Anfänger!

Im Voraus vielen Dank für eure Hilfe.
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

wie du das Ergebnis abholst ist... komisch. Die Python DB 2.0 API - die du über das Python SQLite-Modul nutzt, kennen `fetchone()` zum Abholen von einem Abfrageergebnis, `fetchall` alle Ergebnisse. Siehe https://docs.python.org/3/library/sqlit ... r.fetchone.

In deinem Fall, wo ja ziemlich sicher nur ein Ergebnis zurück kommt, wäre `fetchone()` das Mittel der Wahl. `fetchone()` liefert als Ergebnis ein Tupel mit einem Element. Wie man darauf zugreift weißt du hoffentlich, weil das Python Grundlagen sind.

Dein Programm sieht dann aufgeräumt so aus - ungetestet:

Code: Alles auswählen

 import sqlite3

ACCOUNT = "BA"
DATABASE = "G:\\Python\\Database.db"
connection = sqlite3.connect(DATABASE)
cursor = connection.cursor()
result = cursor.execute("SELECT SUM(Betrag) FROM Buchungen WHERE Bank = 'BA'").fetchone()

cursor.execute("UPDATE Konten SET Saldo = ? WHERE Konto = ?", (result[0], ACCOUNT))
cursor.commit()
Abgesehen davon: wenn du den Saldo in der DB speicherst hast du die Daten quasi doppelt in der DB. Du kannst du Saldo ja jederzeit aus der DB berechnen. Davon ausgehen, dass die Tabelle "Buchungen" auch das Datum der Buchung enthält. Das speichern des Saldo ist nicht per se falsch, kann aber überflüssig im Sinne von doppelt gemoppelt sein.

Gruß, noisefloor
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@KaKri: noisefloor hat die Namen schon ”konventionalisiert”, ohne das zu kommentieren: Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Namen sollten keine kryptischen Abkürzungen enthalten. Und auch keine Grunddatentypen. In Kombination wird es nicht besser. Also kein `fWert` und kein `cKonto`. Die Datentypen ändert man im Laufe der Entwicklung gerne mal zu etwas spezifischerem, teilweise selbst geschrieben, und dann hat man falsche, irreführende Namen im Programm oder muss alle betroffenen Namen ändern. Der Wert könnte Beispielsweise auch `decimal.Decimal` sein statt `float`, weil das viele Datenbankanbindungen für die NUMERIC-Typen in SQL verwenden wenn dort eine feste Anzahl von Nachkommastellen deklariert ist.

Und das schliessen von Verbindung und Cursor sollte man sicherstellen:

Code: Alles auswählen

#!/usr/bin/env python3
import sqlite3
from contextlib import closing

ACCOUNT = "BA"
DATABASE = R"G:\Python\Database.db"


def main():
    with closing(sqlite3.connect(DATABASE)) as connection:
        with closing(connection.cursor()) as cursor:
            cursor.execute(
                "SELECT SUM(betrag) FROM buchungen WHERE bank = 'BA'"
            )
            cursor.execute(
                "UPDATE konten SET saldo = ? WHERE konto = ?",
                (cursor.fetchone()[0], ACCOUNT),
            )
            cursor.commit()


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
KaKri
User
Beiträge: 2
Registriert: Freitag 10. November 2023, 16:28

Vielen Dank für die prompten Hilfen. Das Problem ist damit behoben. Die sonstigen Empfehlungen sind für mich als Anfänger sehr wertvoll.

Liebe Grüße
bb1898
User
Beiträge: 200
Registriert: Mittwoch 12. Juli 2006, 14:28

noisefloor hat geschrieben: Freitag 10. November 2023, 19:17

Code: Alles auswählen

 import sqlite3

ACCOUNT = "BA"
...
result = cursor.execute("SELECT SUM(Betrag) FROM Buchungen WHERE Bank = 'BA'").fetchone()

cursor.execute("UPDATE Konten SET Saldo = ? WHERE Konto = ?", (result[0], ACCOUNT))
cursor.commit()
Zweierlei kommt mir auch hier noch seltsam vor:

Was ist "BA" denn nun? Die Bank oder das Konto bei der Bank?

Und, wichtiger: wieso wird im ersten SELECT der Parameter 'BA' direkt hineingeschrieben - was man bekanntlich nicht tut? Im UPDATE-Befehl ist es ja auch richtig gemacht.
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Und, wichtiger: wieso wird im ersten SELECT der Parameter 'BA' direkt hineingeschrieben - was man bekanntlich nicht tut?
Die Aussage ist in der Pauschalität falsch. Wer sagt denn, dass die Bank (mal davon ausgehen, das "BA" das Kürzel für eine Bank ist) ein variables Parameter ist? Vielleicht gibt des den Query nur mit "BA". Dann ist das vollkommen ok so.

Es liegt zwar Vermutung nahe, dass das vielleicht je nach Entwicklung des Programms, zu einem Parameter werden könnte. Wissen wir aktuell aber nicht und wir kennen aktuell auch nicht das ganze Programm.+

Gruß, noisefloor
Antworten