07d757e9af
001_init.sql opened with USE `nachklang-feedback`, contradicting its own documented apply instructions (mysql ... <FEEDBACK_DB> < 001_init.sql, which already selects the database via the command line). Following the file's own usage note literally would fail unless a database happened to be named exactly nachklang-feedback rather than whatever FEEDBACK_DB is configured to in .env. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
145 lines
7.9 KiB
SQL
145 lines
7.9 KiB
SQL
-- Nachklang e.V. Feedback module — initial schema for FEEDBACK_DB
|
|
-- Apply manually against the FEEDBACK_DB database (separate from CALENDAR_DB).
|
|
-- See nachklang-feedback/IMPLEMENTATION_PLAN.md §2 for the full rationale
|
|
-- behind every design decision below (snapshot columns, denormalisation,
|
|
-- absence-over-sentinels, hashed IPs only).
|
|
--
|
|
-- Apply with e.g.:
|
|
-- mysql -h <DB_HOST> -u <DB_USER> -p <FEEDBACK_DB> < 001_init.sql
|
|
--
|
|
-- Deliberately no USE statement here: the target database is selected via
|
|
-- the mysql command line above (whatever FEEDBACK_DB is actually named in
|
|
-- .env), not hardcoded to a literal schema name.
|
|
|
|
-- 1. events -------------------------------------------------------------
|
|
CREATE TABLE events (
|
|
event_id INT AUTO_INCREMENT PRIMARY KEY,
|
|
slug VARCHAR(80) NOT NULL,
|
|
name VARCHAR(255) NOT NULL,
|
|
subtitle VARCHAR(255) NULL,
|
|
event_date DATE NOT NULL,
|
|
feedback_deadline DATETIME NOT NULL,
|
|
is_published TINYINT(1) NOT NULL DEFAULT 0,
|
|
intro_text TEXT NULL,
|
|
created_by_email VARCHAR(255) NULL,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
UNIQUE KEY uq_events_slug (slug),
|
|
KEY idx_events_eligibility (is_published, event_date, feedback_deadline)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- 2. songs (per-event setlist) ------------------------------------------
|
|
CREATE TABLE songs (
|
|
song_id INT AUTO_INCREMENT PRIMARY KEY,
|
|
event_id INT NOT NULL,
|
|
title VARCHAR(255) NOT NULL,
|
|
composer VARCHAR(255) NULL,
|
|
position INT NOT NULL DEFAULT 0,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
CONSTRAINT fk_songs_event FOREIGN KEY (event_id) REFERENCES events(event_id) ON DELETE CASCADE,
|
|
KEY idx_songs_event_position (event_id, position)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- 3. questions (global reusable library) ---------------------------------
|
|
CREATE TABLE questions (
|
|
question_id INT AUTO_INCREMENT PRIMARY KEY,
|
|
label VARCHAR(500) NOT NULL,
|
|
help_text VARCHAR(500) NULL,
|
|
question_type ENUM('SONG_PICK','SONG_RATING','FREE_TEXT') NOT NULL,
|
|
is_archived TINYINT(1) NOT NULL DEFAULT 0,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
KEY idx_questions_archived_type (is_archived, question_type)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- 4. event_questions (join + ordering) -----------------------------------
|
|
CREATE TABLE event_questions (
|
|
event_question_id INT AUTO_INCREMENT PRIMARY KEY,
|
|
event_id INT NOT NULL,
|
|
question_id INT NOT NULL,
|
|
position INT NOT NULL DEFAULT 0,
|
|
is_active TINYINT(1) NOT NULL DEFAULT 1,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT fk_eq_event FOREIGN KEY (event_id) REFERENCES events(event_id) ON DELETE CASCADE,
|
|
CONSTRAINT fk_eq_question FOREIGN KEY (question_id) REFERENCES questions(question_id) ON DELETE RESTRICT,
|
|
UNIQUE KEY uq_eq_event_question (event_id, question_id),
|
|
KEY idx_eq_event_position (event_id, position, is_active)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- 5. submissions ----------------------------------------------------------
|
|
CREATE TABLE submissions (
|
|
submission_id INT AUTO_INCREMENT PRIMARY KEY,
|
|
event_id INT NOT NULL,
|
|
submitted_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
ip_hash CHAR(64) NULL,
|
|
has_guestbook TINYINT(1) NOT NULL DEFAULT 0,
|
|
has_newsletter TINYINT(1) NOT NULL DEFAULT 0,
|
|
CONSTRAINT fk_sub_event FOREIGN KEY (event_id) REFERENCES events(event_id) ON DELETE CASCADE,
|
|
KEY idx_sub_event_time (event_id, submitted_at),
|
|
KEY idx_sub_iphash_time (ip_hash, submitted_at)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- 6. submission_answers ----------------------------------------------------
|
|
-- Deliberate denormalisation: question label/type and song title are
|
|
-- snapshotted at submission time so later edits to the question library
|
|
-- never retroactively change what a past submission means.
|
|
CREATE TABLE submission_answers (
|
|
answer_id INT AUTO_INCREMENT PRIMARY KEY,
|
|
submission_id INT NOT NULL,
|
|
event_id INT NOT NULL,
|
|
question_id INT NULL,
|
|
event_question_id INT NULL,
|
|
question_label_snapshot VARCHAR(500) NOT NULL,
|
|
question_type ENUM('SONG_PICK','SONG_RATING','FREE_TEXT') NOT NULL,
|
|
position_snapshot INT NOT NULL DEFAULT 0,
|
|
song_id INT NULL,
|
|
song_title_snapshot VARCHAR(255) NULL,
|
|
rating TINYINT NULL,
|
|
text_answer TEXT NULL,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT fk_ans_submission FOREIGN KEY (submission_id) REFERENCES submissions(submission_id) ON DELETE CASCADE,
|
|
CONSTRAINT fk_ans_event FOREIGN KEY (event_id) REFERENCES events(event_id) ON DELETE CASCADE,
|
|
CONSTRAINT fk_ans_question FOREIGN KEY (question_id) REFERENCES questions(question_id) ON DELETE SET NULL,
|
|
CONSTRAINT fk_ans_song FOREIGN KEY (song_id) REFERENCES songs(song_id) ON DELETE SET NULL,
|
|
CONSTRAINT chk_ans_rating CHECK (rating IS NULL OR (rating BETWEEN 1 AND 5)),
|
|
KEY idx_ans_submission (submission_id),
|
|
KEY idx_ans_report (event_id, question_id, song_id),
|
|
KEY idx_ans_type (event_id, question_type)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- 7. guest_book_entries -----------------------------------------------------
|
|
-- Private, admin-only. No public wall in v1.
|
|
CREATE TABLE guest_book_entries (
|
|
entry_id INT AUTO_INCREMENT PRIMARY KEY,
|
|
submission_id INT NOT NULL,
|
|
event_id INT NOT NULL,
|
|
display_name VARCHAR(255) NULL,
|
|
message TEXT NULL,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
CONSTRAINT fk_gb_submission FOREIGN KEY (submission_id) REFERENCES submissions(submission_id) ON DELETE CASCADE,
|
|
CONSTRAINT fk_gb_event FOREIGN KEY (event_id) REFERENCES events(event_id) ON DELETE CASCADE,
|
|
KEY idx_gb_event_time (event_id, created_at)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
-- 8. newsletter_signups -----------------------------------------------------
|
|
CREATE TABLE newsletter_signups (
|
|
signup_id INT AUTO_INCREMENT PRIMARY KEY,
|
|
submission_id INT NOT NULL,
|
|
event_id INT NOT NULL,
|
|
first_name VARCHAR(120) NOT NULL,
|
|
last_name VARCHAR(120) NOT NULL,
|
|
email VARCHAR(255) NOT NULL,
|
|
consent_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
consent_text_version VARCHAR(40) NOT NULL,
|
|
sync_status ENUM('PENDING','SENT','FAILED','SKIPPED') NOT NULL DEFAULT 'PENDING',
|
|
sync_attempts INT NOT NULL DEFAULT 0,
|
|
synced_at DATETIME NULL,
|
|
external_id VARCHAR(120) NULL,
|
|
last_error TEXT NULL,
|
|
CONSTRAINT fk_nl_submission FOREIGN KEY (submission_id) REFERENCES submissions(submission_id) ON DELETE CASCADE,
|
|
CONSTRAINT fk_nl_event FOREIGN KEY (event_id) REFERENCES events(event_id) ON DELETE CASCADE,
|
|
KEY idx_nl_sync_status (sync_status),
|
|
KEY idx_nl_email (email)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|