2016-12-19 12 views
1

私はPythonでexecutemanyを使っていくつかの行を移植しようとしています。Python + sqlite3:executemanyの値が "constructed"

sqlite3.OperationalError: 3 values for 4 columns 

は、私は一般的に、あなたは、できるだけ効率的にDBに生の値を詰め込むためにexecutemanyを使用することを理解し、そのことを念頭に置いて、あなたは一般的に数をしたい:

しかし、私は取得しています列の数と一致するように挿入された値。

しかし、1つ以上の列が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 ...)句で冗長にリストする必要があります。


UPDATE 1私が当初考えていたように、これは、executemanyに固有ではありません。手作業でSQLを実行しても同じ問題があります。

UPDATE 2つの答えは以下使用示唆SELECTなくVALUES、しかし、これは、新しい行がSELECT返すので、(私の「アップサート」の場合のように)追加されるいかなる作業もしないことに行ありません場合。

答えて

0

はい!より良い方法があります! VALUES句をSELECTステートメントに置き換えてください!

クエリは次のようになります。

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

は、パラメータが、今異なる順序になっているので、あなたのdataリストを変更することを忘れないでください。

+0

注:それは無効な構文だと思われるので、SELECTの周りに敬遠してください。 – jwd

+0

これは新しいID(INSERTまたはREPLACEのINSERT側)が実際には機能しないようですが、そうであれば私の場合は機能しません – jwd

3

SQLでは、VALUESに4つのパラメータが必要です。問題は、SQLiteが複数の値をサブクエリに返すことができないため、次のエラーが発生することです。 sqlite3.OperationalError:式の一部であるSELECTに対して1つの結果しか許容されません。

可能なこと必要な値ごとに1つずつ、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はINSERTを行っていません。あなたが最後のデータに 'i-3'を追加したのはなぜですか? – jwd