pysqlite的IntegrityError:区分'NOT NULL'和'UNIQUE'违规

len*_*enz 12 python sqlite pysqlite

在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()
Run Code Online (Sandbox Code Playgroud)

但是,解析错误消息似乎是错误的,可能不可靠.有没有更好的方法来做到这一点,甚至可能使用con.executemany()

len*_*enz 3

这就是我最终所做的:

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()
Run Code Online (Sandbox Code Playgroud)

像这样,在执行数据库操作之前“手动”捕获空值。如果期间发生任何错误execute(例如违反约束UNIQUE),则跳过该条目。请注意,这INSERT OR IGNORE并不意味着忽略唯一性约束,而是忽略(即跳过)输入行。

该解决方案的缺点是对空值的检查要进行两次。不过,我想这还不算太糟糕,因为这可能是相当便宜的手术。我认为,它仍然比解析错误消息更清晰,并且可能对更改更稳健(例如 pysqlite 更新,这可能会更改错误消息中的一些细节)。

致谢:这个想法是在与 Lutz 的讨论中产生的。它也是由 Martijn 独立提出的。