2016-12-19 39 views
1

我试图在Python中使用executemany来填充一些行。Python + sqlite3:带有“构造”值的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没有返回工作,没有行案件。

回答

0

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

查询将看起来像:

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

记住要改变你的data列表,因为这些参数都以不同的顺序了。

+0

注:我删除因为它似乎是无效的语法,所以围绕SELECT选择了parens。 – jwd

+0

看起来这实际上并不起作用,如果它是一个新的ID(INSERT OR REPLACE的'INSERT'),那是对的吗?如果是这样,它对我的​​情况不起作用 – jwd

3

您的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))) 
+0

我希望我可以接受这两个答案;谢谢(: – jwd

+0

不客气(:: – nezhar

+0

)嗯,在尝试这些之后,我遇到了一些麻烦...... INSERT-OR-REPLACE没有在插入' id'不匹配我不知道 - 为什么你为最终数据添加了'i-3'? – jwd