2

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

但是,我得到:

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 ...)子句。


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

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

4

2 回答 2

4

您的 SQL 需要 4 个参数进入 VALUES。问题是 SQLite 无法将多个值返回到子查询中,您将遇到以下错误: sqlite3.OperationalError: only a single result allowed for a SELECT that is part of a expression

您可以做的是使用 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)))
于 2016-12-19T20:10:49.003 回答
0

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

查询将如下所示:

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

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

于 2016-12-19T19:52:22.660 回答