Python führt nicht alle SQL Befehle aus

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
asahdkhaled
User
Beiträge: 29
Registriert: Samstag 28. Oktober 2017, 22:07

Samstag 28. Oktober 2017, 22:13

Hallo zusammen,
ich habe eine Tabelle in einer Oracle-DB mit fehlenden Einträgen.
Die Spalten, in denen Zellen "null" sind, hole ich mir zuvor. Jetzt möchte ich, dass für jede Spalte in der fehlende Zellen vorhanden sind
a) eine neue Spalte erzeugt wird
b) in dieser Spalte 1 oder 0 geschrieben wird, jenachdem ob der Datensatz vorhanden war oder nicht und
c) der Wert in der Zelle zufällig mit einem beliebigen Wert aus der Spalte befüllt wird

Ich habe in meiner Beispieltabelle 3 Spalten, in denen dies der Fall ist. Für 2 Spalten funktioniert die folgende Schleife perfekt. Für die letzte Spalte wird zwar eine neue Spalte hinzugefügt, aber weder mit 1/0 upgedated, noch der fehlende Wert in der Ursprungsspalte überschrieben.
Den SQL Befehl habe ich mir auch schon ausgeben lassen und im SQL-Developer ausgeführt, da funktioniert er. Er scheint einfach die Query nicht abzuschicken (die letzten beiden execute Statements für die letzte Spalte). Hat jemand eine Idee, woran das liegen könnte?

Code: Alles auswählen

for column in missing_columns:
    columns=(tabelle,column)
    missing= (tabelle,column,column)
    replace = (tabelle,column,column,column,tabelle,column)
    #neue Spalte hinzufügen mit_missing suffix
    cur.execute("ALTER TABLE %s ADD %s_missing  varchar(25)" % columns)
    # 1 oder 0 eingeben wenn schon da gewsen
    cur.execute("UPDATE %s SET %s_missing =  CASE WHEN %s IS NULL THEN '1' ELSE '0' END" % missing )
    # leere Zelle mit random Wert ersetzen
    cur.execute("UPDATE %s SET %s = (SELECT %s FROM (SELECT %s FROM %s ORDER BY dbms_random.value ) WHERE rownum = 1) WHERE %s IS NULL" % replace)

cur.close()
db.close()

Benutzeravatar
noisefloor
User
Beiträge: 2523
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: Görgeshausen
Kontaktdaten:

Sonntag 29. Oktober 2017, 09:34

Hallo,

fehlt da nicht einfach das `commit()`? Oder ist deine DB im Auto-Commit Modus?

Gruß, noisefloor
asahdkhaled
User
Beiträge: 29
Registriert: Samstag 28. Oktober 2017, 22:07

Sonntag 29. Oktober 2017, 09:43

Danke für deinen Hinweis. Aber nein, die andern Befehle laufen ja auch und selbst der letzte, also "replace" läuft für 2 von 3 Schleifendurchläufen (Vorausgesetzt es gibt 3 Spalten in denen Werte fehlen).
asahdkhaled
User
Beiträge: 29
Registriert: Samstag 28. Oktober 2017, 22:07

Sonntag 29. Oktober 2017, 12:08

Das Problem hat sich übrigens verlagert, plötzlich funktioniert es für die letzte Spalte aber für die erste nicht.
Außerdem wenn ich das Skript häufiger durchführe, sehe ich irgendwann, dass es doch funktioniert.
Liegt es vll an den mehrfachen execute() Statements, und Python kann das nicht so schnell verarbeiten, kommt durcheinander oder ähnliches???? Danke
Benutzeravatar
noisefloor
User
Beiträge: 2523
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: Görgeshausen
Kontaktdaten:

Sonntag 29. Oktober 2017, 13:01

Hallo,
Liegt es vll an den mehrfachen execute() Statements, und Python kann das nicht so schnell verarbeiten, kommt durcheinander oder ähnliches?
Nein, sicher nicht. `cur.execute(sqlstatement)` schickt `sqlstatement ja nur an die DB, um die Verarbeitung kümmert sich die Datenbank, damit hat Python nix zu tun. Wenn liegt das Problem auf der DB-Seite.

Abgesehen davon ist jeder Befehl "blocking", heißt die nächste Zeile wird erst ausgeführt, wenn die vorherige fertig ist.

Hast du mal in die Logs der DB geschaut, ob das was drin steht? Abgsehen davon halte ich es immer noch für eine gute Idee, ein `connection.commit()` nach jedem `cur.execute()` einzufügen - sofern sich die DB nicht sicher im autocommit-Modus befindet.

Gruß, noisefloor
asahdkhaled
User
Beiträge: 29
Registriert: Samstag 28. Oktober 2017, 22:07

Sonntag 29. Oktober 2017, 13:41

Bei mir steht allerdings, dass es kein commit() Befehl gibt für die connection?!
Benutzeravatar
noisefloor
User
Beiträge: 2523
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: Görgeshausen
Kontaktdaten:

Sonntag 29. Oktober 2017, 14:07

Hallo,

welches Modul nutzt du denn? Da du ja nur einen Teil des Codes gepostet hast, sieht man das nicht... Darin sieht man auch nicht sicher, wie die Instanz deines Connection-Objekts heißt.

Das cx_oracle Modul, welches IMHO das gängigste Modul ist, kennt definitiv ein `commit()` Funktion für Connection-Objekte: Link.
Und da `commit` nun mal zur SQL-Sprache gehört, werden wohl auch alle anderen Clients `commit` beherrschen....

Gruß, noisefloor
__deets__
User
Beiträge: 3973
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sonntag 29. Oktober 2017, 14:12

@noisefloor DDL-statements in Oracle sind nicht transactional. Wenn sie es wären, würde es auch eher gar nicht klappen, statt zur Hälfte. Sonst wäre die Transaktion auch nicht viel wert.

Ich halte allerdings den Ansatz für falsch. Es ist ungewöhnlich Tabellen zu modifizieren um solche Probleme wie beschreiben zu bearbeiten. Stattdessen würde ich eine Tabelle anlegen, in der ich auf die fehlerhaften Daten verweise. Um das genauer zu beurteilen fehlt aber Kontext.
asahdkhaled
User
Beiträge: 29
Registriert: Samstag 28. Oktober 2017, 22:07

Sonntag 29. Oktober 2017, 14:29

Danke für die ganzen Antworten.
@_deetz_ ich muss allerdings für einen bestimmten Use-Case die Spaltne Updaten, aber denke für deinen Hinweis

@noisefloor in der Tat, ich nutze cx_Oracle Modul
__deets__
User
Beiträge: 3973
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sonntag 29. Oktober 2017, 14:38

Ich sehe jetzt erst, das du DDL mit SQL mischst. Das würde ich ich nicht tun, es ist gut möglich, das das undefiniertes Verhalten darstellt. So wie du es auch siehst.

Erklär doch mal warum du glaubst das so tun zu müssen?
__deets__
User
Beiträge: 3973
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sonntag 29. Oktober 2017, 14:57

Und noch ein nachtrag: https://sqljana.wordpress.com/2017/01/2 ... mplicitly/

DDL stellt also implizit einen commit dar. Da du sonst keine hast, ist der Zustand irgendwie. Ich würde das SQL also wie auch schon von noisefloor vorgeschlagen mit einem commit versehen.
asahdkhaled
User
Beiträge: 29
Registriert: Samstag 28. Oktober 2017, 22:07

Sonntag 29. Oktober 2017, 15:03

um ehrlich zu sein, habe ich von python noch so ziemlich keine Ahnung.
Daher kann ich auch nichts damit anfangen, wenn ihr sagt ich mische DLL mit SQL :lol:
Kann mir da vielleicht einer mal kurz auf die Sprünge helfen?

Und welches Modul soll ich nehmen?

Code: Alles auswählen

#coding=utf-8
import cx_Oracle
dsn= cx_Oracle.makedsn("***********",1521,"orcl")
db= cx_Oracle.connect('******','*********',dsn)
ich verstehe auch nicht, wo das Commit hin soll? Wenn ich es mit in dern String reinschreibe, der in der Klammer bei execute() steht, dann kommt
cx_Oracle.DatabaseError: ORA-00933: SQL-Befehl wurde nicht korrekt beendet

Hab jetzt an den Anfang meines Skriptes conn.autocommit = True gepackt. Reicht das auch?


Und eine letzte Sache, was meinst du mit
DDL stellt also implizit einen commit dar. Da du sonst keine hast, ist der Zustand irgendwie.
Danke euch
__deets__
User
Beiträge: 3973
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sonntag 29. Oktober 2017, 15:29

Commit ist eine METHODE. So wie execute. Das schreibst du nicht einfach rein. Das rufst du auf.

cursor.execute(....)
connection.commit()

Und DDL heißt Data Definition Language. Wie man sich übrigens auch schnell ergoogeln kann. Das sind alles die Dinge, welche die Datenbankstruktur verändern, vs DML (ich hatte fälschlich SQL geschrieben), welches Daten in eben diese Struktur eingießt/ändert/entfernt.

Was du hier machst ist Dinge ins Regal zu sortieren, während gleichzeitig jemand neue Regalbretter verlegt. Das gibt Chaos, darum macht man das eigentlich nicht. Und es hat auch nix mit Python zu tun.

Deshalb musst du das letzte Tisch voneinander trennen. Erst DDL. Dann DML. Mit commit.
asahdkhaled
User
Beiträge: 29
Registriert: Samstag 28. Oktober 2017, 22:07

Sonntag 29. Oktober 2017, 15:46

Und was in meinem Code ist genau DLL und was nicht?
Gehe ich also richtig in der Annehme, dass "Alter Table" DLL ist, weil ich damit ja die Datenbankstruktur veränder und alles andere normal SQL?
Sonst ist mir nämlich nicht klar, was du meinst.

Also erst "Alter Table" dann comitten und dann den normalen "Update etc" Code?

Und ist es mit dem

Code: Alles auswählen

db.autocommit = True
getan?
asahdkhaled
User
Beiträge: 29
Registriert: Samstag 28. Oktober 2017, 22:07

Sonntag 29. Oktober 2017, 16:05

Was ich auch nicht verstehe ist, dass wenn sagen wir mal 5 Spalten fehlen,
alles funktioniert, nur für eine Spalte eine SQL Statement (das ersetzen des leeren Wertes durch einen random anderen) nicht funktioniert.
Alle anderen Statements laufen wie geplant durch. Ich kann es mir einfach nicht erklären und selbst mit connection.commit() nach jedem execute statement ist das so
Antworten