Question

In my database of survey questions (s_questions) and possible answers (s_option_choices), I have a lookup table (s_question_options):

Create syntax:

CREATE TABLE `s_question_options` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`questions_id` int(11) unsigned NOT NULL,
`option_choices_id` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `questions_id` (`questions_id`),
KEY `option_choices_id` (`option_choices_id`),
CONSTRAINT `s_question_options_ibfk_1` FOREIGN KEY (`questions_id`) REFERENCES `s_questions` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE,
CONSTRAINT `s_question_options_ibfk_2` FOREIGN KEY (`option_choices_id`) REFERENCES `s_option_choices` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1790 DEFAULT CHARSET=utf8;

Populated thusly:

INSERT INTO s_question_options (questions_id, option_choices_id)
SELECT sq.id, so.id FROM s_questions sq
JOIN s_option_choices so
ON sq.option_groups_id = so.option_groups_id;

If I update s_option_choices with new/changed/deleted possible answers, what syntax will update my lookup table s_question_options so that current questions are made available but old answers (stored in an answers table, and referenced via s_question_options_id) are still correctly referenced?

Edit

I can add new questions or new option_choices to my lookup table with:

INSERT INTO s_question_options (questions_id, option_choices_id)
SELECT sq.id, so.id FROM s_questions sq JOIN s_option_choices so
ON sq.option_groups_id = so.option_groups_id
WHERE sq.id NOT IN (SELECT questions_id FROM s_question_options)
OR so.id NOT IN (SELECT option_choices_id FROM s_question_options)

It's the same select query as the one used originally to populate the lookup table, but with the condition that a) the question entered has not been entered before, or b) the option_choice has not been entered before.

Changing option_choices should not be a requirement, it makes no sense, and has no place in the game of referential integrity.

Deleting option_choices can be solved by my answers table foreign key (id) ON DELETE SET NULL. This will in effect set answers to questions with a deleted option_choice to NULL, and in my application, I can look for this and act accordingly (question not answered, or answered with now irrelevant data).

As was suggested, one could also set a display flag in the option_choices table (or even an array of option_choices in the app) that would prevent those option_choices from being displayed in the form yet still retain the data in the db.

Survey Database Design

Was it helpful?

Solution 2

See my edit for my solution:

INSERT INTO s_question_options (questions_id, option_choices_id)
SELECT sq.id, so.id FROM s_questions sq JOIN s_option_choices so
ON sq.option_groups_id = so.option_groups_id
WHERE sq.id NOT IN (SELECT questions_id FROM s_question_options)
OR so.id NOT IN (SELECT option_choices_id FROM s_question_options)

OTHER TIPS

This is not a syntax problem, this is a database design problem.

You have two major data sets here, questions and options, with a many-to-many relationship between them, based on defined sets made unique by option_groups_id. Presumably, you are then storing the answers somewhere, possibly in this database but possibly in some other data form entirely. And we'll assume that you're storing the answers with that question_option_id you make based on the query above.

To preserve the fidelity of historical answers, you need some way to mark old options as "invalid." To do so, you have two options:

  1. Add an "old answers" store into your database, either by making a new question_answers_old table or by adding an archive or current flag to question_answers.

  2. Adjust the data you store on the answer record, so that it contains both question_id and option_id instead of just question_option_id.

My suggestion is, if at all practical, #2. And replace your table above with a view, unless performance requires you to make it a free-standing table.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top