2016-12-19 3 views
1

Я пытаюсь использовать executemany в Python для заполнения ряда строк.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 ...).


UPDATE 1 Это не относится к executemany, как я первоначально думал. Такая же проблема имеет и запуск простого SQL вручную.

UPDATE 2 ответы ниже предлагают использовать SELECT, а не VALUES, однако, это не делает никакой работы, когда новая строка добавляемого (как в моем «upsert» случае), так как SELECT возвращает нет строки в том, что дело.

ответ

0

Да! Существует лучший способ! Замените предложение VALUES с заявлением SELECT!

Запрос будет выглядеть следующим образом:

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

Не забудьте изменить data список, так как параметры в другом порядке, в настоящее время.

+0

Примечание: я удалил parens вокруг SELECT, поскольку он кажется недопустимым синтаксисом. – jwd

+0

Похоже, что на самом деле это не работает, в случае, если это новый идентификатор ('INSERT' сторона' INSERT OR REPLACE'. Правильно? Если это так, это не работает для моего дела. – jwd

3

Для SQL требуется 4 параметра в VALUES. Проблема в том, что SQLite не может вернуть несколько значений в подзапрос, вы столкнетесь со следующей ошибкой: sqlite3.OperationalError: только один результат, разрешенный для SELECT, который является частью выражения

Что вы можете do - использовать 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() 

Update 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 в том случае, id' не соответствует. Интересно, почему вы добавили 'i-3' для окончательных данных? – jwd

Смежные вопросы