SQLite

【Python】SQLiteの基本的な使い方

【Python】SQLiteの基本的な使い方
naoki-hn

軽量で優れたオープンソースのリレーショナルデータベースである SQLite を Python で使用する方法について解説します。

SQLite

SQLite は、軽量で優れた優れたオープンソースのリレーショナルデータベースエンジンとして広く利用されています。SQLite の公式ホームページは、こちらを参照してください。

SQLite データベースは、Python 標準ライブラリの sqlite3 モジュールで扱うことができるようになっています。

よく世の中の各種システムで使用されている Oracle、SQLServer、MySQL、PostgreSQL 等の有名なデータベースマネジメントシステム(DBMS)をご存じの方は多いと思いますが、これらの本格的なデータベースでは専用にデータ領域用ファイルを作成して動作します。

一方で、SQLite は通常のファイルにデータベースを格納します(一般的に使われる拡張子は .db)。このデータベースファイルは OS の違いなどを超えてコピーして使うことができることから移植性が非常に高くなっています。

また、SQL をサポートしているため単純なリレーショナルデータベースのアプリケーションを作る際には、SQLite は有力な選択肢の一つとなります。

本記事では、Python で SQLite データベースを使う方法について説明します。

SQLiteの基本的な使い方

SQLite の基本操作

sqlite3 を使用して SQLite データベースを扱うプログラムの基本的な流れは以下となります。

  1. sqlite3 をインポートする
  2. connect 関数でデータベースファイルに接続する
  3. cursor メソッドでデータベース操作のためのカーソルを準備する
  4. カーソルの execute メソッドで SQL を実行する
  5. INSERT 等の処理の場合は commit で変更を確定する
  6. 使用後にカーソルとコネクションをクローズする

以降で、テーブル作成、データ登録、検索、更新、削除といった基本操作について例を使って説明していきます。

データベースの準備と終了

以降のサンプルプログラムで共通である、データベースの準備と終了の処理についてまず説明します。

import sqlite3

# データベースに接続する
conn = sqlite3.connect("test.db")
# カーソルを作成する
curs = conn.cursor()

...(データベース処理)...

# カーソルとコネクションをクローズする
curs.close()
conn.close()

SQLite を使う場合には、sqlite3 をインポートし、connect 関数で SQLite データベースファイルに接続します。この時にファイルが存在すれば開かれますが、ファイルが存在しない場合はファイルが新規に作成されます。

次に、データベースの操作をするためのカーソルを cursor メソッドで生成します。

これでデータベースを操作するための準備ができ、以下で紹介する各種データベース処理を実行できます。データベースの処理が終わったら使用したカーソルと接続を close メソッドによりクローズします。

では、以降で具体的なデータベース操作を見ていきましょう。

テーブルを作成する(CREATE TABLE

テーブルを作成するには、以下のように作成したカーソルの execute メソッドにCREATE TABLE の SQL を指定して実行します。

import sqlite3

# データベースに接続する
conn = sqlite3.connect("test.db")
# カーソルを作成する
curs = conn.cursor()

print("===== CREATE TABLE =====")
# テーブルを作成する
curs.execute(
    "CREATE TABLE IF NOT EXISTS person ("
    "id INTEGER PRIMARY KEY AUTOINCREMENT"
    ",name VARCHAR"
    ",age INTEGER)"
)
# コミットする
conn.commit()

# カーソルとコネクションをクローズする
curs.close()
conn.close()

上記処理を実行すると「test.db」というデータベースファイルが作成されます。

この例では「id」「name」「age」という属性がある person テーブルを作成します。id についてはINTEGER のプライマリーキーとして設定し、自動増加するようにAUTOINCREMENT を指定しています。

また、テーブルが存在しない場合に作成されるように「IF NOT EXISTS」をつけています。この部分を除くと既にテーブルがある場合はエラーとなります。

データを追加する(INSERT

作成したテーブルにデータを追加する場合には、execute メソッドで INSERT 文を発行します。

import sqlite3

# データベースに接続する
conn = sqlite3.connect("test.db")
# カーソルを作成する
curs = conn.cursor()

print("===== INSERT =====")
# プレースホルダ―を使ってデータを指定して登録する
insert_sql = "INSERT INTO person(name, age) VALUES(?, ?)"
curs.execute(insert_sql, ("TARO", 30))
curs.execute(insert_sql, ("JIRO", 20))
curs.execute(insert_sql, ("SABURO", 10))

# コミットする
conn.commit()

# カーソルとコネクションをクローズする
curs.close()
conn.close()

データを登録する場合には、以下のように INSERT 用の SQL を用意します。

# プレースホルダ―を使ってデータを指定して登録する
insert_sql = "INSERT INTO person(name, age) VALUES(?, ?)"

値を設定するべき部分で「?」となっている部分をプレースホルダ―と言います。

curs.execute(insert_sql, ("TARO", 30))
curs.execute(insert_sql, ("JIRO", 20))
curs.execute(insert_sql, ("SABURO", 10))

プレースホルダーに値を渡しつつ SQL を実行するには、execute メソッドにプレースホルダーを含む SQL 文と入力値をタプルで渡します。設定値が一つの場合も (XXX,) のようにタプルで渡すことができます。なお、INSERT でデータ登録後は確定のために commit を忘れないようにしましょう。

データを検索する(SELECT

追加したデータを検索する場合には、execute メソッドで SELECT 文を発行します。

import sqlite3

# データベースに接続する
conn = sqlite3.connect("test.db")
# カーソルを作成する
curs = conn.cursor()

print("===== SELECT =====")
# データを検索する
select_all_sql = "SELECT * FROM person"
curs.execute(select_all_sql)
rows = curs.fetchall()
print(rows, "\n")

# 条件を指定して検索する
select_sql = "SELECT * FROM person WHERE age > ?"
curs.execute(select_sql, (20,))
rows = curs.fetchall()
print(rows)

# カーソルとコネクションをクローズする
curs.close()
conn.close()
【実行結果】
===== SELECT =====
[(1, 'TARO', 30), (2, 'JIRO', 20), (3, 'SABURO', 10)] 

[(1, 'TARO', 30)]

データを検索する場合には、以下のように SELECT 用の SQL を用意します。

# データを検索する
select_all_sql = "SELECT * FROM person"

上記はすべてのデータを取得する例ですが、もちろん以下のように条件をプレースホルダーで指定して検索することもできます。

# 条件を指定して検索する
select_sql = "SELECT * FROM person WHERE age > ?"
curs.execute(select_sql, (20,))

SELECT 結果を全て取得する場合は fetchall メソッドでタプルのリストとして取得できます。また、件数を指定する場合には、fetchmany メソッド、1 件を取得する場合は fetchone メソッドが使用できます。

rows = curs.fetchall()
print(rows)

データを更新する(UPDATE

データを更新する場合には、execute メソッドで UPDATE 文を発行します。

import sqlite3

# データベースに接続する
conn = sqlite3.connect("test.db")
# カーソルを作成する
curs = conn.cursor()

print("===== UPDATE =====")
# データを更新する
update_sql = "UPDATE person SET name=? WHERE id=?"
curs.execute(update_sql, ("SHIRO", 1))

# コミットする
conn.commit()

# カーソルとコネクションをクローズする
curs.close()
conn.close()

データを更新する場合には、以下のように UPDATE 用の SQL を用意します。

# データを更新する
update_sql = "UPDATE person SET name=? WHERE id=?"
curs.execute(update_sql, ("SHIRO", 1))

プレースホルダ―で条件を指定している部分は同じです。UPDATE でデータを更新した場合は、確定のための commit を忘れないようにしましょう。

データ更新後、上記で紹介した SELECT で全てのデータを検索すると以下のようにデータが更新されていることが確認できます。

[(1, 'SHIRO', 30), (2, 'JIRO', 20), (3, 'SABURO', 10)] 

データを削除する(DELETE

データを削除する場合には、execute メソッドで DELETE 文を発行します。

import sqlite3

# データベースに接続する
conn = sqlite3.connect("test.db")
# カーソルを作成する
curs = conn.cursor()

print("===== DELETE =====")
# データを削除する
delete_sql = "DELETE FROM person WHERE id = ?"
curs.execute(delete_sql, (2,))

# コミットする
conn.commit()

# カーソルとコネクションをクローズする
curs.close()
conn.close()

データを削除する場合には、以下のように DELETE 用の SQL を用意します。

# データを削除する
delete_sql = "DELETE FROM person WHERE id = ?"
curs.execute(delete_sql, (2,))

プレースホルダ―で条件を指定している部分は同じです。DELETE でデータを削除した場合、削除を確定するための commit を忘れないようにしましょう。

データ削除後、上記で紹介した SELECT で全てのデータを検索すると以下のようにデータが削除されていることが確認できます。

[(1, 'SHIRO', 30), (3, 'SABURO', 10)] 

プレースホルダーを使うようにしましょう

上記で紹介してきた例ではプレースホルダーを使った値の指定を行ってきました。execute メソッドは SQL 文のみをそのまま指定して実行することができるので、画面からの入力値を使って自分で SQL 文字列を組み立てて実行することも可能です。

しかし、有名な攻撃手法である SQL インジェクションの対策の一つとしてプレースホルダーを使うことは非常に重要です。以下の例で見てみましょう。

import sqlite3

# データベースに接続する
conn = sqlite3.connect("test.db")
# カーソルを作成する
curs = conn.cursor()

print("===== SELECT =====")
# 直接SQLを組み立てる
input_name = "SABURO"
select_sql = f"SELECT * FROM person WHERE name = '{input_name}'"
print(select_sql)
curs.execute(select_sql)
rows = curs.fetchall()
print(rows, "\n")

# 悪意ある入力の場合 (SQLインジェクション)
input_name = "SABURO' or 'A'='A"
select_sql = f"SELECT * FROM person WHERE name = '{input_name}'"
print(select_sql)
curs.execute(select_sql)
rows = curs.fetchall()
print(rows, "\n")

# プレースホルダーを使って検索する
input_name = "SABURO' or 'A'='A"
select_sql = "SELECT * FROM person WHERE name = ?"
curs.execute(select_sql, (input_name,))
rows = curs.fetchall()
print(rows)

# カーソルとコネクションをクローズする
curs.close()
conn.close()
【実行結果】
SELECT * FROM person WHERE name = 'SABURO'
[(3, 'SABURO', 10)] 

SELECT * FROM person WHERE name = 'SABURO' or 'A'='A'
[(1, 'TARO', 30), (2, 'JIRO', 20), (3, 'SABURO', 10)] 

[]

上記の例は、これまでに紹介した CREATE TABLEINSERT の後に、実行した場合の例となっています。

# 直接SQLを組み立てる
input_name = "SABURO"
select_sql = f"SELECT * FROM person WHERE name = '{input_name}'"
print(select_sql)
curs.execute(select_sql)
rows = curs.fetchall()
print(rows, "\n")

input_name は画面などから入力された入力値だと思ってください。上記部分では、f-string を使って入力値を埋め込んだSQL文を作成し、execute メソッドに SQL 文を指定しています。この方法でも確かに目的となるデータの抽出はできます。

# 悪意ある入力の場合 (SQLインジェクション)
input_name = "SABURO' or 'A'='A"
select_sql = f"SELECT * FROM person WHERE name = '{input_name}'"
print(select_sql)
curs.execute(select_sql)
rows = curs.fetchall()
print(rows, "\n")

次に上記部分は、悪意のある入力があった例です。input_name という画面入力欄に「SABURO' or 'A'='A」という入力がされたと思ってください。この時に組み立てられるSQL文は「SELECT * FROM person WHERE name = 'SABURO' or 'A'='A'」のようになってしまい、実行結果を見てもらうとすべてのデータが取得できてしまっています。

これは情報漏洩等の観点から大きな問題です。また、他にもデータを全て削除するようなコードが埋めこまれるなど色々なパターンの攻撃が考えられます。

# プレースホルダーを使って検索する
input_name = "SABURO' or 'A'='A"
select_sql = "SELECT * FROM person WHERE name = ?"
curs.execute(select_sql, (input_name,))
rows = curs.fetchall()
print(rows)

上記はプレースホルダーを使って検索している部分です。入力として悪意のある入力があったとしても、プレースホルダーを使った場合は検索結果は空([])となって不正にデータ取得はできていないことが分かります。

SQLインジェクション対策としては、他にも画面入力の際にエスケープ処理(ルールに従ってプログラムで使われる特殊文字や記号を別の文字列に置き換える)といった対策等がありますが、プレースホルダーは非常に有効な対策の 1 つです。

実行時に値を設定して SQL を組み立てる際はプレースホルダ―を使いましょう。

メモリを使用したデータベース操作

SQLite の便利な使い方として、メモリー上にデータベースを作る方法があります。

メモリー上でデータベースを作成し操作したい場合には、以下例のように「:memory:」と指定します。

import sqlite3

conn = sqlite3.connect(":memory:")
curs = conn.cursor()

print("===== CREATE TABLE =====")
# テーブルを作成する
curs.execute(
    "CREATE TABLE person ("
    "id INTEGER PRIMARY KEY AUTOINCREMENT, "
    "name VARCHAR,"
    "age INTEGER)"
)
# 作成したテーブルをコミットする
conn.commit()

print("===== INSERT =====")
# プレースホルダ―を使ってデータを指定して登録する
insert_sql = "INSERT INTO person(name, age) VALUES(?, ?)"
curs.execute(insert_sql, ("TARO", 30))
curs.execute(insert_sql, ("JIRO", 20))
curs.execute(insert_sql, ("SABURO", 10))
# コミットする
conn.commit()

print("===== SELECT =====")
# データを検索する
curs.execute("SELECT * FROM person")
rows = curs.fetchall()
print(rows)

print("===== UPDATE =====")
# データを更新する
update_sql = "UPDATE person SET name=? WHERE id=?"
curs.execute(update_sql, ("SHIRO", 1))
# コミットする
conn.commit()
# データを検索する
curs.execute("SELECT * FROM person")
rows = curs.fetchall()
print(rows)

print("===== DELETE =====")
# データを削除する
curs.execute("DELETE from person WHERE id = 2")
# コミットする
conn.commit()
# データを検索する
curs.execute("SELECT * FROM person")
rows = curs.fetchall()
print(rows)

# カーソルとコネクションをクローズする
curs.close()
conn.close()
【実行結果】
===== CREATE TABLE =====
===== INSERT =====
===== SELECT =====
[(1, 'TARO', 30), (2, 'JIRO', 20), (3, 'SABURO', 10)]
===== UPDATE =====
[(1, 'SHIRO', 30), (2, 'JIRO', 20), (3, 'SABURO', 10)]
===== DELETE =====
[(1, 'SHIRO', 30), (3, 'SABURO', 10)]

上記例は、これまで紹介してきた各種処理を順に実行しているプログラムになります。このプログラムはメモリ上で動作するため、プログラム実行後にデータは消えてしまいますが、何回実行しても結果は同じになります。

実際に test.db 等で作ってしまうと実行のたびに状態が変わるため、必ずしも実行結果が同じになるとは限らず、確認のためには実行前に一度ファイルを消してといった面倒な作業が必要になります。

プログラム開発時には「:memory:」でメモリ上に展開しながら動作を確認し、問題ないことが確認出来たら、実際のデータベースファイルに書いてテストすると非常に効率よく開発を進めることができます。

まとめ

軽量で優れたオープンソースのリレーショナルデータベースである SQLite を Python で使用する方法について解説しました。

SQLite データベースは、Python 標準ライブラリの sqlite3 モジュールで扱うことができます。

SQLite は、SQL をサポートしているため単純なリレーショナルデータベースのアプリケーションを作る際には有力な選択肢の 1 つです。是非、使い方の基本を覚えて Python プログラミングで活用してもらえればと思います。

ソースコード

上記で紹介しているソースコードについては GitHub にて公開しています。参考にしていただければと思います。

あわせて読みたい
【Python Tech】プログラミングガイド
【Python Tech】プログラミングガイド
ABOUT ME
ホッシー
ホッシー
システムエンジニア
はじめまして。当サイトをご覧いただきありがとうございます。 私は製造業のメーカーで、DX推進や業務システムの設計・開発・導入を担当しているシステムエンジニアです。これまでに転職も経験しており、以前は大手電機メーカーでシステム開発に携わっていました。

プログラミング言語はこれまでC、C++、JAVA等を扱ってきましたが、最近では特に機械学習等の分析でも注目されているPythonについてとても興味をもって取り組んでいます。これまでの経験をもとに、Pythonに興味を持つ方のお役に立てるような情報を発信していきたいと思います。どうぞよろしくお願いいたします。

※キャラクターデザイン:ゼイルン様
記事URLをコピーしました