در آموزش کار با فایل‌ها دیدیم چطور با open در فایل بنویسیم و بخوانیم. اما فایل متنی یا CSV تا یک جایی جواب می‌دهد: وقتی بخواهید «فقط نقل‌قول‌های فلان نویسنده را بده» یا «تکراری اضافه نشود»، باید کل فایل را در پایتون بگردید. دیتابیس همین کار را با یک خط SQL انجام می‌دهد. خبر خوب: sqlite3 بخشی از کتابخانهٔ استاندارد پایتون است — هیچ نصبی لازم نیست.

ساخت دیتابیس و جدول اول

import sqlite3

conn = sqlite3.connect("quotes.db")
cursor = conn.cursor()

cursor.execute("""
    CREATE TABLE IF NOT EXISTS quotes (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        text TEXT NOT NULL,
        author TEXT NOT NULL,
        UNIQUE(text, author)
    )
""")
conn.commit()

sqlite3.connect("quotes.db") اگر فایل وجود نداشته باشد، آن را می‌سازد. UNIQUE(text, author) تضمین می‌کند همان نقل‌قول از همان نویسنده دوبار درج نشود.

درج دادهٔ امن — و چرا هرگز نباید رشته‌ها را دستی بچسبانید

# غلط — هرگز این‌طور ننویسید:
cursor.execute(f"INSERT INTO quotes (text, author) VALUES ('{text}', '{author}')")

اگر text شامل یک آپاستروف یا کد SQL باشد، این روش کوئری شما را می‌شکند یا بدتر، امکان SQL Injection می‌دهد — دقیقاً همان خانوادهٔ خطایی که در آموزش ربات تلگرام دربارهٔ نگه‌داشتن امن توکن هشدار دادیم. راه درست همیشه پارامتر ? است:

cursor.execute(
    "INSERT OR IGNORE INTO quotes (text, author) VALUES (?, ?)",
    (text, author)
)
conn.commit()

پایتون خودش مقدار را escape می‌کند؛ کد شما هرگز مستقیم داخل SQL چسبانده نمی‌شود. INSERT OR IGNORE هم یعنی اگر رکورد تکراری بود (به‌خاطر همان UNIQUE)، به‌جای خطا، فقط رد شود.

مثال واقعی: انتقال quotes.csv به دیتابیس

همان quotes.csv که در آموزش وب‌اسکرپینگ ساختیم را کامل به SQLite منتقل می‌کنیم:

import csv
import sqlite3

conn = sqlite3.connect("quotes.db")
cursor = conn.cursor()

cursor.execute("""
    CREATE TABLE IF NOT EXISTS quotes (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        text TEXT NOT NULL,
        author TEXT NOT NULL,
        UNIQUE(text, author)
    )
""")

with open("quotes.csv", encoding="utf-8-sig") as f:
    reader = csv.DictReader(f)
    for row in reader:
        cursor.execute(
            "INSERT OR IGNORE INTO quotes (text, author) VALUES (?, ?)",
            (row["text"], row["author"])
        )

conn.commit()
print(f"{cursor.rowcount} ردیف در این اجرا درج شد")
conn.close()

خواندن داده

conn = sqlite3.connect("quotes.db")
cursor = conn.cursor()

cursor.execute("SELECT text, author FROM quotes")
for text, author in cursor.fetchall():
    print(f"{text}{author}")

fetchall() همهٔ ردیف‌ها را یک‌جا برمی‌گرداند؛ برای دیتابیس‌های خیلی بزرگ، fetchone() را داخل حلقه صدا بزنید تا حافظه پر نشود.

فیلتر و جستجو

# فقط نقل‌قول‌های یک نویسندهٔ خاص
cursor.execute("SELECT text FROM quotes WHERE author = ?", ("Albert Einstein",))

# جستجوی بخشی از متن
cursor.execute("SELECT text, author FROM quotes WHERE text LIKE ?", ("%life%",))

اینجا هم دقت کنید: مقدار جستجو همیشه به‌عنوان پارامتر ? می‌رود، نه داخل رشتهٔ SQL — همان قاعدهٔ امنیتی بالا، بدون استثنا.

بروزرسانی و حذف

cursor.execute("UPDATE quotes SET author = ? WHERE id = ?", ("نویسندهٔ جدید", 3))
cursor.execute("DELETE FROM quotes WHERE id = ?", (3,))
conn.commit()

دیدن دیتابیس بدون نوشتن کد

برای مرور سریع محتوای quotes.db بدون پایتون، DB Browser for SQLite را نصب کنید — رایگان و متن‌باز است، فایل .db را باز می‌کنید و جدول‌ها را مثل اکسل می‌بینید.

کی SQLite کافی است، کی باید مهاجرت کنید

  • برای اسکریپت‌های شخصی، ابزارهای دسکتاپ، یا پروژه‌هایی با یک کاربر همزمان، SQLite دقیقاً کافی است — سبک، بدون نصب سرور، یک فایل ساده
  • وقتی چند کاربر همزمان باید روی داده بنویسند (مثل یک وب‌اپ با ترافیک واقعی)، وقت مهاجرت به PostgreSQL یا MySQL است

تمرین

یک ستون tags به جدول quotes اضافه کنید (ALTER TABLE quotes ADD COLUMN tags TEXT) و اسکریپت انتقال را طوری تغییر دهید که تگ‌های هر نقل‌قول را هم از CSV بخواند و در همان ستون ذخیره کند.

بازگشت به آموزش‌ها