Это был также первый проект, который я сделал в PHP / MySQL около 8 лет назад.
Ваше первое решение - это кодирование базы данных в точном соответствии с вашей формой.Итак, вы хотите записать пользователей и представления в викторине, чтобы это выглядело примерно так:
CREATE TABLE users (
username VARCHAR(16) PRIMARY KEY,
password VARCHAR(8),
email VARCHAR(255),
birthday DATE,
gender ENUM('M', 'F')
);
CREATE TABLE quiz_answers (
username VARCHAR(16) REFERENCES users,
question1 VARCHAR(10),
question2 INT,
question3 ENUM('YES', 'NO', 'MAYBE'),
question4 BOOLEAN,
question5 VARCHAR(25),
submitted_at DATETIME,
PRIMARY KEY (username, submitted_at)
);
Так что это просто запись самого необходимого минимума: пользователь и представления в викторине.Я дал типы для ответов, которые вы должны были бы сделать конкретными для вашей реальной викторины.Я также сделал ответ, отключенный от пользователя и момент, когда они отправили его;у вас больше шансов использовать суррогатный ключ (AUTO_INCREMENT
), но мне нравится как можно больше сопротивляться суррогатам.
Сразу же возникает нарушение 1NF: questionN
.Если бы вы делали это правильно, вы бы назвали эти столбцы после того, как они означают, а не только по какому вопросу.Но нормализация это действительно следующий шаг к формам, которые расширяемы, но отслеживают историю.
Итак, следующее, что вы заметите, это то, что на самом деле тест представляет собой набор вопросов, каждый из которых имеет набор возможныхответы.И тогда отправка формы действительно связывает набор выбранных ответов на их вопросы, в определенной форме теста, от конкретного пользователя теста.Это звучит как четырехсторонние отношения: пользователь, тест, вопрос, ответ.Вы можете обрезать один из них, если не возражаете повторять вопросы о разных викторинах, но для полноты картины давайте пойдем по этому пути.Замените quiz_answers
выше на это:
CREATE TABLE questions (
id INTEGER AUTO_INCREMENT PRIMARY KEY,
question TEXT
);
CREATE TABLE answers (
id INTEGER AUTO_INCREMENT PRIMARY KEY,
question_id INTEGER REFERENCES questions,
answer VARCHAR(255)
);
CREATE TABLE quizzes (
name VARCHAR(255) PRIMARY KEY,
);
У нас нет специальных метаданных для теста, поэтому пока это просто название.
Так что теперь вам нужно отношение один-ко-многим: от вопросов к ответам и от викторин к вопросам.
CREATE TABLE question_answers (
question_id INTEGER REFERENCES questions,
answer_id INTEGER REFERENCES answers,
idx INTEGER,
PRIMARY KEY (question_id, answer_id)
);
CREATE TABLE quiz_questions (
quiz_name VARCHAR(255) REFERENCES quizzes,
question_id INTEGER REFERENCES questions,
idx INTEGER,
PRIMARY KEY (quiz_name, question_id)
);
Сложная часть, как упоминалось выше, это отношения высшего порядка междупользователь и форма отправки, а также ссылка с формы вопросов на ответы пользователей.Я решил разделить это на две таблицы, чтобы избежать повторения.
CREATE TABLE quiz_submissions (
id INTEGER AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(16) REFERENCES users,
quiz_name VARCHAR(255) REFERENCES quizzes,
submitted_at DATETIME
);
CREATE TABLE submission_answer (
submission_id INTEGER REFERENCES quiz_submissions,
question_id INTEGER REFERENCES questions,
answer_id INTEGER REFERENCES answers,
PRIMARY KEY (submission_id, question_id)
);
Это довольно хорошо нормализовано на данный момент.Вы можете видеть, что запрос будет немного сложнее.Чтобы получить все вопросы для викторины, вам нужно присоединиться от викторины к вопросам.Вы можете либо присоединиться оттуда к ответам, чтобы выполнить один большой запрос, чтобы получить все данные, необходимые для построения формы (наряду с необходимостью дополнительной постобработки), либо вы можете попасть в базу данных еще раз для каждого вопроса и сделатьменьше пост-обработки.Я могу спорить в любом случае.Чтобы получить ответы всех конкретных пользователей, вам нужно будет выбрать из user_submissions с идентификатором викторины и именем пользователя в таблице submission_answer на вопрос и ответ, который выбрал пользователь.Таким образом, запрос станет интересным быстро.Вы потеряете свой страх перед объединениями, если он у вас будет.
Надеюсь, это не слишком сильно отстранит вас от реляционных баз данных;делая это, вы фактически создаете реляционную модель внутри реляционной модели, хотя и в ограниченной форме.
Я понимаю, что использование множества естественных ключей, как я делал выше, в наши дни немного неортодоксально.Тем не менее, я рекомендую вам попробовать, по крайней мере, в начале работы, потому что вам будет намного легче увидеть, как должны работать объединения, если они не все целые в диапазоне 1-10.