Select und Update Statement zusammen in einer Connection

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
Soapp
User
Beiträge: 5
Registriert: Montag 28. November 2022, 19:33

Hallo Leute, ich programmiere seit 1 Monat Python.
Hier wäre mein Script, bei dem ich das unwichtige etwas gekürzt habe, sodass es leichter zu durchblicken ist.
Momentan macht das Script noch wenig Sinn.....
Problem: Es wird nur 1 UPDATE-Statement durchgeführt, dann: no results to fetch
Danke für etwaige Hilfe. Das ganze ist etwas wirr aus dem Internet zusammenkopiert, also nicht wundern ;-)

Code: Alles auswählen

import psycopg2
import sys
from config import config
import requests
import json

def ping_address(webAddress):
	
	resp = requests.get("http://192.168.0.91:8080/api/address/"+webAddress)
	
	if resp.ok:
		...
		....
		return status	

	else:
		print(resp.text)
		raise ValueError("cannot connect", resp)
   



def get_vendors():
    
    conn = None
    try:
        params = config()
        conn = psycopg2.connect(**params)
        cur = conn.cursor()
        cur.itersize = 100
        cur.execute("SELECT address FROM webaddresses WHERE date='1996'")
        cptLigne = 0
        for rec in cur:
           cptLigne += 1
           if cptLigne % 100 == 0:
              addr=str(rec[0])
              status= ping_address(addr)    
              cur.execute("UPDATE webaddresses SET date = '%s' WHERE address = '%s'" % ("2022", addr))         
              conn.commit()
              
        
              
        conn.commit() # Also close the cursor
        conn.close()
        
        
    except (Exception, psycopg2.DatabaseError) as error:
        print(error)
    finally:
        if conn is not None:
            conn.close()




if __name__ == '__main__':

    get_vendors()         


Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Variablennamen schreibt man komplett klein. Eingerückt wird immer mit 4 Leerzeichen pro Ebene, man mischt niemals Tabs mit Leerzeichen.
Man benutzt keine Abkürzungen, wenn man response meint, schreibt man nicht resp, connection statt conn, cursor statt cursor, usw.
Es gibt response.raise_for_status().
Statt diesem conn = None und Finale Konstrukt benutzt man contextlib.closing.
Keine Ahnung was cptLigne bedeuten soll. Man benutzt Tiple-Unpacking. Die Adresse ist hoffentlich schon ein String, so dass der str-Aufruf überflüssig ist.
Warum wird nur jede hundertste Adresse benutzt?
Man formatiert niemals Werte in einen SQL-Ausdruck, sondern benutzt Platzhalter! Einen Curser, den man für eine Abfrage benutzt, darf man nicht gleichzeitig für Updates benutzen.
Bei get_vendors würde man eine Funktion erwarten, die Vendors zurückgibt, Deine Funktion gibt aber nichts zurück.
Soapp
User
Beiträge: 5
Registriert: Montag 28. November 2022, 19:33

Die Datenbank hat momentan 37.671.504 Einträge mit Datum "1996"
Daher wollte ich nicht Select * from webaddresses" machen. Den Code hab ich so im Internet gefunden.
Was wäre besser ? fetchone, fetchmany ?

Ich will jede Zeile auslesen, prüfen ob die webadresse noch funktioniert und dann das Datum ändern auf 2022.

Also muss ich für das Update Statement mehr oder weniger eine eigene Funktion schreiben und wieder zur Datenbank connecten ?

sprich:

Code: Alles auswählen

def update_database(addr):
conn = None
    try:
        params = config()
        conn = psycopg2.connect(**params)
        cur = conn.cursor()
       cur.execute("UPDATE webaddresses SET date = '%s' WHERE address = '%s'" % ("2022", addr))         
       conn.commit()
        
    except (Exception, psycopg2.DatabaseError) as error:
        print(error)
    finally:
        if conn is not None:
            conn.close()
Ich hatte gehofft alles in einem machen zu können, wegen Geschwindigkeitvorteil....
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Die Verbindung muss nur einmal gemacht werden. Aber es sind eben zwei Abfragen.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Gegen das SELECT an sich habe ich doch gar nichts geschrieben. Das ist so in Ordnung, es ist aber ein Fehler gleichzeitig den gleichen Cursor für UPDATEs zu verwenden.

Man formatiert keine Parameter in SQL-Statements hinein. Man erstellt nur eine Connection, und reicht die von Funktion zu Funktion.

Code: Alles auswählen

from contextlib import closing

def update_database(connection, address):
    with closing(connection.cursor()) as cursor:
       cursor.execute("UPDATE webaddresses SET date = %s WHERE address = %s" % ("2022", address))
       connection.commit()
Du hast aber noch nicht meine Frage beantwortet, warum Du aus allen Adressen zufällig jede hundertste auswählst.
Soapp
User
Beiträge: 5
Registriert: Montag 28. November 2022, 19:33

<<Du hast aber noch nicht meine Frage beantwortet, warum Du aus allen Adressen zufällig jede hundertste auswählst.>>

tja, wie gesagt, aus dem Internet kopiert, ohne es zu verstehen, aber jetzt hab ich das rausgeschmissen, da gehts wohl um server-side-cursors und irgendiwe muss man den vorher bennennen cur = conn.cursor('cursor-name')

Momentan läuft das Script, aber es dauert 3 Sekunden pro Update Query :(
Das langsame ist wirklich das Updaten, nicht die API-Funktion ping_address()

Code: Alles auswählen

def get_address():
    
    connection = None
    try:
        params = config()
        connection = psycopg2.connect(**params)
        connection.autocommit=True
        cursor = connection.cursor()
        cursor.execute("SELECT address FROM webaddresses WHERE date='1996'")
        
        rows = cursor.fetchall()
        
        for row in rows:
            addr=row[0]
            erfolg = ping_address(str(addr))         
            cursor.execute("UPDATE webaddresses SET date = %s,erfolg=%d WHERE address = '%s'" % ("2022",erfolg, addr)) 
		       
            
        
    except (Exception, psycopg2.DatabaseError) as error:
        print(error)
    finally:
        if connection is not None:
            connection.close()
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Soapp: Die Frage ist ob „auto commit“ so eine gute Idee ist. Vielleicht ist auch ein einziges `commit()` nicht so gut, dann könnte man versuchen alle n Updates ein `commit()` abzusetzen.

Ansonsten wäre noch die Frage was `address` in der Datenbank eigentlich‽ Ist das eine Zeichenkette die als Primärschlüssel deklariert ist, oder gibt es da wenigstens einen Index drauf? Hat die Tabelle `webaddresses` keinen künstlichen Primärschlüssel, in der Regel `id` oder so genannt?

Es werden immer noch Werte in eine SQL-Zeichenkette formatiert. Auch das kann, neben Fehlern und Sicherheitsproblemen, auch Leistungseinbussen zur Folge haben.

Bei `Exception` ist die Fehlerbehandlung mit einem einfachen `print()` keine gute Idee. Bei unerwarteten Fehlern will man mindestens mehr Informationen, und in der Regel auch nicht, das einfach weitergemacht wird als wäre nichts passiert.

Ist `date` tatsächlich eine Zeichenkette mit der Jahreszahl‽
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wenn das so langsam ist, dann fehlt dir eben ein Index. Eine Datenbank ist keine magische Wundermaschine. Wenn man schnelle Antworten oder wie hier Updates haben will, muss die Tabelle gut indiziert sein, damit der Eintrag schnell und nicht in ewiger Suche gefunden wird.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Sorry für den Schreibfehler, es muß so heißen:

Code: Alles auswählen

def update_database(connection, address):
    with closing(connection.cursor()) as cursor:
       cursor.execute("UPDATE webaddresses SET date = %s WHERE address = %s", ("2022", address))
       connection.commit()
denn wie schon geschrieben, formatiert man keine Werte in SQL-Statements hinein.

Wenn ein Update tatsächlich drei Sekunden dauert, dann ist die Maschine sehr lahm und es fehlt die passende Indizierung über address. Wenn Du alle 26 Jahre ein Update fährst, dann ist aber auch diese Zeit vernachlässigbar.
Der Funktionsname ist irreführend, weil gar keine Addresse geholt wird.
`date` sollte wohl besser year` heißen, und `erfolg` `success`. Exceptionhandling ist nur dann sinnvoll, wenn man auch passend auf den Fehler reagiert. Einfach nur die nützliche Information zum Ort wegzuschmeißen, ist nicht sinnvoll.

Code: Alles auswählen

def update_addresses():
    old_year = 1996
    new_year = 2022
    params = config()
    with closing(psycopg2.connect(**params)) as connection:
        connection.autocommit = True
        cursor = connection.cursor()
        cursor.execute("SELECT address FROM webaddresses WHERE date=%s", [old_year])
        rows = cursor.fetchall()
        
        for address, in rows:
            success = ping_address(address)         
            cursor.execute("UPDATE webaddresses SET date=%s, erfolg=%d WHERE address=%s", (new_year, success, address))
Soapp
User
Beiträge: 5
Registriert: Montag 28. November 2022, 19:33

__blackjack__ hat geschrieben: Dienstag 29. November 2022, 10:16 @Soapp: Die Frage ist ob „auto commit“ so eine gute Idee ist. Vielleicht ist auch ein einziges `commit()` nicht so gut, dann könnte man versuchen alle n Updates ein `commit()` abzusetzen.

Ansonsten wäre noch die Frage was `address` in der Datenbank eigentlich‽ Ist das eine Zeichenkette die als Primärschlüssel deklariert ist, oder gibt es da wenigstens einen Index drauf? Hat die Tabelle `webaddresses` keinen künstlichen Primärschlüssel, in der Regel `id` oder so genannt?

Es werden immer noch Werte in eine SQL-Zeichenkette formatiert. Auch das kann, neben Fehlern und Sicherheitsproblemen, auch Leistungseinbussen zur Folge haben.

Bei `Exception` ist die Fehlerbehandlung mit einem einfachen `print()` keine gute Idee. Bei unerwarteten Fehlern will man mindestens mehr Informationen, und in der Regel auch nicht, das einfach weitergemacht wird als wäre nichts passiert.

Ist `date` tatsächlich eine Zeichenkette mit der Jahreszahl‽
hm, die datenbank hat nur 3 felder: address (character), erfolg (character), date (character), keinen Index und keinen Primärschlüssel, Ok, ich glaube da muss ich mal nachlesen, wie man eine Datenbank richtig aufbaut ;-)
Bzgl. SQL-Zeichenkette und Hochkommas, python nimmt nur WHERE address='%s', ohne Hochkommas gibts fehlermeldung "syntax error"

Danke erstmal für die Hilfe, ich denke, erstmal Index erstellen über address wäre hilfreich :)
VG
Soapp
User
Beiträge: 5
Registriert: Montag 28. November 2022, 19:33

Index erstellt, WOW, was für ein Unterschied!

Danke für die Hilfe!

VG
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Soapp: WHERE address='%s' ist falsch, wenn das funktioniert, bist Du immer noch dabei Werte in eine Zeichenkette mit SQL zu formatieren.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten