2014-06-16 9 views
12

In pysqlite, la violazione di un vincolo NOT NULL o UNIQUE genera anche un'eccezione Integrity. Sfortunatamente, questo tipo di eccezione non fornisce un codice di errore, ma solo un messaggio.IntegrityError di pysqlite: distinguere 'NOT NULL' dalla violazione 'UNIQUE'

Quindi, supponiamo di voler ignorare le violazioni dei vincoli univoci, perché so che questo è sicuro sui dati dati, ma i valori Null nelle colonne chiave devono essere segnalati.

mi è venuta in mente la seguente soluzione:

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() 

Tuttavia, l'analisi del messaggio di errore sembra sbagliato e potrebbe essere inaffidabile. C'è un modo migliore per farlo, magari usando anche con.executemany()?

+0

Non ho una risposta alla tua domanda. Ma mi chiedo a cosa serve un vincolo se può essere ignorato in sicurezza? Perché non lasciarlo? Nel tuo codice di esempio '(A, B)' sei PK. Penso che questo vincolo non debba essere ignorato. Quale sarebbe un caso d'uso del mondo reale? –

+0

Il vincolo univoco non viene ignorato, le linee di input lo sono. Ci si aspetta che l'input contenga duplicati su quei campi, e sto saltando silenziosamente le ripetizioni. – lenz

+0

È possibile filtrare l'input e rimuovere tutti i duplicati prima di passarlo a SQLite. –

risposta

2

Questo è quello che ho finito per fare:

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() 

In questo modo, i valori vuoti sono catturati "manualmente" prima di eseguire l'operazione DB. Se si verifica un errore durante execute (come una violazione del vincolo UNIQUE), la voce viene saltata. Si prega di notare che INSERT OR IGNORE non significa ignorare il vincolo di unicità, ma piuttosto ignorare (cioè saltare) una linea di input.

Lo svantaggio di questa soluzione è che il controllo dei valori vuoti viene eseguito due volte. Immagino che questo non sia poi così male, dal momento che è presumibilmente un'operazione piuttosto economica. Penso che sia ancora più pulito dell'analisi del messaggio di errore e probabilmente più robusto delle modifiche (come un aggiornamento di pysqlite, che potrebbe modificare alcuni dettagli nel messaggio di errore).

Crediti: L'idea è emersa dalla discussione con Lutz. È stato anche suggerito in modo indipendente da Martijn.

-1

Qui di seguito è un codice di lavoro:

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 

prova:

>>> 
There is a NULL value 
>>> 

Seconda Risultato del test:

>>> 
There is unique violation 
>>> 

speranze, può aiutare.

+1

La domanda richiede un modo * migliore * rispetto all'analisi del messaggio di errore. –

+0

Questo codice non ignora le violazioni dei vincoli UNIQUE. –

+0

@CL. Ho modificato la mia risposta per rilevare la violazione NULL e UNIQUE. –

0

Una soluzione più elegante è affidarsi interamente alla funzionalità SQL (ite). Specificando una clausola conflitto per la chiave primaria (ON CONFLICT IGNORE), il comportamento desiderato è già raggiunto:

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)''') 

Così, linee duplicate (che violano il vincolo unicità della chiave primaria) vengono scartate silenziosamente, mentre i valori Null causare una interruzione (risultante in un'eccezione sqlite3). Tutto questo è stato ottenuto senza pre-filtrare i dati per i valori Null/None o giocherellare con i messaggi di errore dell'API sqlite3. Ora possiamo semplicemente chiamare con.executemany(), senza ulteriori indugi:

with con: 
    con.executemany('INSERT INTO ABCD VALUES (?, ?, ?, ?)', inputs) 
Problemi correlati