CREATE TABLE IF NOT EXISTS `user_notifications` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `recipient_user_id` INT UNSIGNED NOT NULL, `tenant_id` INT UNSIGNED NULL, `type` VARCHAR(60) NOT NULL, `title` VARCHAR(255) NOT NULL, `body` VARCHAR(500) NULL, `link` VARCHAR(500) NULL, `data` JSON NULL, `dedupe_fingerprint` CHAR(64) NULL, `dedupe_bucket` INT UNSIGNED NULL, `dedupe_until` DATETIME NULL DEFAULT NULL, `is_read` TINYINT(1) NOT NULL DEFAULT 0, `read_at` DATETIME NULL DEFAULT NULL, `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_un_recipient_read_created` (`recipient_user_id`, `is_read`, `created` DESC), KEY `idx_un_recipient_created` (`recipient_user_id`, `created` DESC), KEY `idx_un_tenant_created` (`tenant_id`, `created` DESC), KEY `idx_un_cleanup` (`is_read`, `read_at`), UNIQUE KEY `uniq_un_dedupe_bucket` (`recipient_user_id`, `dedupe_fingerprint`, `dedupe_bucket`), CONSTRAINT `fk_un_recipient` FOREIGN KEY (`recipient_user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, CONSTRAINT `fk_un_tenant` FOREIGN KEY (`tenant_id`) REFERENCES `tenants` (`id`) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ALTER TABLE `user_notifications` ADD COLUMN IF NOT EXISTS `dedupe_fingerprint` CHAR(64) NULL AFTER `data`, ADD COLUMN IF NOT EXISTS `dedupe_bucket` INT UNSIGNED NULL AFTER `dedupe_fingerprint`, ADD COLUMN IF NOT EXISTS `dedupe_until` DATETIME NULL DEFAULT NULL AFTER `dedupe_bucket`; CREATE UNIQUE INDEX IF NOT EXISTS `uniq_un_dedupe_bucket` ON `user_notifications` (`recipient_user_id`, `dedupe_fingerprint`, `dedupe_bucket`);