Day 63:データ操作 ― 「増やす・直す・消す」でテーブルを生きたものにする
Day 63では、データベースの世界でとても重要な3つの動きを扱います。
- INSERT:新しいレコードを「追加」する
- UPDATE:既存のレコードを「更新」する
- DELETE:いらなくなったレコードを「削除」する
昨日までの SELECT は「読む」操作でしたが、 今日は「書く・直す・消す」という、より“生活感”のある操作に踏み込んでいきます。
テーブルがただの「静止した表」ではなく、 時間とともに変化していく生きたデータ に見えてくるはずです。
データ操作の全体像をイメージする
3つの基本動作で、ほとんどのCRUDができてしまう
データベースの世界では、よく CRUD という言葉が出てきます。
- Create → INSERT(作る)
- Read → SELECT(読む)
- Update → UPDATE(直す)
- Delete → DELETE(消す)
今日はこのうち、C / U / D をしっかり体験していきます。
例として、Day 61・62で使った users テーブルをそのまま使います。
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL,
age INTEGER
);
このテーブルに対して、 「ユーザーを追加する」「メールアドレスを直す」「ユーザーを削除する」 といった操作を、Python+SQLiteで書いていきます。
INSERT ― 新しいレコードを追加する
INSERTの基本形を押さえる
まずは、INSERT からいきましょう。
新しいユーザーを登録したい
という場面をイメージします。
SQLの基本形はこうです。
INSERT INTO users (name, email, age)
VALUES ('Taro', 'taro@example.com', 25);
Python+SQLiteで書くと、こんな感じになります。
# day63_data_ops.py
import sqlite3
from pathlib import Path
def get_connection(db_name: str = "day61_example.db") -> sqlite3.Connection:
"""
SQLiteデータベースに接続するための関数です。
すでに作成済みの DB を再利用します。
"""
return sqlite3.connect(Path(db_name))
def insert_user(conn: sqlite3.Connection, name: str, email: str, age: int | None = None) -> None:
"""
users テーブルに新しいユーザーを追加する関数です。
1回の呼び出しで、1レコード(1行)が追加されます。
"""
print(f"[INSERT] ユーザーを追加します: name={name}, email={email}, age={age}")
sql = """
INSERT INTO users (name, email, age)
VALUES (?, ?, ?);
"""
cur = conn.cursor()
# ? プレースホルダに、Python側の値を渡します。
cur.execute(sql, (name, email, age))
conn.commit()
print("[INSERT] ユーザーの追加が完了しました。")
Pythonプレースホルダを使う理由をしっかり理解しておく
ここは少し深掘りしておきたいポイントです。
VALUES (?, ?, ?)の?は「あとで値を差し込む場所」です。cur.execute(sql, (name, email, age))で、Python側の値を安全に埋め込みます。- 文字列連結で
"... VALUES ('" + name + "', ..."のように書くと、 入力値に悪意のある文字列が混ざったときに、SQLインジェクションの危険が出てきます。
「SQLは必ずプレースホルダ+executeで書く」 この癖を早めにつけておくと、後々の安全性がぐっと高まります。
UPDATE ― 既存のレコードを更新する
UPDATEの基本形
次に、UPDATE です。
あるユーザーのメールアドレスを変更したい
という場面を考えてみます。
SQLの基本形はこうです。
UPDATE users
SET email = 'new@example.com'
WHERE id = 1;
SETで「どのカラムをどう変えるか」を指定WHEREで「どのレコードを対象にするか」を指定
Pythonで書くと、こうなります。
def update_user_email(conn: sqlite3.Connection, user_id: int, new_email: str) -> None:
"""
指定した id のユーザーのメールアドレスを更新する関数です。
"""
print(f"[UPDATE] ユーザーのメールアドレスを更新します: id={user_id}, new_email={new_email}")
sql = """
UPDATE users
SET email = ?
WHERE id = ?;
"""
cur = conn.cursor()
# SET の ? に new_email、WHERE の ? に user_id を渡します。
cur.execute(sql, (new_email, user_id))
conn.commit()
print("[UPDATE] メールアドレスの更新が完了しました。")
PythonWHEREなしのUPDATEは「全件更新」になる
ここで、かなり重要な注意点があります。
もし、こんなふうに書いてしまったらどうなるでしょうか。
UPDATE users
SET email = 'test@example.com';
WHERE がありません。
この場合、users テーブルの全レコードの email が同じ値に書き換えられてしまいます。
怖いですよね。
UPDATEを書くときは、
- 「どのカラムを変えるか」
- 「どのレコードを対象にするか」
この2つをセットで意識することが、とても大事です。
DELETE ― レコードを削除する
DELETEの基本形
最後に、DELETE です。
あるユーザーを削除したい
という場面を考えます。
SQLの基本形はこうです。
DELETE FROM users
WHERE id = 1;
Pythonで書くと、こうなります。
def delete_user(conn: sqlite3.Connection, user_id: int) -> None:
"""
指定した id のユーザーを削除する関数です。
"""
print(f"[DELETE] ユーザーを削除します: id={user_id}")
sql = """
DELETE FROM users
WHERE id = ?;
"""
cur = conn.cursor()
cur.execute(sql, (user_id,))
conn.commit()
print("[DELETE] ユーザーの削除が完了しました。")
PythonDELETEも「WHEREなし」はとても危険
UPDATEと同じく、DELETEも WHERE がないと危険です。
DELETE FROM users;
これは、users テーブルの全レコードを削除する という意味になります。
もちろん、意図的に「全件削除」したい場面もありますが、 多くの場合は「うっかり」だと大惨事です。
DELETEを書くときは、 「本当にこの条件で合っているか?」 を一度立ち止まって確認する癖をつけておくと安心です。
INSERT・UPDATE・DELETEをまとめて試すミニプロジェクト
ここまでの3つを、ひとつの流れで試せるように、 Day 63ミニプロジェクト としてまとめてみます。
# day63_crud_demo.py
import sqlite3
from pathlib import Path
def get_connection(db_name: str = "day61_example.db") -> sqlite3.Connection:
return sqlite3.connect(Path(db_name))
def create_users_table(conn: sqlite3.Connection) -> None:
"""
users テーブルがない場合に作成する関数です。
Day 61 と同じ定義を使います。
"""
sql = """
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL,
age INTEGER
);
"""
cur = conn.cursor()
cur.execute(sql)
conn.commit()
def insert_user(conn: sqlite3.Connection, name: str, email: str, age: int | None = None) -> None:
sql = "INSERT INTO users (name, email, age) VALUES (?, ?, ?);"
cur = conn.cursor()
cur.execute(sql, (name, email, age))
conn.commit()
def update_user_email(conn: sqlite3.Connection, user_id: int, new_email: str) -> None:
sql = "UPDATE users SET email = ? WHERE id = ?;"
cur = conn.cursor()
cur.execute(sql, (new_email, user_id))
conn.commit()
def delete_user(conn: sqlite3.Connection, user_id: int) -> None:
sql = "DELETE FROM users WHERE id = ?;"
cur = conn.cursor()
cur.execute(sql, (user_id,))
conn.commit()
def fetch_all_users(conn: sqlite3.Connection) -> list[tuple]:
sql = "SELECT id, name, email, age FROM users;"
cur = conn.cursor()
cur.execute(sql)
return cur.fetchall()
def main():
conn = get_connection()
create_users_table(conn)
print("=== INSERT: ユーザーを3人追加します ===")
insert_user(conn, "Taro", "taro@example.com", 25)
insert_user(conn, "Hanako", "hanako@example.com", 30)
insert_user(conn, "Ken", "ken@example.com", 22)
print("\n=== 現在のユーザー一覧 ===")
for u in fetch_all_users(conn):
print(f"id={u[0]}, name={u[1]}, email={u[2]}, age={u[3]}")
print("\n=== UPDATE: id=1 のメールアドレスを変更します ===")
update_user_email(conn, user_id=1, new_email="taro.new@example.com")
print("\n=== UPDATE後のユーザー一覧 ===")
for u in fetch_all_users(conn):
print(f"id={u[0]}, name={u[1]}, email={u[2]}, age={u[3]}")
print("\n=== DELETE: id=2 のユーザーを削除します ===")
delete_user(conn, user_id=2)
print("\n=== DELETE後のユーザー一覧 ===")
for u in fetch_all_users(conn):
print(f"id={u[0]}, name={u[1]}, email={u[2]}, age={u[3]}")
conn.close()
if __name__ == "__main__":
main()
Pythonこのスクリプトを実行すると、
- テーブルがなければ作成
- ユーザーを3人追加(INSERT)
- 一覧表示
- 1人のメールアドレスを更新(UPDATE)
- 再度一覧表示
- 1人を削除(DELETE)
- 最後に一覧表示
という流れが、目で追えるようになっています。
Day 63のまとめ ― データを「動かせる」ようになると、世界が一段階変わる
今日扱ったキーワードは、
- INSERT:新しいレコードを追加する
- UPDATE:既存のレコードを更新する
- DELETE:いらなくなったレコードを削除する
でした。
そして、どの操作でも共通して大事だったのが、
- プレースホルダ
?を使って、安全に値を渡すこと WHEREで「どのレコードを対象にするか」をきちんと指定することconn.commit()で変更を確定させること
この3つです。
ここまで来ると、 データベースは「ただ読むだけの箱」ではなく、 「増やして、直して、整理していける、ちゃんとした情報の拠点」 に見えてくるはずです。
