Yayın Tarihi: 31 Ocak 2025
JSON’dan SQLite’e Geçiş Rehberi
JSON ya da başka bir yöntemden SQLite’e geçiş yapmak, yazılım uygulamanızın mevcut veri saklama yöntemini SQLite gibi daha güçlü ve esnek bir veritabanı çözümüne taşımanız anlamına gelir. Bu rehberde, modern SQLite özelliklerini de kapsayan detaylı bir geçiş süreci anlatılmaktadır.
1. Mevcut JSON Yapısını Anlama
İlk olarak, JSON dosyanızdaki veri yapısını incelemeniz gerekir. Örneğin:
{
"users": [
{"id": 1, "name": "Alice", "age": 25, "email": "alice@example.com"},
{"id": 2, "name": "Bob", "age": 30, "email": "bob@example.com"},
{"id": 3, "name": "Charlie", "age": 35, "email": "charlie@example.com"}
],
"books": [
{"id": 1, "title": "Book A", "author": "Author A", "year": 2020},
{"id": 2, "title": "Book B", "author": "Author B", "year": 2021}
]
}
Bu yapı, ilişkisel tablolar halinde saklanacaktır:
- Users tablosu: Kullanıcı bilgilerini temsil eder
- Books tablosu: Kitap bilgilerini temsil eder
2. Veri Doğrulama ve Şema Kontrolü
Veri taşıma işleminden önce, verilerin doğruluğunu kontrol etmek önemlidir:
import jsonschema
def validate_json_data(data):
schema = {
"type": "object",
"properties": {
"users": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {"type": "integer"},
"name": {"type": "string"},
"age": {"type": "integer"},
"email": {"type": "string", "format": "email"}
},
"required": ["id", "name", "email"]
}
},
"books": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {"type": "integer"},
"title": {"type": "string"},
"author": {"type": "string"},
"year": {"type": "integer"}
},
"required": ["id", "title", "author"]
}
}
}
}
try:
jsonschema.validate(instance=data, schema=schema)
return True, "Veri şeması geçerli"
except jsonschema.exceptions.ValidationError as e:
return False, f"Veri şeması hatası: {str(e)}"
3. Modern SQLite Veritabanı Hazırlığı
SQLite’in modern özelliklerini kullanan bir veritabanı oluşturma:
import sqlite3
import logging
from datetime import datetime
class DatabaseManager:
def __init__(self, db_file):
self.db_file = db_file
self.conn = None
self.setup_logging()
def setup_logging(self):
logging.basicConfig(
filename=f'db_migration_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
def connect(self):
try:
self.conn = sqlite3.connect(self.db_file)
# JSON1 uzantısını etkinleştir
self.conn.enable_load_extension(True)
self.conn.load_extension("json1")
return True
except sqlite3.Error as e:
logging.error(f"Veritabanı bağlantı hatası: {e}")
return False
def create_tables(self):
try:
cursor = self.conn.cursor()
# Users tablosu
cursor.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
age INTEGER,
email TEXT UNIQUE NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
metadata JSON,
GENERATED COLUMN email_domain AS (
json_extract(metadata, '$.email_info.domain')
)
)
""")
# Books tablosu
cursor.execute("""
CREATE TABLE IF NOT EXISTS books (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
author TEXT NOT NULL,
year INTEGER,
metadata JSON,
FOREIGN KEY (author) REFERENCES authors(name)
)
""")
# FTS5 tablosu kitap araması için
cursor.execute("""
CREATE VIRTUAL TABLE IF NOT EXISTS books_fts USING fts5(
title,
author,
content='books',
content_rowid='id'
)
""")
# İndeksler
cursor.execute("""
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
CREATE INDEX IF NOT EXISTS idx_books_author ON books(author);
""")
self.conn.commit()
logging.info("Tablolar başarıyla oluşturuldu")
return True
except sqlite3.Error as e:
logging.error(f"Tablo oluşturma hatası: {e}")
self.conn.rollback()
return False
4. Toplu Veri Aktarımı
Verileri batch halinde aktarmak için optimize edilmiş bir yöntem:
from tqdm import tqdm
def bulk_insert_data(db_manager, json_data, batch_size=1000):
try:
cursor = db_manager.conn.cursor()
# Users verilerini aktar
users = json_data.get('users', [])
for i in tqdm(range(0, len(users), batch_size), desc="Users aktarılıyor"):
batch = users[i:i + batch_size]
cursor.executemany(
"""
INSERT INTO users (id, name, age, email, metadata)
VALUES (?, ?, ?, ?, json(?))
""",
[(user['id'], user['name'], user['age'], user['email'],
json.dumps({'email_info': {'domain': user['email'].split('@')[1]}}))
for user in batch]
)
# Books verilerini aktar
books = json_data.get('books', [])
for i in tqdm(range(0, len(books), batch_size), desc="Books aktarılıyor"):
batch = books[i:i + batch_size]
cursor.executemany(
"""
INSERT INTO books (id, title, author, year, metadata)
VALUES (?, ?, ?, ?, json(?))
""",
[(book['id'], book['title'], book['author'], book['year'],
json.dumps({'added_date': datetime.now().isoformat()}))
for book in batch]
)
# FTS tablosunu güncelle
cursor.executemany(
"""
INSERT INTO books_fts (rowid, title, author)
VALUES (?, ?, ?)
""",
[(book['id'], book['title'], book['author']) for book in batch]
)
db_manager.conn.commit()
logging.info("Veri aktarımı başarıyla tamamlandı")
return True
except sqlite3.Error as e:
logging.error(f"Veri aktarım hatası: {e}")
db_manager.conn.rollback()
return False
5. Veri Doğrulama ve Test
Aktarılan verilerin doğruluğunu kontrol etmek için test fonksiyonları:
def verify_migration(db_manager, original_data):
try:
cursor = db_manager.conn.cursor()
# Users verilerini kontrol et
cursor.execute("SELECT COUNT(*) FROM users")
users_count = cursor.fetchone()[0]
if users_count != len(original_data['users']):
logging.error(f"Users veri sayısı uyuşmazlığı: {users_count} != {len(original_data['users'])}")
return False
# Books verilerini kontrol et
cursor.execute("SELECT COUNT(*) FROM books")
books_count = cursor.fetchone()[0]
if books_count != len(original_data['books']):
logging.error(f"Books veri sayısı uyuşmazlığı: {books_count} != {len(original_data['books'])}")
return False
# FTS araması test et
cursor.execute("SELECT COUNT(*) FROM books_fts")
fts_count = cursor.fetchone()[0]
if fts_count != books_count:
logging.error("FTS indeks sayısı uyuşmazlığı")
return False
logging.info("Veri doğrulama başarılı")
return True
except sqlite3.Error as e:
logging.error(f"Doğrulama hatası: {e}")
return False
6. Örnek Kullanım
Tüm bu işlemleri bir araya getiren ana fonksiyon:
def migrate_json_to_sqlite(json_file_path, db_file_path):
# JSON verisini oku
with open(json_file_path, 'r') as f:
json_data = json.load(f)
# Veri şemasını doğrula
is_valid, message = validate_json_data(json_data)
if not is_valid:
logging.error(message)
return False
# Veritabanı yöneticisini başlat
db_manager = DatabaseManager(db_file_path)
if not db_manager.connect():
return False
# Tabloları oluştur
if not db_manager.create_tables():
return False
# Verileri aktar
if not bulk_insert_data(db_manager, json_data):
return False
# Doğrulama yap
if not verify_migration(db_manager, json_data):
return False
logging.info("Migrasyon başarıyla tamamlandı")
return True
7. Modern SQLite Özellikleri
FTS5 ile Metin Araması
-- Kitap başlıklarında arama
SELECT * FROM books_fts WHERE title MATCH 'python';
-- Yazar isimlerinde bulanık arama
SELECT * FROM books_fts WHERE author MATCH 'rob*';
JSON Fonksiyonları
-- JSON verilerinden değer çıkarma
SELECT json_extract(metadata, '$.email_info.domain') as domain
FROM users;
-- JSON güncelleme
UPDATE users
SET metadata = json_set(metadata, '$.last_login', CURRENT_TIMESTAMP)
WHERE id = 1;
Window Functions
-- Her yazarın en popüler kitabını bulma
SELECT title, author,
ROW_NUMBER() OVER (PARTITION BY author ORDER BY year DESC) as latest_book
FROM books;
8. Performans İpuçları
- İndeks Kullanımı:
- Sık sorgulanan sütunlar için indeks oluşturun
- Compound indeksleri düşünün
- Transaction Yönetimi:
- Büyük veri aktarımlarında transaction kullanın
- Batch size’ı optimize edin
- Memory Kullanımı:
- PRAGMA cache_size ve page_size değerlerini ayarlayın
- temp_store pragmasını memory olarak ayarlayın
- Concurrency:
- WAL (Write-Ahead Logging) modunu kullanın
- Busy timeout değerini ayarlayın
9. Güvenlik Önlemleri
- Yetkilendirme:
- Dosya izinlerini doğru ayarlayın
- Veritabanı dosyasını güvenli bir konumda saklayın
- Veri Güvenliği:
- Hassas verileri şifreleyin
- Düzenli yedekleme yapın
- SQL Injection Koruması:
- Parametrize sorgular kullanın
- User input’u doğrulayın
Sonuç
Bu modern SQLite geçiş rehberi, veri taşıma işlemini güvenli, verimli ve ölçeklenebilir bir şekilde yapmanızı sağlar. SQLite’in modern özelliklerini kullanarak, uygulamanızın veri katmanını güçlendirebilir ve performansını artırabilirsiniz.
Özellikle dikkat edilmesi gereken noktalar:
- Veri doğrulama
- Batch processing
- Hata yönetimi
- Performans optimizasyonu
- Güvenlik önlemleri
Her projenin gereksinimleri farklı olabileceğinden, bu rehberi kendi ihtiyaçlarınıza göre uyarlayabilirsiniz.