2016-12-19 23 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