簡體   English   中英

pysqlite的IntegrityError:區分'NOT NULL'和'UNIQUE'違規

[英]pysqlite's IntegrityError: distinguish 'NOT NULL' from 'UNIQUE' violation

在pysqlite中,違反NOT NULLUNIQUE約束同樣會引發IntegrityError。 不幸的是,此Exception類型不提供錯誤代碼,而只提供消息。

所以,假設我想忽略唯一約束違規,因為我知道這對給定數據是安全的,但是應該報告關鍵列中的Null值。

我想出了以下解決方案:

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

但是,解析錯誤消息似乎是錯誤的,可能不可靠。 有沒有更好的方法來做到這一點,甚至可能使用con.executemany()

這就是我最終做的事情:

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

像這樣,在執行DB操作之前,“手動”捕獲空值。 如果在execute期間發生任何錯誤(例如違反UNIQUE約束),則跳過該條目。 請注意, INSERT OR IGNORE並不意味着忽略唯一性約束,而是忽略(即跳過)輸入行。

此解決方案的缺點是檢查空值兩次。 我想這不是太糟糕,因為它可能是相當便宜的操作。 我認為,它仍然比解析錯誤消息更清晰,並且可能對更改更加健壯(例如pysqlite更新,這可能會更改錯誤消息中的某些細節)。

致謝:這個想法來自與Lutz的討論。 它也是Martijn獨立提出的。

更優雅的解決方案是完全依賴SQL(ite)功能。 通過為主鍵指定沖突子句( ON CONFLICT IGNORE ),已經實現了所需的行為:

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

因此,靜默跳過重復行(違反主鍵的唯一性約束),而Null值導致中止(導致sqlite3異常)。 這一切都是在沒有預先過濾Null / None值的數據或擺弄sqlite3 API的錯誤消息的情況下實現的。 我們現在可以簡單地調用con.executemany() ,而con.executemany()

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

以下是工作代碼:

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

測試:

>>> 
There is a NULL value
>>> 

第二次測試結果:

>>> 
There is unique violation
>>> 

希望,可以幫助你。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM