2014-06-16 5 views
12

In pysqlite, Verletzung einer NOT NULL oder eine UNIQUE Einschränkung ebenfalls einen IntegrityError. Leider bietet dieser Ausnahmetyp keinen Fehlercode, sondern nur eine Nachricht.pysqlite IntegrityError: unterscheiden 'NOT NULL' von 'UNIQUE' Verletzung

Also sagen wir, ich möchte Unique-Constraint-Verletzungen ignorieren, weil ich weiß, dass dies für die gegebenen Daten sicher ist, aber Null-Werte in den Schlüsselspalten sollten gemeldet werden.

Ich habe mit folgenden Lösung gekommen:

con = sqlite3.connect(':MEMORY:') 
con.execute('''CREATE TABLE ABCD (A TEXT NOT NULL, 
            B TEXT NOT NULL, 
            C TEXT NOT NULL, 
            D TEXT NOT NULL, 
            PRIMARY KEY (A, B))''') 
with con: 
    for a, b, c, d in inputs: 
     try: 
      con.execute('INSERT INTO ABCD VALUES (?, ?, ?, ?)', 
         (a, b, c, d)) 
     except sqlite3.IntegrityError as e: 
      # Skip 'not unique' errors, but raise others. 
      if not e.message.endswith('not unique'): 
       raise 
con.close() 

scheint jedoch falsch die Fehlermeldung Parsen und könnte unzuverlässig sein. Gibt es einen besseren Weg, dies zu tun, vielleicht sogar con.executemany()?

+0

Ich habe keine Antwort auf Ihre Frage haben. Aber ich frage mich, was eine Beschränkung ist, wenn sie sicher ignoriert werden kann? Warum lass es nicht einfach fallen? In Ihrem Beispiel ist der Code '(A, B)' PK. Ich denke, dass diese Einschränkung nicht ignoriert werden darf. Was wäre ein echter Anwendungsfall? –

+0

Die eindeutige Einschränkung wird nicht ignoriert, die Eingabezeilen sind. Es wird erwartet, dass die Eingabe Duplikate für diese Felder enthält, und ich überspringe Wiederholungen stillschweigend. – lenz

+0

Sie können die Eingabe filtern und alle Duplikate entfernen, bevor Sie sie an SQLite übergeben. –

Antwort

2

Dies ist, was ich am Ende tun: diese

con = sqlite3.connect(':MEMORY:') 
con.execute('''CREATE TABLE ABCD (A TEXT NOT NULL, 
            B TEXT NOT NULL, 
            C TEXT NOT NULL, 
            D TEXT NOT NULL, 
            PRIMARY KEY (A, B))''') 
with con: 
    for a, b, c, d in inputs: 
     if any(elem is None for elem in (a, b, c, d)): 
      raise ValueError('Fields must not be empty.') 
     con.execute('INSERT OR IGNORE INTO ABCD VALUES (?, ?, ?, ?)', 
        (a, b, c, d)) 
con.close() 

Wie werden leere Werte „von Hand“ gefangen, bevor Sie den DB-Betrieb ausgeführt wird. Tritt während execute ein Fehler auf (z. B. ein Verstoß gegen die UNIQUE-Einschränkung), wird der Eintrag übersprungen. Bitte beachten Sie, dass INSERT OR IGNORE nicht bedeutet, die Eindeutigkeitseinschränkung zu ignorieren, sondern eine Eingabezeile zu ignorieren (dh zu überspringen).

Der Nachteil dieser Lösung ist, dass die Überprüfung auf leere Werte zweimal durchgeführt wird. Ich schätze das ist aber nicht so schlimm, da es vermutlich eine eher billige Operation ist. Ich denke, es ist immer noch sauberer als das Parsen der Fehlermeldung und wahrscheinlich robuster gegenüber Änderungen (wie zum Beispiel ein Pysqlite-Update, das einige Details in der Fehlermeldung ändern kann).

Danksagungen: Die Idee entstand aus der Diskussion mit Lutz. Es wurde unabhängig davon auch von Martijn vorgeschlagen.

-1

Im Folgenden finden Sie einen Arbeitscode:

import sqlite3 

con = sqlite3.connect(':memory:') 
con.execute('''CREATE TABLE ABCD (A TEXT NOT NULL, 
            B TEXT NOT NULL, 
            C TEXT NOT NULL, 
            D TEXT NOT NULL, 
            PRIMARY KEY (A, B));''') 

inputs = [('cow', 'pig', 'cat', 'dog'), ('cow', 'pig', 'quail', 'turkey')] 
with con: 
    for a, b, c, d in inputs: 
     try: 
      con.execute('INSERT INTO ABCD VALUES (?, ?, ?, ?);', 
         (a, b, c, d)) 
     except sqlite3.IntegrityError as e: 
      if 'not null' in e.args[0].lower(): 
       print('There is a NULL value') 
      elif 'unique constraint' in e.args[0].lower(): 
       print('There is unique violation') 
      else: 
       raise 

Test:

>>> 
There is a NULL value 
>>> 

Zweites Tester:

>>> 
There is unique violation 
>>> 

Hopes, können Ihnen helfen.

+1

Die Frage fragt nach einem Weg, der * besser * ist als das Parsen der Fehlermeldung. –

+0

Dieser Code ignoriert UNIQUE-Constraint-Verletzungen nicht. –

+0

@ CL. Ich habe meine Antwort bearbeitet, um NULL und UNIQUE-Verletzung zu fangen. –

0

Eine elegantere Lösung besteht darin, sich vollständig auf die SQL (ite) -Funktionalität zu verlassen. einen Konfliktsatz für den Primärschlüssel (ON CONFLICT IGNORE) wird das gewünschte Verhalten bereits erreicht, indem festgelegt wird:

con = sqlite3.connect(':memory:') 
con.execute('''CREATE TABLE ABCD (A TEXT NOT NULL, 
            B TEXT NOT NULL, 
            C TEXT NOT NULL, 
            D TEXT NOT NULL, 
            PRIMARY KEY (A, B) ON CONFLICT IGNORE)''') 

Somit doppelte Linien (die die Eindeutigkeitsbedingung des Primärschlüssels verletzen) geräuschlos übersprungen werden, während die Null-Werte einen Abbruch verursachen (was zu einer Ausnahme von sqlite3 führt). Dies alles wird erreicht, ohne die Daten für Null/None-Werte vorzufiltern oder mit Fehlernachrichten der sqlite3-API herumzuspielen. Wir können jetzt rufen Sie einfach con.executemany() ohne weiteres:

with con: 
    con.executemany('INSERT INTO ABCD VALUES (?, ?, ?, ?)', inputs) 
Verwandte Themen