Гарантировать правильную запись по внешним ключам - MySQL

Узнай цену своей работы

Формулировка задачи:

Ситуация: Есть таблицы tests, questions, answers, passed_test, passed_question_answer. test -> questions - один ко многим questions -> answers - один ко многим test -> passed_test - один ко многим passed_test -> passed_question_answer - один ко многим questions -> passed_question_answer - один ко многим answers -> passed_question_answer - один ко многим Cоздание:
Листинг программы
  1. CREATE TABLE `tests` (
  2. `id` INT(10) UNSIGNED NOT NULL,
  3. `name` VARCHAR(255) COLLATE utf8_unicode_ci NOT NULL,
  4. `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
  5. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
  6. ALTER TABLE `tests`
  7. ADD PRIMARY KEY (`id`),
  8. ADD UNIQUE KEY `tests_name_unique` (`name`);CREATE TABLE `question` (
  9. `id` INT(10) UNSIGNED NOT NULL,
  10. `test_id` INT(10) UNSIGNED NOT NULL,
  11. `name` VARCHAR(255) COLLATE utf8_unicode_ci NOT NULL,
  12. `text` longtext COLLATE utf8_unicode_ci NOT NULL,
  13. `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
  14. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
  15. ALTER TABLE `question`
  16. ADD PRIMARY KEY (`id`),
  17. ADD KEY `question_test_id_foreign` (`test_id`);
  18. ALTER TABLE `question`
  19. ADD CONSTRAINT `question_test_id_foreign` FOREIGN KEY (`test_id`) REFERENCES `tests` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION;CREATE TABLE `answer` (
  20. `id` INT(10) UNSIGNED NOT NULL,
  21. `question_id` INT(10) UNSIGNED NOT NULL,
  22. `name` VARCHAR(255) COLLATE utf8_unicode_ci NOT NULL,
  23. `correct` tinyint(1) NOT NULL
  24. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
  25. ALTER TABLE `answer`
  26. ADD PRIMARY KEY (`id`),
  27. ADD KEY `answer_question_id_foreign` (`question_id`);
  28. ALTER TABLE `answer`
  29. ADD CONSTRAINT `answer_question_id_foreign` FOREIGN KEY (`question_id`) REFERENCES `question` (`id`);CREATE TABLE `passed_test` (
  30. `id` INT(10) UNSIGNED NOT NULL,
  31. `user_id` INT(10) UNSIGNED NOT NULL,
  32. `test_id` INT(10) UNSIGNED NOT NULL,
  33. `started_at` TIMESTAMP NULL DEFAULT NULL,
  34. `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
  35. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
  36. ALTER TABLE `passed_test`
  37. ADD PRIMARY KEY (`id`),
  38. ADD KEY `passed_test_user_id_foreign` (`user_id`),
  39. ADD KEY `passed_test_test_id_foreign` (`test_id`);
  40. ALTER TABLE `passed_test`
  41. ADD CONSTRAINT `passed_test_test_id_foreign` FOREIGN KEY (`test_id`) REFERENCES `tests` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION,
  42. ADD CONSTRAINT `passed_test_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION;CREATE TABLE `passed_question_answer` (
  43. `id` INT(10) UNSIGNED NOT NULL,
  44. `passed_test_id` INT(10) UNSIGNED NOT NULL,
  45. `question_id` INT(10) UNSIGNED NOT NULL,
  46. `answer_id` INT(10) UNSIGNED NOT NULL
  47. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
  48. ALTER TABLE `passed_question_answer`
  49. ADD PRIMARY KEY (`id`),
  50. ADD KEY `passed_question_answer_passed_test_id_foreign` (`passed_test_id`),
  51. ADD KEY `passed_question_answer_question_id_foreign` (`question_id`),
  52. ADD KEY `passed_question_answer_answer_id_foreign` (`answer_id`);
  53. ALTER TABLE `passed_question_answer`
  54. ADD CONSTRAINT `passed_question_answer_answer_id_foreign` FOREIGN KEY (`answer_id`) REFERENCES `answer` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION,
  55. ADD CONSTRAINT `passed_question_answer_passed_test_id_foreign` FOREIGN KEY (`passed_test_id`) REFERENCES `passed_test` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION,
  56. ADD CONSTRAINT `passed_question_answer_question_id_foreign` FOREIGN KEY (`question_id`) REFERENCES `question` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION;
При такой реализации получается что в таблицу

passed_question_answer

может попасть question_id который вовсе не принадлежит этому тесту (passed_test_id -> test_id), так же и answer_id (ответ) который никак не связан с question_id. Начал реализовывать в логике приложения использующего бд, то есть заранее проверять связаны ли ответ и вопрос, тест и вопрос и по результату проверки записывать или нет. В какой-то момент задумался... а можно ли решить это в самой бд... На этом моменте остановился и пока без идей.

Решение задачи: «Гарантировать правильную запись по внешним ключам»

textual
Листинг программы
  1. ALTER TABLE `passed_question_answer`
  2.   ADD CONSTRAINT `passed_question_answer_answer_id_foreign` FOREIGN KEY (`answer_id`,`question_id`) REFERENCES `answer` (`id`,`question_id`) ON DELETE CASCADE ON UPDATE NO ACTION

Объяснение кода листинга программы

  1. В данном коде происходит изменение таблицы passed_question_answer
  2. Добавляется новый внешний ключ с именем passed_question_answer_answer_id_foreign
  3. Указывается, что внешний ключ ссылается на столбцы answer_id и question_id таблицы answer
  4. Устанавливаются действия для внешнего ключа при удалении и обновлении данных:
    • при удалении записи в таблице answer соответствующие записи в таблице passed_question_answer также будут удалены (ON DELETE CASCADE)
    • при обновлении записи в таблице answer соответствующие записи в таблице passed_question_answer не будут изменены (ON UPDATE NO ACTION)

ИИ поможет Вам:


  • решить любую задачу по программированию
  • объяснить код
  • расставить комментарии в коде
  • и т.д
Попробуйте бесплатно

Оцени полезность:

10   голосов , оценка 4 из 5

Нужна аналогичная работа?

Оформи быстрый заказ и узнай стоимость

Бесплатно
Оформите заказ и авторы начнут откликаться уже через 10 минут