パラメータとしてのSQLクエリのPythonリスト


124

私はPythonのリストを持っています、lと言います

l = [1,5,8]

リストのすべての要素のデータを取得するSQLクエリを記述したいと思います。

select name from students where id = |IN THE LIST l|

どうすればこれを達成できますか?

回答:


111

これまでの回答は、値をプレーンSQL文字列にテンプレート化しています。整数の場合はこれで問題ありませんが、文字列の場合はエスケープの問題が発生します。

以下は、両方で機能するパラメーター化クエリを使用したバリアントです。

placeholder= '?' # For SQLite. See DBAPI paramstyle.
placeholders= ', '.join(placeholder for unused in l)
query= 'SELECT name FROM students WHERE id IN (%s)' % placeholders
cursor.execute(query, l)

11
','.join(placeholder * len(l))読みやすいですが、少し短くなります
ThiefMaster

7
@Thiefmaster:はい、([placeholder]*len(l))プレースホルダーは複数の文字になる可能性があるため、一般的なケースでなければなりません。
ボビンス2013年

1
@bobince私の場合、変数a = 'john'とb = 'snow'を割り当てて、got = ['a、b']という名前のタプルに格納し、同じクエリを実行しました。これを行うと、エラーが発生しました。つまり、タイプエラー:文字列のフォーマット中にすべての引数が変換されるわけではありません。どのように解決すればよいですか
TechJhola

2
この作業をdbapiに任せるのではなく、なぜ「手動」で参加したいのですか?鍵は...リストではなくタプルを使用することです
アレッサンドロDentella

5
この回答に基づいて、ここにPython 3.6の 単一行ソリューションがありcursor.execute(f'SELECT name FROM students WHERE id IN ({','.join('?' for _ in l)})', l)ます。@bobince、あなたはあなたのソリューション?で、SQLインジェクションを避けるという観点からは、使用することが安全な方法であることにも言及する必要があります。脆弱な答えはたくさんありますが、基本的にはPythonで文字列を連結するものすべてです。
toto_tico 2018年

59

最も簡単な方法は、リストをtuple最初にすることです

t = tuple(l)
query = "select name from studens where id IN {}".format(t)

1
これは実際に正しい答えです。なぜ無視されたのかわかりません。最初にリストlを設定し、次にタプルを使用してから、タプルをクエリに渡します。よくやった。
MEdwin 2018年

11
@renstrmで述べたように、lに1つの要素しか含まれていない場合、これは機能しません。IDIN(1)になります。これは構文エラーです。
ダブルダウン

1
@Borisあなたの入力が常にリストであることを保証することができない場合、あなたはこの1つのライナーに似た何かを行うことができますstackoverflow.com/questions/38821586/...をクエリに引数を渡す前に
アミールImani

10
これは、ここで受け入れられているもの以外のほとんどの回答と同様に、SQLインジェクションに対して脆弱であるため、使用しないでください。
ベーコンビット

2
@BaconBits Fair警告ですが、SQLインジェクションが問題ではない一部のアプリケーション(たとえば、私が唯一のユーザーであるデータベースの分析)の場合は、これで問題ありません。
アイリーン

26

複雑にしないでください。この解決策は簡単です。

l = [1,5,8]

l = tuple(l)

params = {'l': l}

cursor.execute('SELECT * FROM table where id in %(l)s',params)

ここに画像の説明を入力してください

これが役に立てば幸いです!!!


10
l要素が1つだけの場合、これは機能しませんid IN (1,)。結果はになります。これは構文エラーです。
renstrm

3
lに1つの要素しか含まれていない場合は、それがタプルであることを確認してください。私の意見では、この解決策は受け入れられた解決策よりも明確です。
アレッサンドロデンテラ2017年

2
ただし、タプルが空の場合は引き続き機能しないため、クエリを実行する前に追加のチェックが必要です。
НикитаКонин

2
これは私にとってはうまくいきませんsqlite3。どのライブラリに対してこれをテストしましたか?
Nick Chammas

1
良い解決策ですが、@ renstrmとНикита-Конинは、タプルに単一の要素があるか要素がない場合に追加のチェックを要求するのに適しています。
アニッシュサナ

23

あなたが望むSQLは

select name from studens where id in (1, 5, 8)

あなたがPythonからこれを構築したいなら、あなたは使うことができます

l = [1, 5, 8]
sql_query = 'select name from studens where id in (' + ','.join(map(str, l)) + ')'

マップ機能は、使用してカンマで一緒に接着することができる文字列のリストにリストを変換しますstr.joinの方法を。

または:

l = [1, 5, 8]
sql_query = 'select name from studens where id in (' + ','.join((str(n) for n in l)) + ')'

map関数よりもジェネレーター式を好む場合。

更新:S.ロットは、Python SQLiteバインディングがシーケンスをサポートしていないことをコメントで述べています。その場合は、

select name from studens where id = 1 or id = 5 or id = 8

によって生成されます

sql_query = 'select name from studens where ' + ' or '.join(('id = ' + str(n) for n in l))

2
:-(結合PythonのSQLiteはシーケンスをサポートしていません。
S.Lott

ああ?気づかなかった 繰り返しになりますが、SQLiteバインディングソリューションが必要だとは思いもしませんでした。
ブレアコンラッド

申し訳ありませんが、SQLiteを示唆または示唆するものではありません。リストのバインディングがそこで機能しないのは残念です。
S.Lott、2008年

11

string.joinは、コンマで区切られたリスト値であり、フォーマット演算子を使用してクエリ文字列を形成します。

myquery = "select name from studens where id in (%s)" % ",".join(map(str,mylist))

(ありがとう、ブレアコンラッド


2
このアプローチはデータベースパラメータの置換を使用しないため、SQLインジェクション攻撃にさらされます。ここ%で使用されているのは、単なるプレーンなPython文字列フォーマットです。
Nick Chammas

8

ボビンスの答えが好きです:

placeholder= '?' # For SQLite. See DBAPI paramstyle.
placeholders= ', '.join(placeholder for unused in l)
query= 'SELECT name FROM students WHERE id IN (%s)' % placeholders
cursor.execute(query, l)

しかし、私はこれに気づきました:

placeholders= ', '.join(placeholder for unused in l)

と置き換えることができます:

placeholders= ', '.join(placeholder*len(l))

賢くなく、一般的ではない場合、これはより直接的であると思います。ここでlは長さを指定する必要があります(つまり、__len__メソッドを定義するオブジェクトを参照)。これは問題にはなりません。ただし、プレースホルダーも単一の文字でなければなりません。複数文字のプレースホルダーの使用をサポートするには:

placeholders= ', '.join([placeholder]*len(l))

2

(1)は有効なSQLではないため、1要素のタプルで壊れたため、@ umount回答の解決策:

>>> random_ids = [1234,123,54,56,57,58,78,91]
>>> cursor.execute("create table test (id)")
>>> for item in random_ids:
    cursor.execute("insert into test values (%d)" % item)
>>> sublist = [56,57,58]
>>> cursor.execute("select id from test where id in %s" % str(tuple(sublist)).replace(',)',')'))
>>> a = cursor.fetchall()
>>> a
[(56,), (57,), (58,)]

SQL文字列の他のソリューション:

cursor.execute("select id from test where id in (%s)" % ('"'+'", "'.join(l)+'"'))

5
SQLインジェクションのため、これはより良いソリューションではありません。
Anurag 2016

2
placeholders= ', '.join("'{"+str(i)+"}'" for i in range(len(l)))
query="select name from students where id (%s)"%placeholders
query=query.format(*l)
cursor.execute(query)

これで問題が解決するはずです。


2

Psycopg2ライブラリでPostgreSQLを使用している場合は、そのタプル適応にすべてのエスケープと文字列補間を行わせることができます。例:

ids = [1,2,3]
cur.execute(
  "SELECT * FROM foo WHERE id IN %s",
  [tuple(ids)])

つまり、INパラメータをとして渡していることを確認してくださいtuple。それがaのlist場合、= ANY配列構文を使用できます

cur.execute(
  "SELECT * FROM foo WHERE id = ANY (%s)",
  [list(ids)])

これらはどちらも同じクエリプランに変換されるため、どちらか簡単な方を使用する必要があります。たとえば、リストがタプルに入っている場合は前者を使用し、リストに格納されている場合は後者を使用します。


1

たとえば、SQLクエリが必要な場合:

select name from studens where id in (1, 5, 8)

何について:

my_list = [1, 5, 8]
cur.execute("select name from studens where id in %s" % repr(my_list).replace('[','(').replace(']',')') )


1
l = [1] # or [1,2,3]

query = "SELECT * FROM table WHERE id IN :l"
params = {'l' : tuple(l)}
cursor.execute(query, params)

:var表記は単純に思えます。(Python 3.7)


0

これはパラメータ置換を使用し、単一の値リストのケースを処理します。

l = [1,5,8]

get_operator = lambda x: '=' if len(x) == 1 else 'IN'
get_value = lambda x: int(x[0]) if len(x) == 1 else x

query = 'SELECT * FROM table where id ' + get_operator(l) + ' %s'

cursor.execute(query, (get_value(l),))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.