【问题标题】:pysqlite's IntegrityError: distinguish 'NOT NULL' from 'UNIQUE' violationpysqlite 的 IntegrityError:区分 'NOT NULL' 和 'UNIQUE' 违规
【发布时间】:2014-08-06 10:04:57
【问题描述】:

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

【问题讨论】:

  • 我没有回答你的问题。但是我想知道如果可以安全地忽略约束有什么用?为什么不直接放弃呢?在您的示例代码(A, B) 中,您是PK。我认为这个约束不能被忽视。什么是真实世界的用例?
  • 唯一约束不被忽略,输入行被忽略。输入应包含这些字段上的重复项,我正在默默地跳过重复项。
  • 您可以在将输入传递给 SQLite 之前过滤输入并删除所有重复项。
  • 仅在键列中根据 NULL 值对数据进行预过滤要容易得多。忽略非唯一键就像使用 INSERT OR IGNORE 一样简单。
  • 查看 pysqlite 和 sqlite3 源代码,表明您正在寻找的返回错误代码是 #define SQLITE_CONSTRAINT 19 并且对于所有违反约束的约束类型都是相同的。这与 SQLITE_MISMATCH 一起映射到pysqlite IntegrityException.Parsing 与该异常相关的错误消息似乎是在不向绑定和 sqlite3 添加新功能的情况下区分情况的唯一方法。

标签: python sqlite pysqlite


【解决方案1】:

这就是我最终做的:

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 独立提出的。

【讨论】:

    【解决方案2】:

    更优雅的解决方案是完全依赖 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(),不用多说:

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

    【讨论】:

      【解决方案3】:

      下面是一个工作代码:

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

      希望,可以帮助你。

      【讨论】:

      • 该问题要求一种比解析错误消息更好的方法。
      • 此代码不会忽略 UNIQUE 约束违规。
      • @CL。我已经编辑了我的答案以捕获 NULL 和 UNIQUE 违规。
      猜你喜欢
      • 2012-09-11
      • 2014-10-16
      • 2023-03-20
      • 2023-03-29
      • 2017-12-27
      • 2020-12-19
      • 2016-04-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多