2025年10月16日 星期四

SQLite 資料庫 -3

  SQLite 資料庫 -3




studentdepartment 透過 系碼 欄位建立關聯(外鍵),形成一個完整的 關聯式資料庫 (Relational Database),並在 Tkinter 介面中一次「顯示學生 + 系所資料」。


設計目標

資料庫:MySchoolDB.db
關聯: student.系碼 → 對應 department.系碼

顯示欄位如下:
| 學號 | 姓名 | 系碼 | 系名 | 系主任 |


Python + Tkinter 程式

import tkinter as tk
from tkinter import ttk, messagebox
import sqlite3
import os

DB_NAME = "MySchoolDB.db"

# === 建立資料庫與資料表 ===
def create_db():
    conn = sqlite3.connect(DB_NAME)
    cursor = conn.cursor()

    # 建立 department 資料表
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS department (
            系碼 CHAR(10) PRIMARY KEY,
            系名 VARCHAR(50),
            系主任 VARCHAR(50)
        )
    ''')

    # 建立 student 資料表,外鍵對應 department
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS student (
            學號 CHAR(10) PRIMARY KEY,
            姓名 VARCHAR(50) NOT NULL,
            系碼 CHAR(10) NOT NULL,
            FOREIGN KEY(系碼) REFERENCES department(系碼)
        )
    ''')

    conn.commit()
    conn.close()
    messagebox.showinfo("訊息", f"{DB_NAME} 中的 student 與 department 資料表已建立!")

# === 新增 department 資料 ===
def add_department():
    if not os.path.exists(DB_NAME):
        messagebox.showwarning("警告", "請先建立資料庫!")
        return

    dept_id = entry_dept_id.get().strip()
    dept_name = entry_dept_name.get().strip()
    dept_head = entry_dept_head.get().strip()

    if dept_id == "":
        messagebox.showwarning("警告", "系碼不可為空!")
        return

    conn = sqlite3.connect(DB_NAME)
    cursor = conn.cursor()
    try:
        cursor.execute("INSERT INTO department VALUES (?, ?, ?)", (dept_id, dept_name, dept_head))
        conn.commit()
        messagebox.showinfo("成功", "科系資料已新增!")
        show_all()
    except sqlite3.IntegrityError:
        messagebox.showerror("錯誤", "該系碼已存在!")
    conn.close()

# === 新增 student 資料 ===
def add_student():
    if not os.path.exists(DB_NAME):
        messagebox.showwarning("警告", "請先建立資料庫!")
        return

    sid = entry_sid.get().strip()
    sname = entry_sname.get().strip()
    dept_id = entry_sdept.get().strip()

    if sid == "" or sname == "" or dept_id == "":
        messagebox.showwarning("警告", "請完整輸入學生資料!")
        return

    conn = sqlite3.connect(DB_NAME)
    cursor = conn.cursor()

    # 檢查系碼是否存在於 department
    cursor.execute("SELECT 系碼 FROM department WHERE 系碼=?", (dept_id,))
    result = cursor.fetchone()
    if not result:
        messagebox.showerror("錯誤", f"系碼 {dept_id} 不存在於 department!")
        conn.close()
        return

    try:
        cursor.execute("INSERT INTO student VALUES (?, ?, ?)", (sid, sname, dept_id))
        conn.commit()
        messagebox.showinfo("成功", "學生資料已新增!")
        show_all()
    except sqlite3.IntegrityError:
        messagebox.showerror("錯誤", "該學號已存在!")
    conn.close()

# === 顯示 JOIN 結果 ===
def show_all():
    if not os.path.exists(DB_NAME):
        messagebox.showwarning("警告", "請先建立資料庫!")
        return

    for row in tree.get_children():
        tree.delete(row)

    conn = sqlite3.connect(DB_NAME)
    cursor = conn.cursor()

    # 使用 JOIN 顯示學生 + 系所資料
    cursor.execute('''
        SELECT s.學號, s.姓名, s.系碼, d.系名, d.系主任
        FROM student s
        LEFT JOIN department d ON s.系碼 = d.系碼
    ''')
    rows = cursor.fetchall()
    for row in rows:
        tree.insert("", "end", values=row)
    conn.close()

# === 刪除資料庫 ===
def delete_db():
    if os.path.exists(DB_NAME):
        os.remove(DB_NAME)
        for row in tree.get_children():
            tree.delete(row)
        messagebox.showinfo("成功", f"{DB_NAME} 已刪除!")
    else:
        messagebox.showwarning("警告", "資料庫不存在!")

# === Tkinter 介面 ===
root = tk.Tk()
root.title("MySchoolDB 關聯式資料庫系統")
root.geometry("900x600")

# === Department 區 ===
frame_dept = tk.LabelFrame(root, text="科系資料 (department)")
frame_dept.pack(pady=5, fill="x", padx=10)

tk.Label(frame_dept, text="系碼:").grid(row=0, column=0, padx=5, pady=3)
entry_dept_id = tk.Entry(frame_dept, width=10)
entry_dept_id.grid(row=0, column=1)

tk.Label(frame_dept, text="系名:").grid(row=0, column=2, padx=5, pady=3)
entry_dept_name = tk.Entry(frame_dept, width=20)
entry_dept_name.grid(row=0, column=3)

tk.Label(frame_dept, text="系主任:").grid(row=0, column=4, padx=5, pady=3)
entry_dept_head = tk.Entry(frame_dept, width=20)
entry_dept_head.grid(row=0, column=5)

ttk.Button(frame_dept, text="新增科系", command=add_department).grid(row=0, column=6, padx=10)

# === Student 區 ===
frame_stu = tk.LabelFrame(root, text="學生資料 (student)")
frame_stu.pack(pady=5, fill="x", padx=10)

tk.Label(frame_stu, text="學號:").grid(row=0, column=0, padx=5, pady=3)
entry_sid = tk.Entry(frame_stu, width=10)
entry_sid.grid(row=0, column=1)

tk.Label(frame_stu, text="姓名:").grid(row=0, column=2, padx=5, pady=3)
entry_sname = tk.Entry(frame_stu, width=20)
entry_sname.grid(row=0, column=3)

tk.Label(frame_stu, text="系碼:").grid(row=0, column=4, padx=5, pady=3)
entry_sdept = tk.Entry(frame_stu, width=10)
entry_sdept.grid(row=0, column=5)

ttk.Button(frame_stu, text="新增學生", command=add_student).grid(row=0, column=6, padx=10)

# === 功能按鈕 ===
frame_btn = tk.Frame(root)
frame_btn.pack(pady=5)

ttk.Button(frame_btn, text="建立資料庫", command=create_db).grid(row=0, column=0, padx=5)
ttk.Button(frame_btn, text="顯示所有資料 (JOIN)", command=show_all).grid(row=0, column=1, padx=5)
ttk.Button(frame_btn, text="刪除資料庫", command=delete_db).grid(row=0, column=2, padx=5)

# === Treeview 顯示區 ===
columns = ("學號", "姓名", "系碼", "系名", "系主任")
tree = ttk.Treeview(root, columns=columns, show="headings", height=15)
for col in columns:
    tree.heading(col, text=col)
    tree.column(col, width=130)
tree.pack(pady=10, fill="x")

root.mainloop()

沒有留言:

張貼留言

ESP32 (ESP-IDF in VS Code) MFRC522 + MQTT + PYTHON TKinter +SQLite

 ESP32 (ESP-IDF in VS Code) MFRC522 + MQTT + PYTHON TKinter +SQLite  ESP32 VS Code 程式 ; PlatformIO Project Configuration File ; ;   Build op...