gologiusの巣

プログラミングなどの技術メモです。誰かの役に立てるとうれしいです。

【Python】SQLiteでSQLエラーが発生する

SQLiteプレースホルダー関連でエラーが発生するので解決砲を記載。ハマる人はハマるのではと思ったり。

なお、下記のソースはそのままでは動かないので適宜改変してください。

その1 ''で囲んでいる

ソース

sql = "SELECT NAME FROM MEMBERS WHERE NAME='?'"  #<<<<<<これ
name = "testname"

try :
   #SQL実行
   conn = sqlite3.connect("your db path.db")
   cur = conn.cursor()
   cur.execute(sql,(name, ))

   cur.close()
   conn.close()
except:
   print(traceback.format_exc())
   return DBResult.SQL_ERROR

結果

Traceback (most recent call last):
sqlite3.ProgrammingError: Incorrect number of bindings supplied. The current statement uses 0, and there are 1 supplied.

原因

?を''で囲んだせいで、?が文字列として認識されてしまっているようです。 よって、プレースホルダ(?)が0個なのに、値(name)を一個プレスホルダに入れようとしていてエラーになっています。

SQLをかじった人なら、SQL内で文字列比較する際には''囲みをすることを把握しているかと思います。 それにより引き起こされるミスですね。

解決策

下記のようにする

sql = "SELECT NAME FROM MEMBERS WHERE NAME=?"

その2 タプルで渡していない

ソース

sql = "INSERT INTO MEMBERS (NAME, GROUP) VALUES (?, ?)"
values = ["testname", "A"] #<<<<<<これ

try :
   #SQL実行
   conn = sqlite3.connect("your db path.db")
   cur = conn.cursor()
   cur.execute(sql, values) #<<<<<<これ

   cur.close()
   conn.close()
except:
   print(traceback.format_exc())

エラー

sqlite3.ProgrammingError: Incorrect number of bindings supplied. The current statement uses 1, and there are 2 supplied.

原因

execute()の引数はタプル、executemany()の引数はタプルのリストとなります。 実際の内部処理はよくわかりませんが、プレースホルダ(?)が2に対して、引数がリストでまとめて1つ、 と判断されているのだと思います。

上記のエラーでとりあえずググれば出てくる、有名な話ですね。

解決策

タプルに変換すればいいです

values = ("testname", "A") #タプルにしてあげる
values = ("testname",) #要素が一つの場合は、末尾カンマも忘れずに

#executemanyを使用する場合、タプルのリストに変換してあげる
value_list = ["testname1","testname2","testname3"]
tuple_list = []
   for name in value_list:
       tuple_list.append((name,))

その3 executemanyをselectで使用している

ソース

sql = "SELECT NAME FROM MEMBERS WHERE NAME=?"

name_list = ["aaa","bbb","ccc"]
tuple_list = []
   for name in name_list:
       name_list.append((name,)) #引数がlist[tuple]でないと受け付けないため、変換する

try :
   #SQL実行
   conn = sqlite3.connect("your db path.db")
   cur = conn.cursor()
   cur.executemany(sql, tuple_list)

   cur.close()
   conn.close()
except:
   print(traceback.format_exc())

エラー

sqlite3.ProgrammingError: executemany() can only execute DML statements.

原因

executemanyはDML文でしか使用できません。

DML文とは、表の値を操作したり、削除したりと、「表に変更を与えるようなSQL」です。 最後にcommit()が必要な文ですね。

docs.oracle.com

SELECTは読み取るだけなので、表に変化は与えません。 なのでexecutemanyが使用できないみたいです。

解決策

条件文を駆使して、SQLの構文を用いてまとめて取得しましょう。 下記はIN文での例です

name_list = ["aaa","bbb","ccc"]
sql = "SELECT NAME FROM MEMBERS WHERE NAME IN (" + ",".join(name_list) + ")"

try :
   #SQL実行
   conn = sqlite3.connect("your db path.db")
   cur = conn.cursor()
   cur.execute(sql)

   cur.close()
   conn.close()
except:
   print(traceback.format_exc())

もしくはSQLを都度発行する方法もあります。 パフォーマンス的にはあまりよくないのかもしれませんが・・・

name_list = ["aaa","bbb","ccc"]

try :
    sql = "SELECT NAME, GROUP FROM MEMBER WHERE NAME = ?"    
    conn = sqlite3.connect("your db path.db")
    cur = conn.cursor()

    for name in name_list:    
        cur.execute(sql, (name,))
        records = cur.fetchall()
        print(records)

    cur.close()
    conn.close()
except:
    print(sql)
    print(traceback.format_exc())
    return DBResult.SQL_ERROR

まとめ

最近SQL全然使ってないのでハマった。