繁体   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