web-dev-qa-db-ja.com

Python Sqlite3:INSERT INTO table VALUE(辞書はここにあります)

辞書を使用して値をテーブルに挿入したいのですが、どうすればよいですか?

import sqlite3

db = sqlite3.connect('local.db')
cur = db.cursor()

cur.execute('DROP TABLE IF EXISTS Media')

cur.execute('''CREATE TABLE IF NOT EXISTS Media(
                id INTEGER PRIMARY KEY, title TEXT, 
                type TEXT,  genre TEXT,
                onchapter INTEGER,  chapters INTEGER,
                status TEXT
                )''')


values = {'title':'jack', 'type':None, 'genre':'Action', 'onchapter':None,'chapters':6,'status':'Ongoing'}

#What would I Replace x with to allow a 
#dictionary to connect to the values? 
cur.execute('INSERT INTO Media VALUES (NULL, x)'), values)
cur.execute('SELECT * FROM Media')

meida = cur.fetchone()

print meida
22
Crispy

dictを使用して列名と値の両方を指定しようとしている場合、少なくとも直接はできません。

これはSQLに本来備わっていることです。列名のリストを指定しない場合は、_CREATE TABLE_の順序で指定する必要があります。これは、dictには順序がないため、dictでは実行できません。もちろん、本当にしたい場合は、_collections.OrderedDict_を使用して、正しい順序になっていることを確認してから、values.values()を渡すだけです。しかし、その時点で、なぜ最初にlist(またはTuple)を使用しないのですか?すべての値が正しい順序で確実にあり、名前ではなく順序でそれらを参照したい場合は、listではなくdictを使用します。

また、SQLで列名(またはテーブル名など)をバインドする方法はなく、値だけをバインドします。

もちろん、SQLステートメントを動的に生成することもできます。例えば:

_columns = ', '.join(values.keys())
placeholders = ', '.join('?' * len(values))
sql = 'INSERT INTO Media ({}) VALUES ({})'.format(columns, placeholders)
cur.execute(sql, values.values())
_

ただし、これはほとんど常に悪い考えです。これは、生成およびexecing動的Pythonコードよりもはるかに優れています。そして、プレースホルダーを使用することのすべての利点を失っただけです。主にSQLインジェクション攻撃からの保護ですが、また、DBエンジン内でのコンパイルの高速化、キャッシュの改善など、重要性の低いものもあります。

おそらく、一歩下がって、この問題をより高いレベルから見る方が良いでしょう。たとえば、プロパティの静的リストではなく、名前と値のMediaPropertiesテーブルが必要な場合はどうでしょうか。または、代わりに、ある種のドキュメントベースのストレージが必要な場合があります(高性能のnosqlシステムか、shelveに格納されたJSONまたはYAMLオブジェクトの集まりか)。


名前付きプレースホルダー を使用した代替:

_columns = ', '.join(my_dict.keys())
placeholders = ':'+', :'.join(my_dict.keys())
query = 'INSERT INTO my_table (%s) VALUES (%s)' % (columns, placeholders)
print query
cur.execute(query, my_dict)
con.commit()
_
35
abarnert

辞書を使用するための解決策があります。まず、sql-statement

INSERT INTO Media VALUES (NULL, 'x');

abarnertが述べたように、CREATE TABLEステートメントで定義されている順序ですべての列を参照していると想定しているため、機能しません。 (- SQLite INSERT を参照してください。)

列を指定して修正したら、名前付きプレースホルダーを使用してデータを挿入できます。これの利点は、キー文字を安全にエスケープできることです。そのため、心配する必要はありません。 Python sqlite-documentation から:

values = {'title':'jack', 'type':None, 'genre':'Action', 'onchapter':None,'chapters':6,'status':'Ongoing'}
cur.execute('INSERT INTO Media (id, title, type, onchapter, chapters, status) VALUES (:id, :title, :type, :onchapter, :chapters, :status);'), values)
14
MrGumble

名前付きパラメーター を使用できます:

cur.execute('INSERT INTO Media VALUES (NULL, :title, :type, :genre, :onchapter, :chapters, :status)', values)

これはINSERTステートメントの列の順序に依存します(これらの:values辞書でキーとしてのみ使用されます)が、少なくとも値を順序付ける必要がなくなります。 python側では、ここで無視されるvaluesの他の項目を含めることができます。複数のテーブルに格納するためにdictにあるものを引き離している場合、それは役に立ちます。

それでも名前の重複を避けたい場合は、ダミーのクエリを実行した後、sqlite3.Row結果オブジェクトまたはcur.descriptionから名前を抽出できます。 CREATE TABLEを実行する場所の近くにpython形式でそれらを保持するほうが無意味かもしれません。

10
eichin

エスケープの利点を備えたより一般的な方法は次のとおりです。

# One way. If keys can be corrupted don't use.
sql = 'INSERT INTO demo ({}) VALUES ({})'.format(
            ','.join(my_dict.keys()),
            ','.join(['?']*len(my_dict)))

# Another, better way. Hardcoded w/ your keys.
sql = 'INSERT INTO demo ({}) VALUES ({})'.format(
            ','.join(my_keys),
            ','.join(['?']*len(my_dict)))

cur.execute(sql, Tuple(my_dict.values()))
3
keithpjolley
key_lst = ('status', 'title', 'chapters', 'onchapter', 'genre', 'type')
cur.execute('INSERT INTO Media (status,title,chapters,onchapter,genre,type) VALUES ' + 
            '(?,?,?,?,?,?);)',Tuple(values[k] for k in key_lst))

エスケープしてください

おそらく、どこかでcommitを呼び出す必要もあります。

1
tacaswell

私は同様の問題を抱えていて、完全に次のようなものではありませんでした(注-これはビットが変更されたOPのコードで、要求どおりに機能するようになっています)-

import sqlite3
db = sqlite3.connect('local.db')
cur = db.cursor()

cur.execute('DROP TABLE IF EXISTS Media')

cur.execute('''CREATE TABLE IF NOT EXISTS Media(
                id INTEGER PRIMARY KEY, title TEXT, 
                type TEXT,  genre TEXT,
                onchapter INTEGER,  chapters INTEGER,
                status TEXT
                )''')


values = {'title':'jack', 'type':None, 'genre':'Action',     'onchapter':None,'chapters':6,'status':'Ongoing'}

#What would I Replace x with to allow a 
#dictionary to connect to the values? 
#cur.execute('INSERT INTO Media VALUES (NULL, x)'), values)
# Added code.
cur.execute('SELECT * FROM Media')
colnames = cur.description
list = [row[0] for row in cur.description]
new_list = [values[i] for i in list if i in values.keys()]
sql = "INSERT INTO Media VALUES ( NULL, "
qmarks = ', '.join('?' * len(values))
sql += qmarks + ")"
cur.execute(sql, new_list)
#db.commit() #<-Might be important.
cur.execute('SELECT * FROM Media')
media = cur.fetchone()
print (media)
0
Subgenius

これにはかなり遅れましたが、自分の答えを追加すると思いました。専門家ではありませんが、うまくいくことがわかりました。

辞書を使用するときの順序の保存に関する問題がありますが、他のユーザーが述べていますが、次のことを行うことができます。

# We're going to use a list of dictionaries, since that's what I'm having to use in my problem
input_list = [{'a' : 1 , 'b' : 2 , 'c' : 3} , {'a' : 14 , 'b' : '' , 'c' : 43}]
for i in input_list:
    # I recommend putting this inside a function, this way if this 
    # Evaluates to None at the end of the loop, you can exit without doing an insert
    if i :
        input_dict = i 
    else:
        input_dict = None
        continue
# I am noting here that in my case, I know all columns will exist.
# If you're not sure, you'll have to get all possible columns first.

keylist = list(input_dict.keys())
vallist = list(input_dict.values())

query = 'INSERT INTO example (' +','.join( ['[' + i + ']' for i in keylist]) + ') VALUES (' + ','.join(['?' for i in vallist]) + ')'

items_to_insert = list(Tuple(x.get(i , '') for i in keylist) for x in input_list)
# Making sure to preserve insert order. 

conn = sqlite3.connect(':memory:')
cur = conn.cursor()
cur.executemany(query , items_to_insert)
conn.commit()
0
a7xcarter

私は同様の問題を抱えていたので、最初に文字列を作成してから、その文字列を渡してコマンドを実行しました。実行には時間がかかりますが、マッピングは完璧でした。ただ回避策:

create_string = "INSERT INTO datapath_rtg( Sr_no"
for key in record_tab:
    create_string = create_string+ " ," + str(key)
create_string = create_string+ ") VALUES("+ str(Sr_no) 
for key in record_tab:
    create_string = create_string+ " ," + str(record_tab[key])
create_string = create_string + ")"
cursor.execute(create_string)

上記のことを行うことで、dict(record_tab)に特定のフィールドが含まれていない場合、スクリプトがエラーをスローせず、適切なマッピングを実行できることを確認しました。そのため、最初に辞書を使用しました。

0
yash jain