Python + sqlite3:使用“构造”值执行许多

Posted

技术标签:

【中文标题】Python + sqlite3:使用“构造”值执行许多【英文标题】:Python + sqlite3: executemany with "constructed" values 【发布时间】:2017-05-04 21:44:06 【问题描述】:

我正在尝试在 Python 中使用 executemany 来填充一些行。

但是,我得到:

sqlite3.OperationalError: 3 values for 4 columns

我了解,一般来说,您使用executemany 尽可能高效地将原始值填充到数据库中,考虑到这一点,您通常希望插入的值的数量与列数相匹配。

但是如果一个或多个列是通过 SQL “构造”的,并且没有任何对应的输入值呢?

小例子:

#!/usr/bin/env python2.7
import os
import sys
import sqlite3
from contextlib import closing

DB_FILE = 'test.sqlite'

with closing(sqlite3.connect(DB_FILE)) as db:

    # First, some setup...
    cursor = db.cursor()
    cursor.execute('CREATE TABLE test (id TEXT PRIMARY KEY, a TEXT, b TEXT, c TEXT)')

    sql = '''INSERT INTO test (id, a, b, c) VALUES (?, ?, ?, ?)'''

    data = []
    for i in range(5):
        idStr = str(i)
        data.append( (idStr, 'a'.format(i), 'b'.format(i), 'c'.format(i)) )

    cursor.executemany(sql, data)

    # Now, to exercise the problem:
    # This does an 'upsert', but the same problem could occur in
    # other circumstances.

    sql = '''
INSERT OR REPLACE INTO test (id, a, b, c)
VALUES (
    ?,
    (SELECT a,b FROM test WHERE id = ?),
    ?
)'''

    data = []
    for i in range(3,7):
        idStr = str(i)
        data.append( (idStr, idStr, 'c'.format(i)) )

    cursor.executemany(sql, data)

    db.commit()

当您运行它时,您会收到顶部提到的错误 (3 values for 4 columns)。

我想我可以通过将 SELECT a,b 拆分为单独的 SELECT a..., SELECT b... 部分来解决此问题,但有更好的方法吗?

给出的示例很小 — 实际上,它可能是 10 列,我现在必须详细列出它们自己的 (SELECT ...) 子句。


更新 1 这不是 executemany 特有的,正如我最初所想的那样。即使手动运行纯 SQL 也有同样的问题。

更新 2 下面的答案建议使用 SELECT 而不是 VALUES,但是,在添加新行时这不起作用(如在我的“upsert”案例中),因为SELECT 在这种情况下不返回任何行。

【问题讨论】:

【参考方案1】:

您的 SQL 需要将 4 个参数放入 VALUES。问题是 SQLite 无法将多个值返回到子查询中,您将遇到以下错误: sqlite3.OperationalError:作为表达式一部分的 SELECT 只允许一个结果

您可以做的是使用 2 个 Select 语句,一个用于您需要的每个值。这是我尝试过的:

sql = '''
INSERT OR REPLACE INTO test (id, a, b, c)
VALUES (
    ?,
    (SELECT a FROM test WHERE id = ?),
    (SELECT b FROM test WHERE id = ?),
    ?
)'''

还有一个更优雅的解决方案。

sql = '''
INSERT OR REPLACE INTO test (id, a, b, c)
SELECT
    ?, a, b, ?
FROM test WHERE id = ?
'''

这是一个工作示例,我进行了一些更改,以便相应地填充数据库表:

#!/usr/bin/env python2.7
import os
import sys
import sqlite3
from contextlib import closing

DB_FILE = 'test.sqlite'

with closing(sqlite3.connect(DB_FILE)) as db:

    # First, some setup...
    cursor = db.cursor()
    cursor.execute('CREATE TABLE test (id TEXT PRIMARY KEY, a TEXT, b TEXT, c TEXT)')

    sql = '''INSERT INTO test (id, a, b, c) VALUES (?, ?, ?, ?)'''

    data = []
    for i in range(5):
        idStr = str(i)
        data.append( (idStr, 'a'.format(i), 'b'.format(i), 'c'.format(i)) )

    cursor.executemany(sql, data)

    sql = '''
    INSERT OR REPLACE INTO test (id, a, b, c)
    SELECT
        ?, a, b, ?
    FROM test WHERE id = ?
    '''

    data = []
    for i in range(4, 7):
        data.append((i, 'c'.format(i), i - 3))

    cursor.executemany(sql, data)

    db.commit()

更新 1

如果你需要默认值以防选择返回任何内容,你可以使用这个:

sql = '''
INSERT OR REPLACE INTO test (id, a, b, c)
VALUES (
    ?,
    (SELECT ifnull((SELECT a FROM test WHERE id = ?), 'Default 1')),
    (SELECT ifnull((SELECT b FROM test WHERE id = ?), 'Default 2')),
    ?
)'''

data = []
for i in range(4, 10):
    data.append((i, i, i, 'c'.format(i)))

【讨论】:

嗯,在尝试了更多之后,我遇到了一些麻烦......在 thd id 不匹配的情况下,INSERT-OR-REPLACE 不会执行 INSERT。我想知道 - 你为什么要为最终数据添加 i-3 这只是为了匹配使用第一条语句完成的条目的一些 id,因此新条目不会在 ab 列。 但是如果我 确实 想要 a 和 b 列中的 NULL(或其他一些占位符值),这样就无法实现,对吧?我的意思是,这不仅仅是设置一些值,这也是INSERT 发生的原因。 使用它可以让你在 select 不返回任何内容的情况下插入具有空值的插入: ` sql = ''' INSERT OR REPLACE INTO test (id, a, b, c) 值 (?, (SELECT a FROM test WHERE id = ?), (SELECT b FROM test WHERE id = ?), ?)''' data = [] for i in range(4, 10): data.append((i, i, i, 'c'.format(i))) `【参考方案2】:

是的!有一个更好的方法!用 SELECT 语句替换您的 VALUES 子句!

查询将如下所示:

INSERT OR REPLACE INTO test (id, a, b, c)
    SELECT ?, a, b, ? FROM test WHERE id = ?

记得更改您的data 列表,因为现在参数的顺序不同了。

【讨论】:

注意:我删除了 SELECT 周围的括号,因为它似乎是无效的语法。 看起来这实际上不起作用,如果它是一个新 ID(INSERT OR REPLACEINSERT 一侧。是吗?如果是这样,它就不起作用就我而言。

以上是关于Python + sqlite3:使用“构造”值执行许多的主要内容,如果未能解决你的问题,请参考以下文章

python-简单的sqlite3使用

python sqlite3

使用 `executemany` 更新现有 SQLite3 数据库中的条目(使用 Python sqlite3)

python sqlite3的问题

如何使用带有 Python 3.7 的 sqlite3 python 模块的 FTS5 扩展?

python内置的sqlite3模块,使用其内置数据库