SQLite och DB Browser
SQLite är en kompakt, serverless, self-contained SQL-databasmotor som inte kräver konfiguration och lagrar hela databasen i en enda fil. DB Browser for SQLite är ett användarvänligt grafiskt verktyg som gör det enkelt att arbeta med SQLite-databaser. Denna kombination är perfekt för utveckling, prototyper, små applikationer och lärande.
Innehållsförteckning
- Introduktion
- Vad är SQLite
- Fördelar med SQLite
- När ska man använda SQLite
- Installation och setup
- DB Browser for SQLite
- Grundläggande användning
- SQLite-specifika funktioner
- C# och SQLite integration
- Praktiska exempel
- Begränsningar
- Best practices
- Felsökning
- Slutsats
- TL;DR
Introduktion
SQLite är världens mest använda databasmotor, inbyggd i nästan alla smartphones, webbläsare och operativsystem. Till skillnad från traditionella databaser som MySQL eller SQL Server kräver SQLite ingen server - hela databasen finns i en enda fil som kan kopieras, flyttas och delas enkelt. DB Browser for SQLite kompletterar detta med ett intuitivt grafiskt gränssnitt som gör databashantering tillgänglig för alla utvecklare.
Vad är SQLite
Serverless arkitektur
SQLite är en embedded databasmotor som inte kräver en separat server:
Traditionell databas: SQLite:
[App] ↔ [Server] ↔ [Files] [App] ↔ [database.db]
Self-contained
Hela SQLite-biblioteket är mindre än 1MB och behöver inga externa dependencies:
// Inget behov av databas-server installation
// Ingen konfiguration av användare eller rättigheter
// Bara en fil: "mydatabase.db"
Zero-configuration
Ingen setup eller administration krävs:
// Skapa en databas - om filen inte finns skapas den automatiskt
string connectionString = "Data Source=myapp.db";
using var connection = new SQLiteConnection(connectionString);
connection.Open();
// Klar att använda!
Fördelar med SQLite
1. Enkelhet
// Hela databashanteringen i en fil
string dbPath = "C:\\MyApp\\data.db";
// Kopiera databasen = kopiera filen
File.Copy(dbPath, "backup.db");
// Flytta databasen = flytta filen
File.Move(dbPath, "D:\\NewLocation\\data.db");
2. Prestanda
SQLite är extremt snabb för de flesta användningsfall:
-- Enkla SELECT-operationer kan vara snabbare än stora databaser
-- eftersom det inte finns nätverkskommunikation
SELECT * FROM Users WHERE Id = 123; -- Mikrosekunder, inte millisekunder
3. Portabilitet
// Samma databas fungerar på alla plattformar
// Windows: C:\App\data.db
// Linux: /home/user/app/data.db
// macOS: /Users/user/app/data.db
// Identisk funktionalitet överallt
4. Tillförlitlighet
SQLite är extremt vältestad och stabil:
- Miljontals timmar av testning
- ACID-compliant transactions
- Används i kritiska system (flygplan, bilar, medicinsk utrustning)
- 100% branch test coverage
5. Kostnadsfri
- Public domain (inte ens copyleft)
- Inga licensavgifter
- Inga begränsningar på distribution
- Gratis support via community
När ska man använda SQLite
Perfekt för:
1. Utveckling och prototyper
// Snabb setup för nya projekt
// Ingen databas-server att konfigurera
// Lätt att dela mellan utvecklare
2. Desktop applikationer
// Settings, användardata, cache
// Offline-först applikationer
// Lokala databaser för sync
3. Mobila applikationer
// iOS och Android inbyggt stöd
// Perfekt för offline-data
// Minimal minnesförbrukning
4. Små till medelstora webbsajter
// Upp till ~100,000 page views/dag
// Enklare deployment (bara filer)
// Mindre resurskrav på server
5. IoT och embedded systems
// Minimal footprint
// Inga externa dependencies
// Fungerar på begränsad hårdvara
Inte lämplig för:
1. Stora webbapplikationer med många concurrent users
// SQLite låser hela databasen vid skrivning
// Begränsat till en skriv-operation åt gången
// Bättre med PostgreSQL/MySQL för >1000 samtidiga användare
2. Client-server arkitekturer
// Ingen nätverksåtkomst
// Kan inte delas mellan flera applikationer samtidigt
// Behöver traditional databas-server för detta
3. Mycket stora dataset
// Teoretisk gräns: 281 TB
// Praktisk gräns: några GB för bästa prestanda
// Större dataset → PostgreSQL/MySQL/SQL Server
Installation och setup
SQLite Command Line Tool
# Windows (ladda ner från sqlite.org)
# 1. Ladda ner sqlite-tools-win32-x86-*.zip
# 2. Extrahera sqlite3.exe
# 3. Lägg till i PATH (valfritt)
# Linux
sudo apt-get install sqlite3
# macOS
brew install sqlite
.NET/C# SQLite
<!-- Lägg till i .csproj -->
<PackageReference Include="System.Data.SQLite" Version="1.0.118" />
<!-- eller -->
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.0" />
// Exempel med System.Data.SQLite
using System.Data.SQLite;
string connectionString = "Data Source=myapp.db;Version=3;";
using var connection = new SQLiteConnection(connectionString);
connection.Open();
DB Browser for SQLite
Installation
Windows:
1. Gå till https://sqlitebrowser.org/
2. Ladda ner Windows installer
3. Kör installation
4. Starta "DB Browser for SQLite"
macOS:
brew install --cask db-browser-for-sqlite
Linux:
sudo apt-get install sqlitebrowser
# eller
sudo snap install sqlitebrowser
Huvudfunktioner
1. Database Structure
- Visa alla tabeller
- Se kolumner och datatyper
- Hantera indexes
- Visa constraints och triggers
2. Browse Data
- Navigera through tabell-data
- Sortera och filtrera
- Editera data direkt
- Pagination för stora dataset
3. Execute SQL
- Skriv och kör SQL-kommandon
- Syntax highlighting
- Resultat i tabellformat
- SQL-historik
4. DB Schema Visualization
- Visuell representation av tabellstrukturer
- ER-diagram generation
- Export av schema
Grundläggande arbetsflöde
Skapa ny databas
1. File → New Database
2. Välj location och namn: "myproject.db"
3. Klicka "Save"
Skapa tabell via GUI
1. Högerklicka i "Database Structure"
2. "Create Table"
3. Ange namn: "Users"
4. Lägg till fält:
- Id: INTEGER, PRIMARY KEY, AUTOINCREMENT
- Username: TEXT, NOT NULL
- Email: TEXT, UNIQUE
- CreatedDate: TEXT (för ISO datum)
5. "OK"
Import/Export data
File → Import → Table from CSV file
File → Export → Table as CSV file
File → Export → Database to SQL file
Grundläggande användning
Skapa databas och tabeller
-- Skapa databas (automatiskt när man sparar första gången)
-- Ingen explicit CREATE DATABASE kommando behövs
-- Skapa tabeller
CREATE TABLE Users (
UserId INTEGER PRIMARY KEY AUTOINCREMENT,
Username TEXT NOT NULL UNIQUE,
Email TEXT NOT NULL UNIQUE,
PasswordHash TEXT NOT NULL,
CreatedDate TEXT DEFAULT (datetime('now')),
IsActive INTEGER DEFAULT 1 -- SQLite boolean som integer
);
CREATE TABLE Posts (
PostId INTEGER PRIMARY KEY AUTOINCREMENT,
UserId INTEGER NOT NULL,
Title TEXT NOT NULL,
Content TEXT,
CreatedDate TEXT DEFAULT (datetime('now')),
FOREIGN KEY (UserId) REFERENCES Users(UserId)
);
CRUD-operationer i DB Browser
-- INSERT via SQL tab
INSERT INTO Users (Username, Email, PasswordHash) VALUES
('alice', 'alice@example.com', 'hash123'),
('bob', 'bob@example.com', 'hash456'),
('charlie', 'charlie@example.com', 'hash789');
-- SELECT via Browse Data tab eller SQL
SELECT * FROM Users ORDER BY CreatedDate DESC;
-- UPDATE via Edit cell in Browse Data eller SQL
UPDATE Users SET Email = 'alice.doe@example.com' WHERE Username = 'alice';
-- DELETE via Delete row in Browse Data eller SQL
DELETE FROM Users WHERE IsActive = 0 AND CreatedDate < '2023-01-01';
Backup och restore
-- Backup (kopiera filen)
-- Eller via DB Browser: File → Export → Database to SQL file
-- Vacuum för att minska filstorlek
VACUUM;
-- Integrity check
PRAGMA integrity_check;
SQLite-specifika funktioner
Datatyper och affinity
SQLite använder “type affinity” istället för strikta typer:
CREATE TABLE TypeExamples (
-- INTEGER affinity
Id INTEGER,
Count INT,
-- TEXT affinity
Name TEXT,
Description VARCHAR(255),
-- REAL affinity
Price REAL,
Weight FLOAT,
-- NUMERIC affinity
Score NUMERIC,
-- BLOB affinity
Data BLOB
);
-- SQLite är flexibel med typer
INSERT INTO TypeExamples (Id, Name, Price) VALUES
(1, 'Product A', 29.99),
('2', 'Product B', '39.99'), -- Text '2' konverteras till integer
(3.14, 'Product C', 'free'); -- Mixed types OK
Datum och tid
-- SQLite har inga native datum-typer, använd TEXT/INTEGER/REAL
CREATE TABLE Events (
EventId INTEGER PRIMARY KEY,
EventName TEXT,
-- ISO 8601 format (rekommenderat)
EventDate TEXT DEFAULT (date('now')),
EventDateTime TEXT DEFAULT (datetime('now')),
EventTimestamp TEXT DEFAULT (datetime('now', 'localtime')),
-- Unix timestamp
UnixTime INTEGER DEFAULT (strftime('%s', 'now'))
);
-- Datum-funktioner
SELECT
date('now') as CurrentDate,
datetime('now') as CurrentDateTime,
datetime('now', '+7 days') as NextWeek,
datetime('now', '-1 month') as LastMonth,
strftime('%Y-%m-%d %H:%M', 'now') as Formatted;
JSON-stöd (SQLite 3.38+)
-- JSON som TEXT med JSON funktioner
CREATE TABLE JsonData (
Id INTEGER PRIMARY KEY,
UserProfile TEXT, -- JSON som text
Settings TEXT -- JSON som text
);
INSERT INTO JsonData (UserProfile, Settings) VALUES
('{"name": "Alice", "age": 30, "city": "Stockholm"}',
'{"theme": "dark", "notifications": true}');
-- JSON queries
SELECT
json_extract(UserProfile, '$.name') as Name,
json_extract(UserProfile, '$.age') as Age,
json_extract(Settings, '$.theme') as Theme
FROM JsonData;
Pragma statements
-- Database info
PRAGMA table_info(Users); -- Beskrivning av tabell
PRAGMA database_list; -- Lista databaser
PRAGMA compile_options; -- Compile options
-- Performance tuning
PRAGMA cache_size = 10000; -- Öka cache (default 2000)
PRAGMA temp_store = MEMORY; -- Använd RAM för temp tables
PRAGMA journal_mode = WAL; -- Write-Ahead Logging för bättre concurrency
-- Foreign keys (disabled by default!)
PRAGMA foreign_keys = ON; -- Aktivera foreign key constraints
-- Encoding
PRAGMA encoding = 'UTF-8'; -- Sätt encoding
C# och SQLite integration
Grundläggande connection
using System.Data.SQLite;
public class DatabaseService
{
private readonly string _connectionString;
public DatabaseService(string dbPath)
{
_connectionString = $"Data Source={dbPath};Version=3;";
InitializeDatabase();
}
private void InitializeDatabase()
{
using var connection = new SQLiteConnection(_connectionString);
connection.Open();
string createTable = @"
CREATE TABLE IF NOT EXISTS Users (
UserId INTEGER PRIMARY KEY AUTOINCREMENT,
Username TEXT NOT NULL UNIQUE,
Email TEXT NOT NULL UNIQUE,
CreatedDate TEXT DEFAULT (datetime('now'))
)";
using var command = new SQLiteCommand(createTable, connection);
command.ExecuteNonQuery();
}
}
CRUD operations i C#
public class UserRepository
{
private readonly string _connectionString;
public UserRepository(string connectionString)
{
_connectionString = connectionString;
}
// CREATE
public async Task<int> CreateUserAsync(string username, string email)
{
using var connection = new SQLiteConnection(_connectionString);
await connection.OpenAsync();
string sql = "INSERT INTO Users (Username, Email) VALUES (@username, @email)";
using var command = new SQLiteCommand(sql, connection);
command.Parameters.AddWithValue("@username", username);
command.Parameters.AddWithValue("@email", email);
await command.ExecuteNonQueryAsync();
// Få senaste ID
command.CommandText = "SELECT last_insert_rowid()";
var result = await command.ExecuteScalarAsync();
return Convert.ToInt32(result);
}
// READ
public async Task<User?> GetUserAsync(int userId)
{
using var connection = new SQLiteConnection(_connectionString);
await connection.OpenAsync();
string sql = "SELECT UserId, Username, Email, CreatedDate FROM Users WHERE UserId = @userId";
using var command = new SQLiteCommand(sql, connection);
command.Parameters.AddWithValue("@userId", userId);
using var reader = await command.ExecuteReaderAsync();
if (await reader.ReadAsync())
{
return new User
{
UserId = reader.GetInt32("UserId"),
Username = reader.GetString("Username"),
Email = reader.GetString("Email"),
CreatedDate = DateTime.Parse(reader.GetString("CreatedDate"))
};
}
return null;
}
// UPDATE
public async Task<bool> UpdateUserEmailAsync(int userId, string newEmail)
{
using var connection = new SQLiteConnection(_connectionString);
await connection.OpenAsync();
string sql = "UPDATE Users SET Email = @email WHERE UserId = @userId";
using var command = new SQLiteCommand(sql, connection);
command.Parameters.AddWithValue("@email", newEmail);
command.Parameters.AddWithValue("@userId", userId);
int rowsAffected = await command.ExecuteNonQueryAsync();
return rowsAffected > 0;
}
// DELETE
public async Task<bool> DeleteUserAsync(int userId)
{
using var connection = new SQLiteConnection(_connectionString);
await connection.OpenAsync();
string sql = "DELETE FROM Users WHERE UserId = @userId";
using var command = new SQLiteCommand(sql, connection);
command.Parameters.AddWithValue("@userId", userId);
int rowsAffected = await command.ExecuteNonQueryAsync();
return rowsAffected > 0;
}
}
public class User
{
public int UserId { get; set; }
public string Username { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public DateTime CreatedDate { get; set; }
}
Connection management
public class SQLiteConnectionManager : IDisposable
{
private readonly SQLiteConnection _connection;
private bool _disposed = false;
public SQLiteConnectionManager(string dbPath)
{
string connectionString = $"Data Source={dbPath};Version=3;Pooling=true;Max Pool Size=100;";
_connection = new SQLiteConnection(connectionString);
_connection.Open();
// Aktivera foreign keys
using var cmd = new SQLiteCommand("PRAGMA foreign_keys = ON", _connection);
cmd.ExecuteNonQuery();
}
public SQLiteConnection Connection => _connection;
public void Dispose()
{
if (!_disposed)
{
_connection?.Dispose();
_disposed = true;
}
}
}
Praktiska exempel
Komplett To-Do applikation
// Models
public class TodoItem
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
public string? Description { get; set; }
public bool IsCompleted { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime? CompletedDate { get; set; }
public int Priority { get; set; } = 1; // 1=Low, 2=Medium, 3=High
}
// Repository
public class TodoRepository
{
private readonly string _dbPath;
public TodoRepository(string dbPath)
{
_dbPath = dbPath;
InitializeDatabase();
}
private void InitializeDatabase()
{
using var connection = new SQLiteConnection($"Data Source={_dbPath}");
connection.Open();
string createTable = @"
CREATE TABLE IF NOT EXISTS TodoItems (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Title TEXT NOT NULL,
Description TEXT,
IsCompleted INTEGER DEFAULT 0,
CreatedDate TEXT DEFAULT (datetime('now')),
CompletedDate TEXT,
Priority INTEGER DEFAULT 1
)";
using var command = new SQLiteCommand(createTable, connection);
command.ExecuteNonQuery();
}
public async Task<List<TodoItem>> GetAllTodosAsync()
{
var todos = new List<TodoItem>();
using var connection = new SQLiteConnection($"Data Source={_dbPath}");
await connection.OpenAsync();
string sql = @"
SELECT Id, Title, Description, IsCompleted, CreatedDate, CompletedDate, Priority
FROM TodoItems
ORDER BY Priority DESC, CreatedDate DESC";
using var command = new SQLiteCommand(sql, connection);
using var reader = await command.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
todos.Add(new TodoItem
{
Id = reader.GetInt32("Id"),
Title = reader.GetString("Title"),
Description = reader.IsDBNull("Description") ? null : reader.GetString("Description"),
IsCompleted = reader.GetInt32("IsCompleted") == 1,
CreatedDate = DateTime.Parse(reader.GetString("CreatedDate")),
CompletedDate = reader.IsDBNull("CompletedDate") ? null : DateTime.Parse(reader.GetString("CompletedDate")),
Priority = reader.GetInt32("Priority")
});
}
return todos;
}
public async Task<int> AddTodoAsync(TodoItem todo)
{
using var connection = new SQLiteConnection($"Data Source={_dbPath}");
await connection.OpenAsync();
string sql = @"
INSERT INTO TodoItems (Title, Description, Priority)
VALUES (@title, @description, @priority)";
using var command = new SQLiteCommand(sql, connection);
command.Parameters.AddWithValue("@title", todo.Title);
command.Parameters.AddWithValue("@description", todo.Description);
command.Parameters.AddWithValue("@priority", todo.Priority);
await command.ExecuteNonQueryAsync();
command.CommandText = "SELECT last_insert_rowid()";
var result = await command.ExecuteScalarAsync();
return Convert.ToInt32(result);
}
public async Task<bool> ToggleCompleteAsync(int id)
{
using var connection = new SQLiteConnection($"Data Source={_dbPath}");
await connection.OpenAsync();
string sql = @"
UPDATE TodoItems
SET IsCompleted = CASE WHEN IsCompleted = 0 THEN 1 ELSE 0 END,
CompletedDate = CASE WHEN IsCompleted = 0 THEN datetime('now') ELSE NULL END
WHERE Id = @id";
using var command = new SQLiteCommand(sql, connection);
command.Parameters.AddWithValue("@id", id);
int rowsAffected = await command.ExecuteNonQueryAsync();
return rowsAffected > 0;
}
}
// Användning
class Program
{
static async Task Main(string[] args)
{
var todoRepo = new TodoRepository("todos.db");
// Lägg till todos
await todoRepo.AddTodoAsync(new TodoItem
{
Title = "Learn SQLite",
Description = "Complete the database course",
Priority = 3
});
await todoRepo.AddTodoAsync(new TodoItem
{
Title = "Practice C#",
Priority = 2
});
// Hämta och visa todos
var todos = await todoRepo.GetAllTodosAsync();
foreach (var todo in todos)
{
Console.WriteLine($"[{(todo.IsCompleted ? "X" : " ")}] {todo.Title} (Priority: {todo.Priority})");
if (!string.IsNullOrEmpty(todo.Description))
Console.WriteLine($" {todo.Description}");
}
}
}
Settings/Configuration manager
public class AppSettingsManager
{
private readonly string _dbPath;
public AppSettingsManager(string dbPath)
{
_dbPath = dbPath;
InitializeDatabase();
}
private void InitializeDatabase()
{
using var connection = new SQLiteConnection($"Data Source={_dbPath}");
connection.Open();
string createTable = @"
CREATE TABLE IF NOT EXISTS AppSettings (
Key TEXT PRIMARY KEY,
Value TEXT,
Type TEXT,
UpdatedDate TEXT DEFAULT (datetime('now'))
)";
using var command = new SQLiteCommand(createTable, connection);
command.ExecuteNonQuery();
}
public async Task<T?> GetSettingAsync<T>(string key, T? defaultValue = default)
{
using var connection = new SQLiteConnection($"Data Source={_dbPath}");
await connection.OpenAsync();
string sql = "SELECT Value, Type FROM AppSettings WHERE Key = @key";
using var command = new SQLiteCommand(sql, connection);
command.Parameters.AddWithValue("@key", key);
using var reader = await command.ExecuteReaderAsync();
if (await reader.ReadAsync())
{
string value = reader.GetString("Value");
string type = reader.GetString("Type");
return (T?)Convert.ChangeType(value, Type.GetType(type) ?? typeof(T));
}
return defaultValue;
}
public async Task SetSettingAsync<T>(string key, T value)
{
using var connection = new SQLiteConnection($"Data Source={_dbPath}");
await connection.OpenAsync();
string sql = @"
INSERT OR REPLACE INTO AppSettings (Key, Value, Type, UpdatedDate)
VALUES (@key, @value, @type, datetime('now'))";
using var command = new SQLiteCommand(sql, connection);
command.Parameters.AddWithValue("@key", key);
command.Parameters.AddWithValue("@value", value?.ToString());
command.Parameters.AddWithValue("@type", typeof(T).FullName);
await command.ExecuteNonQueryAsync();
}
}
// Användning
var settings = new AppSettingsManager("appsettings.db");
// Spara inställningar
await settings.SetSettingAsync("theme", "dark");
await settings.SetSettingAsync("fontSize", 14);
await settings.SetSettingAsync("autoSave", true);
// Hämta inställningar
string theme = await settings.GetSettingAsync("theme", "light");
int fontSize = await settings.GetSettingAsync("fontSize", 12);
bool autoSave = await settings.GetSettingAsync("autoSave", false);
Begränsningar
1. Concurrency limitations
// SQLite låser hela databasen vid skrivning
// Bara en writer åt gången (men många readers)
// Problemscenario:
Task.Run(() => {
// Writer 1 - låser databasen
using var conn1 = new SQLiteConnection(connectionString);
conn1.Open();
using var trans = conn1.BeginTransaction();
// Lång operation...
trans.Commit();
});
Task.Run(() => {
// Writer 2 - måste vänta på Writer 1
using var conn2 = new SQLiteConnection(connectionString);
conn2.Open();
// Får timeout eller väntar
});
2. Nätverksåtkomst
// FUNGERAR INTE - SQLite har inget nätverksstöd
// string connectionString = "Data Source=server.com\\database.db";
// FUNGERAR - lokal fil
string connectionString = "Data Source=C:\\App\\database.db";
3. Stored procedures och triggers
-- SQLite har begränsat stöd för stored procedures
-- Inga stored procedures som i SQL Server/MySQL
-- Men har triggers:
CREATE TRIGGER UpdateTimestamp
AFTER UPDATE ON Users
FOR EACH ROW
BEGIN
UPDATE Users SET LastModified = datetime('now') WHERE UserId = NEW.UserId;
END;
4. Datatyper
-- SQLite har bara 5 storage classes:
-- NULL, INTEGER, REAL, TEXT, BLOB
-- Detta fungerar men lagras som TEXT:
CREATE TABLE Test (
Date DATE, -- Lagras som TEXT
Number DECIMAL, -- Lagras som TEXT eller REAL
Flag BOOLEAN -- Lagras som INTEGER (0/1)
);
Best practices
1. Databas-design
-- Använd AUTOINCREMENT sparsamt (prestanda)
CREATE TABLE Users (
UserId INTEGER PRIMARY KEY, -- Bra: använder ROWID
Username TEXT NOT NULL
);
-- Inte:
CREATE TABLE Users (
UserId INTEGER PRIMARY KEY AUTOINCREMENT, -- Långsammare
Username TEXT NOT NULL
);
-- Aktivera foreign keys
PRAGMA foreign_keys = ON;
-- Använd NOT NULL där möjligt
CREATE TABLE Orders (
OrderId INTEGER PRIMARY KEY,
UserId INTEGER NOT NULL, -- Inte UserId INTEGER
OrderDate TEXT NOT NULL DEFAULT (datetime('now'))
);
2. Index-strategier
-- Index för WHERE-clauses
CREATE INDEX idx_users_email ON Users(Email);
CREATE INDEX idx_orders_user_date ON Orders(UserId, OrderDate);
-- Composite index för vanliga queries
CREATE INDEX idx_products_category_price ON Products(CategoryId, Price);
-- Kontrollera index-användning
EXPLAIN QUERY PLAN SELECT * FROM Users WHERE Email = 'test@example.com';
3. Connection handling
// Använd using statements
using var connection = new SQLiteConnection(connectionString);
connection.Open();
// Connection pooling
string connectionString = "Data Source=app.db;Pooling=true;Max Pool Size=100;";
// WAL mode för bättre concurrency
using var cmd = new SQLiteCommand("PRAGMA journal_mode=WAL", connection);
cmd.ExecuteNonQuery();
4. Backup-strategier
public class SQLiteBackupService
{
public static void BackupDatabase(string sourcePath, string backupPath)
{
// Enkel fil-kopia (kräver exclusive access)
File.Copy(sourcePath, backupPath, overwrite: true);
}
public static void OnlineBackup(string sourcePath, string backupPath)
{
using var source = new SQLiteConnection($"Data Source={sourcePath}");
using var backup = new SQLiteConnection($"Data Source={backupPath}");
source.Open();
backup.Open();
// SQLite backup API
source.BackupDatabase(backup, "main", "main", -1, null, 0);
}
public static void ScheduledBackup(string dbPath)
{
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
string backupPath = $"backup_{timestamp}.db";
// Vacuum först för att minska filstorlek
using var connection = new SQLiteConnection($"Data Source={dbPath}");
connection.Open();
using var cmd = new SQLiteCommand("VACUUM", connection);
cmd.ExecuteNonQuery();
BackupDatabase(dbPath, backupPath);
}
}
Felsökning
Vanliga fel och lösningar
// 1. "Database is locked"
// Lösning: Stäng alla connections korrekt
using var connection = new SQLiteConnection(connectionString);
// connection stängs automatiskt
// 2. "Foreign key constraint failed"
// Lösning: Aktivera foreign keys
using var cmd = new SQLiteCommand("PRAGMA foreign_keys = ON", connection);
cmd.ExecuteNonQuery();
// 3. "No such table"
// Lösning: Kontrollera att tabellen existerar
string checkTable = "SELECT name FROM sqlite_master WHERE type='table' AND name='Users'";
using var cmd = new SQLiteCommand(checkTable, connection);
var result = cmd.ExecuteScalar();
if (result == null)
{
// Skapa tabell
}
// 4. Performance problem
// Lösning: Använd EXPLAIN QUERY PLAN
string query = "EXPLAIN QUERY PLAN SELECT * FROM Users WHERE Email = ?";
using var cmd = new SQLiteCommand(query, connection);
cmd.Parameters.AddWithValue("", "test@example.com");
// Kolla om index används
Debug och monitoring
public class SQLiteDebugLogger
{
public static void EnableSQLLogging(SQLiteConnection connection)
{
// Log alla SQL commands (endast för development)
connection.StateChange += (sender, e) =>
{
Console.WriteLine($"Connection state: {e.CurrentState}");
};
}
public static void AnalyzePerformance(SQLiteConnection connection, string query)
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
using var cmd = new SQLiteCommand($"EXPLAIN QUERY PLAN {query}", connection);
using var reader = cmd.ExecuteReader();
Console.WriteLine("Query plan:");
while (reader.Read())
{
Console.WriteLine($" {reader["detail"]}");
}
stopwatch.Stop();
Console.WriteLine($"Analysis took: {stopwatch.ElapsedMilliseconds}ms");
}
}
Slutsats
SQLite och DB Browser for SQLite bildar en kraftfull kombination för databashantering utan komplexiteten av traditionella databas-servrar. SQLites serverless arkitektur, kombinerat med full SQL-support och ACID-egenskaper, gör det idealiskt för utveckling, små till medelstora applikationer, och situationer där enkelhet och portabilitet är viktigt. DB Browser tillhandahåller ett användarvänligt grafiskt gränssnitt som gör databashantering tillgänglig för alla utvecklarnivåer. Trots vissa begränsningar kring concurrency och nätverksåtkomst förblir SQLite ett excellent val för många applikationsscenarier.
TL;DR
SQLite = Serverless databas i en fil, inget setup behövs, perfekt för development/små appar. Fördelar: Enkelt, snabbt, portabelt, gratis, ACID-compliant. DB Browser = GUI-verktyg för SQLite, gratis på sqlitebrowser.org. C# usage: System.Data.SQLite NuGet, connection string "Data Source=file.db". Aktivera foreign keys: PRAGMA foreign_keys = ON. Begränsningar: En writer åt gången, ingen nätverksåtkomst, begränsade stored procedures. Best practices: Använd parametrar mot SQL injection, index för prestanda, using statements för connections, WAL mode för bättre concurrency. Backup = kopiera .db-filen. Perfekt för prototyper, desktop apps, mobila appar, små webbsajter.
SQLite och DB Browser for SQLite — snabbstart och tips
Varför SQLite? Lätt att komma igång, ingen server, lämplig för utveckling och testning.
Installation
- DB Browser for SQLite: ladda ner från https://sqlitebrowser.org/ (Windows/Mac/Linux)
- sqlite3 CLI finns ofta på systemet eller via paketmanager.
Snabba steg i DB Browser
- Öppna eller skapa en .db-fil.
- Skapa tabeller i “Database Structure” eller kör SQL i fliken “Execute SQL”.
- Inspektera tabeller och rader, exportera/importera CSV.
Viktiga kommandon (sqlite3)
# Öppna databas
sqlite3 mydb.db
# Lista tabeller
.tables
# Visa schema
.schema
# Kör SQL från fil
sqlite3 mydb.db < script.sql
Vanliga fallgropar
- Fil-låsning: SQLite tillåter endast en skrivare åt gången. Undvik långa transaktioner.
- Foreign keys: måste aktiveras per anslutning: PRAGMA foreign_keys = ON; (Använd i din connection-setup eller connection factory.)
- Datatyper: SQLite är dynamiskt typad — välj rätt constraints i schema och validera i applikationen.
Tips för utveckling
- Använd DB Browser för datautforskning, men kör migrations från kod (dotnet ef / SQL-skript) i CI.
- För integrationstester: kör SQLite i minnet (Data Source=:memory:) eller med temporär fil.
- Säkra backup: kopiera filen när inga anslutningar är öppna.
Övning
- Skapa en liten databas (Customers, Products, Orders), fyll med testdata och skapa vyer för rapporter.