Skip to content

📌 Ne Zaman Kullanılır?

  • ✅ Mobil uygulama, embedded sistem, test/prototip, tek kullanıcılı uygulama, Electron
  • ⚠️ Concurrent write sınırlı (WAL mode ile okuma iyileşir)
  • ❌ Çok kullanıcılı web uygulaması, yüksek yazma trafiği

Önerilen Kullanım: Mobil + test veritabanı + embedded + küçük proje Alternatifler: MySQL (web app), PostgreSQL (gelişmiş), LiteFS (dağıtık SQLite)

SQLite

Ne Zaman Kullanılır? (When to Use?)

  • Embedded uygulamalar: Mobil (iOS/Android), masaüstü uygulamalar
  • Geliştirme/test ortamı: Hızlı prototipleme, unit testler
  • Küçük-orta ölçekli web uygulamaları: Düşük trafik siteleri
  • Veri analizi: Tek kullanıcılı veri işleme, CSV import/export

Python ile Kullanım (Python Usage)

python
import sqlite3

# Veritabanina baglan (dosya yoksa olusturur)
# Connect to database (creates file if not exists)
conn = sqlite3.connect('myapp.db')
conn.row_factory = sqlite3.Row  # Dict-like rows
cursor = conn.cursor()

# Tablo olustur / Create table
cursor.execute('''
    CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        email TEXT NOT NULL UNIQUE,
        age INTEGER DEFAULT 0,
        created_at TEXT DEFAULT (datetime('now'))
    )
''')

# INSERT
cursor.execute(
    'INSERT INTO users (name, email, age) VALUES (?, ?, ?)',
    ('Ali Yilmaz', 'ali@test.com', 28)
)

# Toplu INSERT / Bulk insert
users_data = [
    ('Ayse Demir', 'ayse@test.com', 32),
    ('Mehmet Kaya', 'mehmet@test.com', 25),
]
cursor.executemany('INSERT INTO users (name, email, age) VALUES (?, ?, ?)', users_data)

# SELECT
cursor.execute('SELECT * FROM users WHERE age > ?', (25,))
rows = cursor.fetchall()
for row in rows:
    print(f"{row['name']} - {row['email']}")

# UPDATE
cursor.execute('UPDATE users SET age = ? WHERE email = ?', (30, 'ali@test.com'))

# DELETE
cursor.execute('DELETE FROM users WHERE id = ?', (3,))

# Kaydet ve kapat / Commit and close
conn.commit()
conn.close()

# Context manager kullanimi / Using context manager
with sqlite3.connect('myapp.db') as conn:
    conn.execute('SELECT * FROM users')

CLI Komutları (CLI Commands)

bash
# SQLite shell ac / Open SQLite shell
sqlite3 myapp.db

# Faydali komutlar / Useful commands
.tables              # Tablolari listele
.schema users        # Tablo semasini goster
.mode column         # Sutun gorunumu
.headers on          # Baslik satirlarini goster
.mode csv            # CSV cikti modu
.output result.csv   # Ciktiyi dosyaya yonlendir
.import data.csv users  # CSV'den icerik aktar
.dump                # Tum veritabanini SQL olarak cikart
.quit                # Cikis

CRUD

sql
-- SQLite ozellikleri / SQLite specifics
CREATE TABLE products (
    id INTEGER PRIMARY KEY AUTOINCREMENT,  -- AUTOINCREMENT (not AUTO_INCREMENT)
    name TEXT NOT NULL,
    price REAL NOT NULL,
    in_stock INTEGER DEFAULT 1,            -- Boolean as INTEGER (0/1)
    data TEXT,                              -- JSON stored as TEXT
    created_at TEXT DEFAULT (datetime('now','localtime'))
);

-- UPSERT
INSERT INTO products (name, price) VALUES ('Widget', 9.99)
ON CONFLICT(name) DO UPDATE SET price = excluded.price;

-- Tarih islemleri / Date operations
SELECT * FROM products WHERE created_at > datetime('now', '-7 days');
SELECT strftime('%Y-%m', created_at) AS month, COUNT(*) FROM orders GROUP BY month;

-- LIMIT ve OFFSET
SELECT * FROM products ORDER BY price DESC LIMIT 10 OFFSET 20;

Veri Tipleri ve Type Affinity (Data Types & Type Affinity)

SQLite dinamik tip sistemine sahiptir. Deklare edilen tip bir "öneri" niteliğindedir; veri herhangi bir sütuna yazılabilir.

5 Storage Class

Storage ClassAçıklama
NULLBoş değer
INTEGER1, 2, 4, 6 veya 8 byte tam sayı
REAL8 byte IEEE floating point
TEXTUTF-8/UTF-16 metin
BLOBHam binary veri

Type Affinity Kuralları (Type Affinity Rules)

SQLite, deklare edilen tipe göre bir affinity atar. Bu affinity, gelen verinin hangi storage class'a dönüştürüleceğini belirler:

Deklare Edilen Tip İçeriğiAffinityÖrnek
INTINTEGERINT, BIGINT, SMALLINT
CHAR, CLOB, TEXTTEXTVARCHAR(255), TEXT
BLOB veya tip belirtilmemişBLOBBLOB, tip yok
REAL, FLOA, DOUBREALREAL, DOUBLE, FLOAT
Diğer her şeyNUMERICBOOLEAN, DATE, DECIMAL
sql
-- Affinity ornegi: VARCHAR deklare edilse bile TEXT affinity uygulanir
CREATE TABLE demo (
    id INTEGER PRIMARY KEY,
    name VARCHAR(100),   -- TEXT affinity
    score REAL,          -- REAL affinity
    data BLOB            -- BLOB affinity
);

-- Tip kontrolu
SELECT typeof(name), typeof(score), typeof(data) FROM demo;

Strict Tables (SQLite 3.37+)

Strict modda SQLite, tip uyumsuzluklarında hata verir. Dinamik tiplemenin istenmediği durumlarda kullanılır:

sql
CREATE TABLE strict_users (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    age INTEGER NOT NULL,
    balance REAL DEFAULT 0.0
) STRICT;

-- Hata verir: TEXT sutununa INTEGER yazma denemesi
-- INSERT INTO strict_users (id, name, age) VALUES (1, 123, 25);
-- Error: cannot store INTEGER value in TEXT column strict_users.name

WAL Mode (Write-Ahead Logging)

sql
-- WAL modunu etkinlestir / Enable WAL mode (better concurrent reads)
PRAGMA journal_mode=WAL;

-- Diger faydali pragma'lar / Other useful pragmas
PRAGMA foreign_keys = ON;        -- FK dogrulamasini etkinlestir
PRAGMA cache_size = -20000;      -- 20MB cache
PRAGMA synchronous = NORMAL;     -- Performans/guvenlik dengesi
PRAGMA temp_store = MEMORY;      -- Gecici tablolar bellekte

Advanced Queries

CTE (Common Table Expressions)

sql
-- Basit CTE: Departman bazinda ortalama maas
WITH dept_avg AS (
    SELECT department, AVG(salary) AS avg_salary
    FROM employees
    GROUP BY department
)
SELECT e.name, e.salary, d.avg_salary
FROM employees e
JOIN dept_avg d ON e.department = d.department
WHERE e.salary > d.avg_salary;

Recursive CTE -- Ağaç Yapısı (Tree Structure)

sql
-- Kategori agaci (parent-child iliskisi)
CREATE TABLE categories (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    parent_id INTEGER REFERENCES categories(id)
);

-- Tum alt kategorileri recursive olarak getir
WITH RECURSIVE category_tree AS (
    -- Baslangic: kok dugum
    SELECT id, name, parent_id, 0 AS depth
    FROM categories
    WHERE parent_id IS NULL

    UNION ALL

    -- Recursive adim: cocuklari ekle
    SELECT c.id, c.name, c.parent_id, ct.depth + 1
    FROM categories c
    JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT id, printf('%.*c', depth * 2, ' ') || name AS tree_name, depth
FROM category_tree
ORDER BY id;

Window Functions

sql
-- ROW_NUMBER: Her departmanda maasa gore siralama
SELECT
    name,
    department,
    salary,
    ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rank_in_dept
FROM employees;

-- RANK ve DENSE_RANK farki
SELECT
    name,
    salary,
    RANK() OVER (ORDER BY salary DESC) AS rank,
    DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank
FROM employees;

-- LAG ve LEAD: Onceki/sonraki satir degeri
SELECT
    date,
    revenue,
    LAG(revenue, 1) OVER (ORDER BY date) AS prev_day_revenue,
    LEAD(revenue, 1) OVER (ORDER BY date) AS next_day_revenue,
    revenue - LAG(revenue, 1) OVER (ORDER BY date) AS daily_change
FROM daily_sales;

JSON Fonksiyonlari

sql
-- JSON veri saklama ve sorgulama
CREATE TABLE events (
    id INTEGER PRIMARY KEY,
    data TEXT  -- JSON olarak saklanir
);

INSERT INTO events (data) VALUES ('{"type":"click","x":100,"y":200,"tags":["ui","btn"]}');

-- json_extract: Belirli bir alan cek
SELECT json_extract(data, '$.type') AS event_type FROM events;

-- json_each: JSON array'i satir satir ac
SELECT e.id, j.value AS tag
FROM events e, json_each(json_extract(e.data, '$.tags')) j;

-- json_group_array: Sonuclari JSON array olarak topla
SELECT department, json_group_array(name) AS members
FROM employees
GROUP BY department;

-- json_set: JSON icerigini guncelle
UPDATE events
SET data = json_set(data, '$.x', 150, '$.processed', true)
WHERE id = 1;

Triggers ve Views (Triggers & Views)

Triggers

sql
-- AFTER INSERT: Yeni kayit eklendiginde log tut
CREATE TRIGGER log_user_insert
AFTER INSERT ON users
BEGIN
    INSERT INTO audit_log (table_name, action, record_id, created_at)
    VALUES ('users', 'INSERT', NEW.id, datetime('now'));
END;

-- BEFORE UPDATE: updated_at otomatik guncelle
CREATE TRIGGER update_timestamp
BEFORE UPDATE ON users
BEGIN
    UPDATE users SET updated_at = datetime('now') WHERE id = NEW.id;
END;

-- AFTER DELETE: Silinen kaydi arsivle
CREATE TRIGGER archive_deleted_user
AFTER DELETE ON users
BEGIN
    INSERT INTO deleted_users (original_id, name, email, deleted_at)
    VALUES (OLD.id, OLD.name, OLD.email, datetime('now'));
END;

Trigger kullanım senaryoları:

  • Audit log: Tüm değişikliklerin otomatik kaydını tutma
  • updated_at: Zaman damgasını her güncelleme öncesi otomatik ayarlama
  • Cascade işlemleri: İlişkili tablolardaki kayıtları otomatik güncelleme/silme
  • Veri doğrulama: INSERT/UPDATE öncesi iş kuralı kontrolü

Views

sql
-- Karmasik sorguyu view olarak tanimla
CREATE VIEW active_user_summary AS
SELECT
    u.id,
    u.name,
    u.email,
    COUNT(o.id) AS total_orders,
    COALESCE(SUM(o.total), 0) AS total_spent,
    MAX(o.created_at) AS last_order_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.is_active = 1
GROUP BY u.id;

-- View kullanimi (normal tablo gibi sorgulanir)
SELECT * FROM active_user_summary WHERE total_spent > 1000;

-- View silme
DROP VIEW IF EXISTS active_user_summary;

Transactions

Temel Kullanım (Basic Usage)

sql
-- Basarili transaction
BEGIN TRANSACTION;
    UPDATE accounts SET balance = balance - 100 WHERE id = 1;
    UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;

-- Hata durumunda geri alma
BEGIN TRANSACTION;
    UPDATE accounts SET balance = balance - 100 WHERE id = 1;
    -- Bir hata olusursa:
ROLLBACK;

Transaction Türleri (Transaction Types)

sql
-- DEFERRED (varsayilan): Ilk veri erisiminde kilit alinir
BEGIN DEFERRED TRANSACTION;

-- IMMEDIATE: Basladiginda yazma kilidi alinir, okumaya izin verir
BEGIN IMMEDIATE TRANSACTION;

-- EXCLUSIVE: Basladiginda tum erisimi kilitler
BEGIN EXCLUSIVE TRANSACTION;
TürOkumaYazmaNe Zaman Kullanılır
DEFERREDHemenGerektiğindeVarsayılan, çoğu durum
IMMEDIATEEvetHemen kilitliYazma garanti edilecekse
EXCLUSIVEKilitliKilitliTam izolasyon gerektiğinde

Nested Transactions (SAVEPOINT)

sql
BEGIN TRANSACTION;
    INSERT INTO orders (user_id, total) VALUES (1, 250);

    SAVEPOINT order_items;
        INSERT INTO order_items (order_id, product_id, qty) VALUES (1, 10, 2);
        INSERT INTO order_items (order_id, product_id, qty) VALUES (1, 20, 1);
        -- Bu adimda sorun olursa sadece order_items geri alinir
    RELEASE SAVEPOINT order_items;

    -- veya hata durumunda:
    -- ROLLBACK TO SAVEPOINT order_items;

COMMIT;

Performance

PRAGMA Optimizasyonları (PRAGMA Optimizations)

PRAGMADeğerAçıklama
journal_modeWALConcurrent okuma iyileştirmesi
cache_size-2000020 MB bellek cache (negatif = KB)
synchronousNORMALPerformans/güvenlik dengesi (WAL ile güvenli)
mmap_size268435456256 MB memory-mapped I/O
temp_storeMEMORYGeçici tabloları bellekte tut
page_size4096Sayfa boyutu (veritabanı oluşturulmadan önce ayarla)
auto_vacuumINCREMENTALOtomatik alan geri kazanımı
sql
-- Uygulama baslatildiginda calistirilacak pragma'lar
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA cache_size = -20000;
PRAGMA mmap_size = 268435456;
PRAGMA temp_store = MEMORY;
PRAGMA foreign_keys = ON;
PRAGMA busy_timeout = 5000;

İndeks Stratejisi (Index Strategy)

sql
-- Tek sutun index
CREATE INDEX idx_users_email ON users(email);

-- Composite index (soldan saga kullanilir)
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);

-- Partial index (sadece belirli kosulu saglayan satirlar)
CREATE INDEX idx_active_users ON users(email) WHERE is_active = 1;

-- Unique index
CREATE UNIQUE INDEX idx_users_username ON users(username);

-- Index kullanilip kullanilmadigini kontrol et
EXPLAIN QUERY PLAN SELECT * FROM users WHERE email = 'test@test.com';

EXPLAIN QUERY PLAN

sql
-- Sorgu planini incele
EXPLAIN QUERY PLAN
SELECT u.name, COUNT(o.id)
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.is_active = 1
GROUP BY u.id;

-- Cikti ornegi:
-- SEARCH TABLE users USING INDEX idx_active_users (is_active=?)
-- SEARCH TABLE orders USING INDEX idx_orders_user_id (user_id=?)

SCAN TABLE goruyorsaniz index eksik demektir. SEARCH TABLE ... USING INDEX ideal sonuctur.

Backup

sql
-- CLI ile backup
sqlite3 myapp.db ".backup backup.db"

-- SQL dump (metin formati, versiyon bagimsiz)
sqlite3 myapp.db ".dump" > backup.sql

-- Geri yukleme
sqlite3 restored.db < backup.sql
python
# Python ile online backup (veritabani acikken)
import sqlite3

source = sqlite3.connect('myapp.db')
backup = sqlite3.connect('backup.db')

source.backup(backup)

backup.close()
source.close()

VACUUM

sql
-- Veritabani dosyasini sikistir (fragmentation gider, boyut kucultme)
VACUUM;

-- Otomatik vacuum ayarla (yeni veritabani icin)
PRAGMA auto_vacuum = INCREMENTAL;

-- Incremental vacuum: Belirli sayfa kadar bosalt
PRAGMA incremental_vacuum(100);

VACUUM işleminin tüm veritabanını yeniden yazdığı için büyük veritabanlarında zaman alabilir. auto_vacuum = INCREMENTAL ile parçalı temizlik tercih edilebilir.

Kullanım Senaryoları (Use Cases)

Electron Uygulamaları

javascript
// better-sqlite3 -- senkron, hizli, native
const Database = require('better-sqlite3');
const db = new Database('app.db');

// Senkron API (Electron main process icin ideal)
const user = db.prepare('SELECT * FROM users WHERE id = ?').get(1);
const users = db.prepare('SELECT * FROM users').all();

// Transaction
const insertMany = db.transaction((items) => {
    const stmt = db.prepare('INSERT INTO items (name, value) VALUES (?, ?)');
    for (const item of items) stmt.run(item.name, item.value);
});
insertMany([{ name: 'a', value: 1 }, { name: 'b', value: 2 }]);
javascript
// sql.js -- pure JavaScript, WASM tabanli (native build gerektirmez)
const initSqlJs = require('sql.js');

const SQL = await initSqlJs();
const db = new SQL.Database();

db.run('CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)');
db.run('INSERT INTO test VALUES (?, ?)', [1, 'hello']);

const result = db.exec('SELECT * FROM test');
// result: [{ columns: ['id', 'name'], values: [[1, 'hello']] }]
Özellikbetter-sqlite3sql.js
PerformansÇok hızlı (native)Yavaş (WASM)
BuildNative addon gerekirPure JS, build gereksiz
APISenkronSenkron (in-memory)
Dosya I/ODoğrudanManuel load/save
KullanımElectron main processBrowser, worker, cross-platform

React Native / Flutter

javascript
// React Native: expo-sqlite
import * as SQLite from 'expo-sqlite';

const db = await SQLite.openDatabaseAsync('app.db');

await db.execAsync(`
    CREATE TABLE IF NOT EXISTS tasks (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        title TEXT NOT NULL,
        done INTEGER DEFAULT 0
    );
`);

const result = await db.getAllAsync('SELECT * FROM tasks WHERE done = ?', [0]);
dart
// Flutter: sqflite
import 'package:sqflite/sqflite.dart';

final db = await openDatabase('app.db', version: 1,
    onCreate: (Database db, int version) async {
        await db.execute('''
            CREATE TABLE tasks (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                title TEXT NOT NULL,
                done INTEGER DEFAULT 0
            )
        ''');
    });

List<Map> tasks = await db.query('tasks', where: 'done = ?', whereArgs: [0]);

Test Veritabanı Olarak (As Test Database)

python
# pytest: In-memory veritabani ile test
import sqlite3
import pytest

@pytest.fixture
def db():
    conn = sqlite3.connect(':memory:')
    conn.execute('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)')
    yield conn
    conn.close()

def test_insert_user(db):
    db.execute('INSERT INTO users (name) VALUES (?)', ('Test',))
    result = db.execute('SELECT name FROM users').fetchone()
    assert result[0] == 'Test'
python
# Django: Test icin SQLite kullan (hiz icin)
# settings/test.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': ':memory:',
    }
}
php
// Laravel: phpunit.xml icinde SQLite test config
// <env name="DB_CONNECTION" value="sqlite"/>
// <env name="DB_DATABASE" value=":memory:"/>

Güvenlik (Security)

Dosya İzinleri (File Permissions)

bash
# Veritabani dosyasina sadece sahip erisebilsin
chmod 600 myapp.db
chmod 600 myapp.db-wal
chmod 600 myapp.db-shm

# Dizin izinleri (SQLite gecici dosya olusturabilmeli)
chmod 700 /path/to/db/directory/

SQLCipher (Veritabanı Şifreleme / Database Encryption)

python
# pysqlcipher3 ile sifrelenmis veritabani
from pysqlcipher3 import dbapi2 as sqlite

conn = sqlite.connect('encrypted.db')
conn.execute("PRAGMA key='super-secret-password'")
conn.execute("PRAGMA cipher_compatibility = 4")

# Normal SQLite gibi kullan
conn.execute('CREATE TABLE secrets (id INTEGER PRIMARY KEY, data TEXT)')
conn.execute('INSERT INTO secrets (data) VALUES (?)', ('gizli bilgi',))
conn.commit()

SQL Injection Korunma (SQL Injection Prevention)

python
# YANLIS -- string birlestirme (SQL injection acigi)
# cursor.execute(f"SELECT * FROM users WHERE name = '{user_input}'")

# DOGRU -- parameterized query
cursor.execute("SELECT * FROM users WHERE name = ?", (user_input,))

# DOGRU -- named parameters
cursor.execute(
    "SELECT * FROM users WHERE name = :name AND age > :age",
    {"name": user_input, "age": min_age}
)

WAL Dosya Güvenliği (WAL File Security)

WAL modunda veritabanı yanında iki ek dosya oluşur:

  • myapp.db-wal -- Write-Ahead Log (commitlenmemiş veriler)
  • myapp.db-shm -- Shared Memory (index bilgisi)

Bu dosyalar veritabanının bütünlüğü için kritiktir. Backup alırken üç dosya birlikte kopyalanmalıdır. Dosya izinleri ana veritabanı dosyasıyla aynı olmalıdır.

Tips

Ne Zaman SQLite Kullan, Ne Zaman Kullanma

SenaryoSQLiteClient-Server DB
Mobil uygulamaEvetHayır
Embedded / IoTEvetHayır
Desktop uygulamasıEvetNadiren
Test/prototipEvetÖnemli değil
Tek kullanıcı webEvetTercih edilebilir
Çok kullanıcılı webHayırEvet
Yüksek yazma trafiğiHayırEvet
Birden fazla sunucuHayırEvet
100+ concurrent writeHayırEvet

WAL vs Journal Mode

ÖzellikJournal (DELETE)WAL
Concurrent okumaYazma sırasında engellenirYazma sırasında okunabilir
Yazma performansıDaha yavaşDaha hızlı
Dosya sayısı1 (+ journal geçici)3 (db + wal + shm)
Network dosya sistemiDesteklerDesteklemez (NFS vb.)
CheckpointOtomatikOtomatik + manuel
VarsayılanEvetHayır (açılması gerekir)

Önemli PRAGMA Ayarları (Important PRAGMA Settings)

sql
-- Her baglantida mutlaka calistirin
PRAGMA foreign_keys = ON;      -- FK kisitlamalarini etkinlestir (varsayilan OFF!)
PRAGMA busy_timeout = 5000;    -- Concurrent erisimde 5 sn bekle (varsayilan 0 = hemen hata)
PRAGMA journal_mode = WAL;     -- Daha iyi okuma performansi
PRAGMA synchronous = NORMAL;   -- WAL ile guvenli, daha hizli

-- Connection pooling gereksizdir
-- SQLite tek dosya uzerinde calisir, pool olusturmak yerine
-- her thread/request icin ayri baglanti acmak yeterlidir.
-- busy_timeout ayarlanmissa concurrent erisimde bekler.

Laravel / Django / Flask ile Kullanım (Framework Usage)

bash
# Laravel .env
DB_CONNECTION=sqlite
DB_DATABASE=/absolute/path/to/database.sqlite
python
# Django settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

# Flask
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///myapp.db'

Limitasyonlar (Limitations)

LimitationDescription
Concurrent writesTek yazıcı / Single writer at a time (WAL helps with reads)
ALTER TABLESınırlı destek / Limited (no DROP COLUMN before 3.35.0)
No native ENUMTEXT + CHECK constraint kullan
No RIGHT/FULL JOINLEFT JOIN ile workaround
No user managementDosya sistemi izinleri ile kontrol / File system permissions
Data typesDinamik tipleme / Dynamic typing (any type in any column)
Max sizePratikte ~1 TB, sonrası yavaşlar

Veritabanı (Database)

Diğer Kategoriler (Other Categories)

Developer Guides & Technical References