-- ============================================================ -- Gym Tracker — SQLite schema v2 -- Гибкие справочники + мультиклиентская схема -- ============================================================ PRAGMA foreign_keys = ON; PRAGMA journal_mode = WAL; -- ============================================================ -- 1. КЛИЕНТЫ (люди, чьи тренировки ведём) -- ============================================================ CREATE TABLE clients ( id INTEGER PRIMARY KEY AUTOINCREMENT, sync_id TEXT NOT NULL UNIQUE, -- UUID для синхронизации full_name TEXT NOT NULL, birth_date TEXT, -- ISO8601 (YYYY-MM-DD) goal TEXT DEFAULT '', -- цель тренировок notes TEXT DEFAULT '', is_active INTEGER NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) ); CREATE INDEX idx_clients_name ON clients(full_name); CREATE INDEX idx_clients_active ON clients(is_active); -- ============================================================ -- 2. СПРАВОЧНИК ТИПОВ ТРЕНИРОВОК -- ============================================================ CREATE TABLE workout_types ( id INTEGER PRIMARY KEY AUTOINCREMENT, sync_id TEXT NOT NULL UNIQUE, name TEXT NOT NULL UNIQUE, sort_order INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) ); -- ============================================================ -- 3. СПРАВОЧНИК УПРАЖНЕНИЙ -- ============================================================ CREATE TABLE exercises ( id INTEGER PRIMARY KEY AUTOINCREMENT, sync_id TEXT NOT NULL UNIQUE, workout_type_id INTEGER, -- FK на workout_types name TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), FOREIGN KEY (workout_type_id) REFERENCES workout_types(id) ON DELETE SET NULL ); CREATE INDEX idx_exercises_name ON exercises(name); CREATE INDEX idx_exercises_type ON exercises(workout_type_id); -- ============================================================ -- 4. ТРЕНИРОВКИ -- ============================================================ CREATE TABLE workouts ( id INTEGER PRIMARY KEY AUTOINCREMENT, sync_id TEXT NOT NULL UNIQUE, client_id INTEGER, -- FK на clients workout_type_id INTEGER, -- FK на workout_types start_time TEXT NOT NULL, -- ISO8601 end_time TEXT, -- NULL = активная notes TEXT DEFAULT '', synced INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), FOREIGN KEY (client_id) REFERENCES clients(id) ON DELETE SET NULL, FOREIGN KEY (workout_type_id) REFERENCES workout_types(id) ON DELETE SET NULL ); CREATE INDEX idx_workouts_client ON workouts(client_id); CREATE INDEX idx_workouts_start ON workouts(start_time); CREATE INDEX idx_workouts_synced ON workouts(synced); -- ============================================================ -- 5. УПРАЖНЕНИЯ ВНУТРИ ТРЕНИРОВКИ -- ============================================================ CREATE TABLE workout_exercises ( id INTEGER PRIMARY KEY AUTOINCREMENT, sync_id TEXT NOT NULL UNIQUE, workout_id INTEGER NOT NULL, exercise_id INTEGER, -- FK на exercises (может быть NULL) exercise_name TEXT NOT NULL, -- дублируем для истории sort_order INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), FOREIGN KEY (workout_id) REFERENCES workouts(id) ON DELETE CASCADE, FOREIGN KEY (exercise_id) REFERENCES exercises(id) ON DELETE SET NULL ); CREATE INDEX idx_wex_workout ON workout_exercises(workout_id); CREATE INDEX idx_wex_exercise ON workout_exercises(exercise_id); CREATE INDEX idx_wex_name ON workout_exercises(exercise_name); -- ============================================================ -- 6. ПОДХОДЫ -- ============================================================ CREATE TABLE sets ( id INTEGER PRIMARY KEY AUTOINCREMENT, sync_id TEXT NOT NULL UNIQUE, workout_exercise_id INTEGER NOT NULL, is_dropset INTEGER NOT NULL DEFAULT 0, weight REAL, reps INTEGER, rating INTEGER CHECK (rating BETWEEN 1 AND 5), notes TEXT DEFAULT '', sort_order INTEGER NOT NULL DEFAULT 0, timestamp TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), FOREIGN KEY (workout_exercise_id) REFERENCES workout_exercises(id) ON DELETE CASCADE, CHECK ( (is_dropset = 0 AND weight IS NOT NULL AND reps IS NOT NULL) OR (is_dropset = 1 AND weight IS NULL AND reps IS NULL) ) ); CREATE INDEX idx_sets_wex ON sets(workout_exercise_id); CREATE INDEX idx_sets_timestamp ON sets(timestamp); -- ============================================================ -- 7. ЭЛЕМЕНТЫ ДРОП-СЕТА -- ============================================================ CREATE TABLE dropset_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, set_id INTEGER NOT NULL, weight REAL NOT NULL, reps INTEGER NOT NULL, sort_order INTEGER NOT NULL DEFAULT 0, FOREIGN KEY (set_id) REFERENCES sets(id) ON DELETE CASCADE, CHECK (weight > 0 AND reps > 0) ); CREATE INDEX idx_dropset_set ON dropset_items(set_id); -- ============================================================ -- 8. КАРДИО -- ============================================================ CREATE TABLE cardio ( id INTEGER PRIMARY KEY AUTOINCREMENT, sync_id TEXT NOT NULL UNIQUE, workout_id INTEGER NOT NULL, cardio_type TEXT NOT NULL CHECK (cardio_type IN ( 'вело', 'ходьба', 'бег', 'эллипс', 'гребля' )), purpose TEXT NOT NULL CHECK (purpose IN ( 'warmup', 'cooldown', 'separate' )), duration INTEGER, distance REAL, speed REAL, incline REAL, resistance INTEGER, notes TEXT DEFAULT '', sort_order INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), FOREIGN KEY (workout_id) REFERENCES workouts(id) ON DELETE CASCADE ); CREATE INDEX idx_cardio_workout ON cardio(workout_id); CREATE INDEX idx_cardio_purpose ON cardio(purpose); -- ============================================================ -- 9. СПРАВОЧНИК ТИПОВ ЗАМЕРОВ ТЕЛА -- ============================================================ CREATE TABLE metric_types ( id INTEGER PRIMARY KEY AUTOINCREMENT, sync_id TEXT NOT NULL UNIQUE, name TEXT NOT NULL UNIQUE, -- "Вес", "Грудь", "Бицепс", "Предплечье"... unit TEXT NOT NULL DEFAULT 'см', -- единица измерения category TEXT DEFAULT 'body', -- для группировки: body, strength, etc. sort_order INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) ); -- ============================================================ -- 10. ЗАМЕРЫ ТЕЛА (факт замера) -- ============================================================ CREATE TABLE body_metrics ( id INTEGER PRIMARY KEY AUTOINCREMENT, sync_id TEXT NOT NULL UNIQUE, client_id INTEGER, -- FK на clients metric_date TEXT NOT NULL, -- ISO8601 notes TEXT DEFAULT '', created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), FOREIGN KEY (client_id) REFERENCES clients(id) ON DELETE SET NULL ); CREATE INDEX idx_body_metrics_client ON body_metrics(client_id); CREATE INDEX idx_body_metrics_date ON body_metrics(metric_date); -- ============================================================ -- 11. ЗНАЧЕНИЯ ЗАМЕРОВ (нормализованно) -- ============================================================ CREATE TABLE body_metric_values ( id INTEGER PRIMARY KEY AUTOINCREMENT, body_metric_id INTEGER NOT NULL, metric_type_id INTEGER NOT NULL, value REAL NOT NULL, FOREIGN KEY (body_metric_id) REFERENCES body_metrics(id) ON DELETE CASCADE, FOREIGN KEY (metric_type_id) REFERENCES metric_types(id) ON DELETE CASCADE, UNIQUE (body_metric_id, metric_type_id) -- один тип замера — одно значение в замере ); CREATE INDEX idx_bmv_metric ON body_metric_values(body_metric_id); CREATE INDEX idx_bmv_type ON body_metric_values(metric_type_id); -- ============================================================ -- 12. ЛОГ СИНХРОНИЗАЦИИ -- ============================================================ CREATE TABLE sync_log ( device_id TEXT PRIMARY KEY, last_sync TEXT NOT NULL, updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) );