From 347188bd0c293b0fb36d73406fbf035ace900818 Mon Sep 17 00:00:00 2001 From: Clemens Creutzburg Date: Sat, 21 Mar 2026 15:27:47 +0100 Subject: [PATCH] Add schema check and migration scripts for impf workflow and warteliste --- admin/sql/2026-03-20_schema_check.sql | 156 ++++++++++++++++++++ admin/sql/2026-03-20_schema_migration.sql | 166 ++++++++++++++++++++++ 2 files changed, 322 insertions(+) create mode 100644 admin/sql/2026-03-20_schema_check.sql create mode 100644 admin/sql/2026-03-20_schema_migration.sql diff --git a/admin/sql/2026-03-20_schema_check.sql b/admin/sql/2026-03-20_schema_check.sql new file mode 100644 index 0000000..fac5035 --- /dev/null +++ b/admin/sql/2026-03-20_schema_check.sql @@ -0,0 +1,156 @@ +-- Prueft die fuer den aktuellen Impf-Workflow relevanten Tabellen, Spalten, +-- Indizes und den Legacy-Migrationsstatus. +-- Das Skript veraendert keine Daten. + +SELECT 'database' AS check_type, DATABASE() AS object_name, 'INFO' AS status; + +SELECT + 'table' AS check_type, + t.required_name AS object_name, + CASE + WHEN it.table_name IS NOT NULL THEN 'OK' + ELSE 'MISSING' + END AS status +FROM ( + SELECT 'impf_workflow_meta' AS required_name + UNION ALL SELECT 'impfstoff_workflow' + UNION ALL SELECT 'impfstoff_wochenplan' + UNION ALL SELECT 'impf_zeitraum' + UNION ALL SELECT 'impf_zeitraum_impfstoff' + UNION ALL SELECT 'warteliste' +) t +LEFT JOIN information_schema.tables it + ON it.table_schema = DATABASE() + AND it.table_name = t.required_name +ORDER BY t.required_name; + +SELECT + 'column' AS check_type, + CONCAT(c.table_name, '.', c.column_name) AS object_name, + CASE + WHEN ic.column_name IS NOT NULL THEN 'OK' + ELSE 'MISSING' + END AS status +FROM ( + SELECT 'impf_workflow_meta' AS table_name, 'meta_key' AS column_name + UNION ALL SELECT 'impf_workflow_meta', 'meta_value' + UNION ALL SELECT 'impf_workflow_meta', 'updated_at' + UNION ALL SELECT 'impfstoff_workflow', 'impfstoff_id' + UNION ALL SELECT 'impfstoff_workflow', 'dosen_pro_flasche' + UNION ALL SELECT 'impfstoff_wochenplan', 'plan_id' + UNION ALL SELECT 'impfstoff_wochenplan', 'impfstoff_id' + UNION ALL SELECT 'impfstoff_wochenplan', 'wochentag' + UNION ALL SELECT 'impfstoff_wochenplan', 'start' + UNION ALL SELECT 'impfstoff_wochenplan', 'ende' + UNION ALL SELECT 'impfstoff_wochenplan', 'impfortid' + UNION ALL SELECT 'impf_zeitraum', 'zeitraum_id' + UNION ALL SELECT 'impf_zeitraum', 'bezeichnung' + UNION ALL SELECT 'impf_zeitraum', 'wochentag' + UNION ALL SELECT 'impf_zeitraum', 'start' + UNION ALL SELECT 'impf_zeitraum', 'ende' + UNION ALL SELECT 'impf_zeitraum', 'impfortid' + UNION ALL SELECT 'impf_zeitraum_impfstoff', 'zeitraum_id' + UNION ALL SELECT 'impf_zeitraum_impfstoff', 'impfstoff_id' + UNION ALL SELECT 'warteliste', 'warteid' + UNION ALL SELECT 'warteliste', 'userid' + UNION ALL SELECT 'warteliste', 'impfenzeitraum' + UNION ALL SELECT 'warteliste', 'zeitraum_id' +) c +LEFT JOIN information_schema.columns ic + ON ic.table_schema = DATABASE() + AND ic.table_name = c.table_name + AND ic.column_name = c.column_name +ORDER BY c.table_name, c.column_name; + +SELECT + 'index' AS check_type, + CONCAT(i.table_name, '.', i.index_name) AS object_name, + CASE + WHEN isx.index_name IS NOT NULL THEN 'OK' + ELSE 'MISSING' + END AS status +FROM ( + SELECT 'warteliste' AS table_name, 'idx_warteliste_zeitraum' AS index_name + UNION ALL SELECT 'impfstoff_wochenplan', 'idx_impfstoff_wochenplan_impfstoff' + UNION ALL SELECT 'impfstoff_wochenplan', 'idx_impfstoff_wochenplan_wochentag' + UNION ALL SELECT 'impf_zeitraum', 'idx_impf_zeitraum_wochentag' + UNION ALL SELECT 'impf_zeitraum', 'idx_impf_zeitraum_impfort' + UNION ALL SELECT 'impf_zeitraum_impfstoff', 'idx_impf_zeitraum_impfstoff_impfstoff' +) i +LEFT JOIN information_schema.statistics isx + ON isx.table_schema = DATABASE() + AND isx.table_name = i.table_name + AND isx.index_name = i.index_name +GROUP BY i.table_name, i.index_name, isx.index_name +ORDER BY i.table_name, i.index_name; + +SELECT + 'meta' AS check_type, + 'impf_workflow_meta.legacy_wochenplan_migrated' AS object_name, + CASE + WHEN EXISTS ( + SELECT 1 + FROM information_schema.tables + WHERE table_schema = DATABASE() + AND table_name = 'impf_workflow_meta' + ) THEN COALESCE(( + SELECT CONCAT('VALUE=', meta_value) + FROM impf_workflow_meta + WHERE meta_key = 'legacy_wochenplan_migrated' + LIMIT 1 + ), 'MISSING') + ELSE 'TABLE_MISSING' + END AS status; + +SELECT + 'data' AS check_type, + 'impfstoff_wochenplan rows' AS object_name, + CASE + WHEN EXISTS ( + SELECT 1 + FROM information_schema.tables + WHERE table_schema = DATABASE() + AND table_name = 'impfstoff_wochenplan' + ) THEN CAST((SELECT COUNT(*) FROM impfstoff_wochenplan) AS CHAR) + ELSE 'TABLE_MISSING' + END AS status +UNION ALL +SELECT + 'data' AS check_type, + 'impf_zeitraum rows' AS object_name, + CASE + WHEN EXISTS ( + SELECT 1 + FROM information_schema.tables + WHERE table_schema = DATABASE() + AND table_name = 'impf_zeitraum' + ) THEN CAST((SELECT COUNT(*) FROM impf_zeitraum) AS CHAR) + ELSE 'TABLE_MISSING' + END AS status +UNION ALL +SELECT + 'data' AS check_type, + 'impf_zeitraum_impfstoff rows' AS object_name, + CASE + WHEN EXISTS ( + SELECT 1 + FROM information_schema.tables + WHERE table_schema = DATABASE() + AND table_name = 'impf_zeitraum_impfstoff' + ) THEN CAST((SELECT COUNT(*) FROM impf_zeitraum_impfstoff) AS CHAR) + ELSE 'TABLE_MISSING' + END AS status +UNION ALL +SELECT + 'data' AS check_type, + 'warteliste with zeitraum_id' AS object_name, + CASE + WHEN EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = DATABASE() + AND table_name = 'warteliste' + AND column_name = 'zeitraum_id' + ) THEN CAST((SELECT COUNT(*) FROM warteliste WHERE zeitraum_id IS NOT NULL) AS CHAR) + ELSE 'COLUMN_MISSING' + END AS status; diff --git a/admin/sql/2026-03-20_schema_migration.sql b/admin/sql/2026-03-20_schema_migration.sql new file mode 100644 index 0000000..2e1c519 --- /dev/null +++ b/admin/sql/2026-03-20_schema_migration.sql @@ -0,0 +1,166 @@ +-- Bringt den SQL-Dump auf den vom aktuellen Quellcode erwarteten Stand. +-- Die Migration ist idempotent und kann mehrfach ausgefuehrt werden. + +CREATE TABLE IF NOT EXISTS `impf_workflow_meta` ( + `meta_key` VARCHAR(100) NOT NULL, + `meta_value` VARCHAR(255) NOT NULL DEFAULT '', + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`meta_key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `impfstoff_workflow` ( + `impfstoff_id` INT NOT NULL, + `dosen_pro_flasche` INT NOT NULL, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`impfstoff_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `impfstoff_wochenplan` ( + `plan_id` INT NOT NULL AUTO_INCREMENT, + `impfstoff_id` INT NOT NULL, + `wochentag` TINYINT NOT NULL, + `start` TIME NOT NULL, + `ende` TIME NOT NULL, + `impfortid` INT NOT NULL, + `aktiv` TINYINT NOT NULL DEFAULT 1, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`plan_id`), + INDEX `idx_impfstoff_wochenplan_impfstoff` (`impfstoff_id`), + INDEX `idx_impfstoff_wochenplan_wochentag` (`wochentag`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `impf_zeitraum` ( + `zeitraum_id` INT NOT NULL AUTO_INCREMENT, + `bezeichnung` VARCHAR(120) NOT NULL DEFAULT '', + `wochentag` TINYINT NOT NULL, + `start` TIME NOT NULL, + `ende` TIME NOT NULL, + `impfortid` INT NOT NULL, + `aktiv` TINYINT NOT NULL DEFAULT 1, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`zeitraum_id`), + INDEX `idx_impf_zeitraum_wochentag` (`wochentag`), + INDEX `idx_impf_zeitraum_impfort` (`impfortid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `impf_zeitraum_impfstoff` ( + `zeitraum_id` INT NOT NULL, + `impfstoff_id` INT NOT NULL, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`zeitraum_id`, `impfstoff_id`), + INDEX `idx_impf_zeitraum_impfstoff_impfstoff` (`impfstoff_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +DROP PROCEDURE IF EXISTS `migrate_praxis_schema_20260320`; +DELIMITER $$ +CREATE PROCEDURE `migrate_praxis_schema_20260320`() +BEGIN + DECLARE v_impf_zeitraum_exists INT DEFAULT 0; + DECLARE v_bezeichnung_exists INT DEFAULT 0; + DECLARE v_warteliste_exists INT DEFAULT 0; + DECLARE v_zeitraum_id_exists INT DEFAULT 0; + DECLARE v_warteliste_index_exists INT DEFAULT 0; + DECLARE v_legacy_plan_exists INT DEFAULT 0; + + SELECT COUNT(*) + INTO v_impf_zeitraum_exists + FROM information_schema.tables + WHERE table_schema = DATABASE() + AND table_name = 'impf_zeitraum'; + + IF v_impf_zeitraum_exists > 0 THEN + SELECT COUNT(*) + INTO v_bezeichnung_exists + FROM information_schema.columns + WHERE table_schema = DATABASE() + AND table_name = 'impf_zeitraum' + AND column_name = 'bezeichnung'; + + IF v_bezeichnung_exists = 0 THEN + ALTER TABLE `impf_zeitraum` + ADD COLUMN `bezeichnung` VARCHAR(120) NOT NULL DEFAULT '' AFTER `zeitraum_id`; + END IF; + END IF; + + SELECT COUNT(*) + INTO v_warteliste_exists + FROM information_schema.tables + WHERE table_schema = DATABASE() + AND table_name = 'warteliste'; + + IF v_warteliste_exists > 0 THEN + SELECT COUNT(*) + INTO v_zeitraum_id_exists + FROM information_schema.columns + WHERE table_schema = DATABASE() + AND table_name = 'warteliste' + AND column_name = 'zeitraum_id'; + + IF v_zeitraum_id_exists = 0 THEN + ALTER TABLE `warteliste` + ADD COLUMN `zeitraum_id` INT NULL AFTER `impfenzeitraum`; + END IF; + + SELECT COUNT(*) + INTO v_warteliste_index_exists + FROM information_schema.statistics + WHERE table_schema = DATABASE() + AND table_name = 'warteliste' + AND index_name = 'idx_warteliste_zeitraum'; + + IF v_warteliste_index_exists = 0 THEN + ALTER TABLE `warteliste` + ADD INDEX `idx_warteliste_zeitraum` (`zeitraum_id`); + END IF; + END IF; + + SELECT COUNT(*) + INTO v_legacy_plan_exists + FROM information_schema.tables + WHERE table_schema = DATABASE() + AND table_name = 'impfstoff_wochenplan'; + + IF v_legacy_plan_exists > 0 AND v_impf_zeitraum_exists > 0 THEN + INSERT INTO `impf_zeitraum` (`bezeichnung`, `wochentag`, `start`, `ende`, `impfortid`, `aktiv`) + SELECT DISTINCT + '', + wp.`wochentag`, + wp.`start`, + wp.`ende`, + wp.`impfortid`, + wp.`aktiv` + FROM `impfstoff_wochenplan` wp + LEFT JOIN `impf_zeitraum` z + ON z.`wochentag` = wp.`wochentag` + AND z.`start` = wp.`start` + AND z.`ende` = wp.`ende` + AND z.`impfortid` = wp.`impfortid` + AND z.`aktiv` = wp.`aktiv` + WHERE z.`zeitraum_id` IS NULL; + + INSERT INTO `impf_zeitraum_impfstoff` (`zeitraum_id`, `impfstoff_id`) + SELECT DISTINCT + z.`zeitraum_id`, + wp.`impfstoff_id` + FROM `impfstoff_wochenplan` wp + INNER JOIN `impf_zeitraum` z + ON z.`wochentag` = wp.`wochentag` + AND z.`start` = wp.`start` + AND z.`ende` = wp.`ende` + AND z.`impfortid` = wp.`impfortid` + AND z.`aktiv` = wp.`aktiv` + LEFT JOIN `impf_zeitraum_impfstoff` m + ON m.`zeitraum_id` = z.`zeitraum_id` + AND m.`impfstoff_id` = wp.`impfstoff_id` + WHERE wp.`impfstoff_id` > 0 + AND m.`zeitraum_id` IS NULL; + + INSERT INTO `impf_workflow_meta` (`meta_key`, `meta_value`) + VALUES ('legacy_wochenplan_migrated', '1') AS `incoming` + ON DUPLICATE KEY UPDATE `meta_value` = `incoming`.`meta_value`; + END IF; +END $$ +DELIMITER ; + +CALL `migrate_praxis_schema_20260320`(); +DROP PROCEDURE IF EXISTS `migrate_praxis_schema_20260320`;