I am building a database system wherein users can vote on questions that they themselves can create. What I am struggling with is populating the database correctly, especially because I have tried normalizing it and it confuses me greatly! I am extremely new to SQL and MySQLi.
In the below database, how would I best put in user information when a person makes a profile on my webpage and then use that information again when I, for example, want to tie their UserID to the QuestionID and the Up- or down-votes they made?
I'm looking for assistance on the design of the database, but especially help with my question about putting data into the database and 'tying' it together.
I'd try out something simpler and normalize it one step at a time.
Users table is good
create table users (
userid int not null auto_increment primary key,
email varchar(100) not null,
password varchar(255) not null
);
Questions table
I'd just mark who asked the question right in the questions table to begin with
create table questions (
questionid int not null auto_increment primary key,
question mediumtext or whatever,
userid int not null,
add fk here for userid
);
Question votes - many to many/junction table
create table question_votes (
question_votesid int not null auto_increment primary key,
questionid int not null,
voterid int not null,
votedate timestamp not null default current_timestamp,
score int not null,
add fk here for questionid and voterid
);
Answers table
create table answers (
answerid int not null auto_increment primary key,
answer mediumtext or whatever,
questionid int not null,
userid int not null,
add fk here for questionid, userid
);
You have the option of creating answers that only holds answers and a junction table called question_answers that holds questionid, answerid columns. You can also choose to put question_userid and answer_userid in this table. Read the last paragraph to get to an answer by yourself.
Answer votes table
create table answer_votes (
answer_votesid int not null auto_increment primary key,
answerid int not null,
voterid int not null,
votedate timestamp not null default current_timestamp,
score int not null,
add fk here for answerid and voterid
);
Put some dummy data into it and start asking yourself questions - what kind of information do I need out of this. What if X happens; can this schema adapt to the answers I will be asked in the future when X happens? etc.
As you go through those Q&A in your head, you will find out where you should normalize and to what degree.
Related
I need to store data in a table that looks like this:
$sql = "CREATE TABLE $table_name (
id int unsigned not null auto_increment,
type varchar(15) default '' not null,
name varchar(40) default '' not null,
folder varchar(25) default '' not null,
data mediumtext default '' not null,
shared_network bool,
owner int unsigned,
shared_label bool,
creator int unsigned,
thumbnail int unsigned,
PRIMARY KEY (id)
)$charset_collate;";
My first question is, because the Data value is going to be a large value, would it be more performant to separate this into two tables: 1 with ID and Data and 1 with everything else?
Second, if it IS more performant to use two tables, which of these would be more performant: Running two queries, one to find the IDs that I need and one to get the Data value for those IDs. Or, an outer join on the ID (I would need all the values).
Finally, if an outer join is the best way to go, is ID a FOREIGN KEY on the table with ID and Data or is it a foreign key on the table with everything else?
Thanks.
UPDATE:
An example query would be:
SELECT * from $table_name WHERE
type='something'
AND shared_network=TRUE
AND shared_label=TRUE
For this query:
SELECT *
from $table_name
WHERE type ='something' AND shared_network = TRUE AND shared_label = TRUE;
You can create an index on table(type, shared_network, shared_label). The index will satisfy the where clause. The rest of the work is fetching the appropriate rows. There is no advantage to having two tables.
I am trying to make a quiz program that will fetch the questions and their choices from a MySQL Database, and display them as a form for the user to fill. The user (upon filling all fields) will then submit. There is no scoring here, the answers are just stored in the database with that user's registration ID (This is because some answers are open, i.e : The user has to physically type in the answer, so this will be evaluated later).
The structure of my tables are (I will post the create query since the table looks distorted and hard to read) :
Table 1 : This contains the q_id (Each question has a unique ID), question (The actual question), quiz_id (The id of the quiz that this question belongs to; There are multiple quizzes!), active (A simple "y" or "n" field that checks if this question is active. If it isnt, the question is not added to the form), and type ('c' for single choice, 'm' for Multiple choice, and 'o' for open/user text input)
CREATE TABLE `db_test`.`insr_questions` (
`q_id` INT(10) NOT NULL AUTO_INCREMENT ,
`question` VARCHAR(255) NOT NULL ,
`quiz_id` MEDIUMINT(8) UNSIGNED NOT NULL ,
`active` CHAR(1) NOT NULL ,
`type` CHAR(1) NOT NULL ,
PRIMARY KEY (`id`) );
Table 2: This contains the fields a_id (which is the unique ID of the answer choice), choice (Which is the actual answer choice), question_id (Which is the question ID that these answer choices belong to), and active (Which is same as above)
CREATE TABLE `db_test`.`insr_answers` (
`a_id` INT(10) NOT NULL AUTO_INCREMENT ,
`choice` VARCHAR(75) NOT NULL ,
`question_id` MEDIUMINT(8) UNSIGNED NOT NULL ,
`active` CHAR(1) NOT NULL ,
PRIMARY KEY (`id`) );
So the idea here is to retrieve a quiz_id and then delve into the questions table and retrieve all the questions that belong to that quiz_id, and then take each question, and retrieve all the answers that belong to that question_id. These will then have to be displayed in a php form.
Any pointers in the right direction are greatly appreciated!
(I am currently reading up on different ways in which this can be done, being a novice in php, so I will add any other code that I work on, to this question!)
Read up on JOINS in MySQL, this way you can get the questions and answers in one go:
SELECT * FROM insr_questions q
LEFT JOIN insr_answers a ON q.q_id = a.question_id
WHERE q.active = 1 AND q.quiz_id = 0
And then use mysql_fetch_array to loop through the results. (notice i used the table aliasses q and a, this reduces typing and used for the quiz_id an arbitrary number)
Finished the problem with the following algorithm :
Begin Loop (i from 0 to count(questions) )
Create TextArea
Textarea.Value = Question[i]
Get count(answers) for Question[i]
Get Type of Answer
Case(c)
Begin Loop (n from 0 to count(answers) )
Create Radio Button[n]
Radiobutton[n].Value = Choice[n]
End Loop
Case(m)
Begin Loop (n from 0 to count(answers) )
Create Checkbox[n]
Checkbox[n].Value = Choice[n]
End Loop
Case(o)
Create TextArea
End Case
End Loop
I am in the process of writing a web-based quiz application using PHP and MySQL. I don't want to bore you with the details of it particularly, so here's what (I think) you need to know.
Questions are all multiple choice, and can be stored in a simple table with a few columns:
ID: The question number (primary index)
Category: The category this question falls under (e.g. animals,
vegetables, minerals)
Text: The question stem (e.g. What is 1+1?)
Answer1: A possible answer (e.g. 2)
Answer2: A possible answer (e.g. 3)
Answer3: A possible answer (e.g. 4)
CorrectAnswer: The correct answer to the question (either 1, 2 or 3 (in this case 1))
Users can sign up by creating a username and password, and then attempt questions from categories.
The problem is that the questions I'm writing are designed to be attempted more than once. However, users need to be given detailed feedback on their progress. The FIRST attempt at a question matters, and contributes to a user's overall 'questions answered first time' score. I therefore need to keep track of how many times a question has been attempted.
Since the application is designed to be flexible, I would like to have support for many hundreds of users attempting many thousands of questions. Thus, trying to integrate this information into the user table or questions table seems to be impossible. The way I would like to approach this problem is to create a new table for each user when they have signed up, with various columns.
Table Name: A user's individual table (e.g. TableForUser51204)
QuestionID: The ID of a question that the user has attempted.
CorrectFirstTime: A boolean value stating whether or not the
question was answered correctly first time.
Correct: The number of times the question has been answered
correctly.
Incorrect: The number of times the question has been answered
incorrectly.
So I guess what I would like to ask is whether or not organising the database in this manner is a wise thing to do. Is there a better approach rather than creating a new table for each user? How much would this hinder the performance if there are say 500 users and 2000 questions?
Thanks.
You don't want to be creating a new table per user. Instead, modify your database structure.
Normally, you'd have a table for questions, a table for options (with maybe a boolean column to indicate if it's the correct answer), a users table, and a join table on users and options to store users' responses. A sample schema:
CREATE TABLE `options` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`question_id` int(10) unsigned NOT NULL,
`text` varchar(255) NOT NULL,
`correct` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
KEY `question_id` (`question_id`)
) TYPE=InnoDB;
CREATE TABLE `options_users` (
`option_id` int(10) unsigned NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`created` timestamp NOT NULL,
KEY `option_id` (`option_id`),
KEY `user_id` (`user_id`)
) TYPE=InnoDB;
CREATE TABLE `questions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`question` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`,`question`)
) TYPE=InnoDB;
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(60) NOT NULL,
`password` char(40) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) TYPE=InnoDB;
ALTER TABLE `options`
ADD CONSTRAINT `options_ibfk_1` FOREIGN KEY (`question_id`) REFERENCES `questions` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE `options_users`
ADD CONSTRAINT `options_users_ibfk_2` FOREIGN KEY (`option_id`) REFERENCES `options` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `options_users_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
This links options to questions, and users' responses to options. I've also added a created column to the options_users table so you can see when a user answered the question and track their progress over time.
I just have a general database theory question. I have a need to make something similar to showing what posts/items a user has viewed or not (such as in a forum) or an unread email message. What I have is there are posts that multiple users can view, but it needs to separate by user who has actually viewed it. So if User A viewed Post 1, it would no longer show that Post 1 is a new item to view, but to User B, it would still show that Post 1 is a new item to view.
I've search for other ideas and one of them is to get a timestamp of when the user last logged in, but I actually need to keep track of the posts they've seen as opposed to posts that have happened since they last logged in.
I would like a MySQL database solution if possible, but I'm open to cookies if that is a must. I could do this on my own and just figure it out, but I'd appreciate any advice on how to properly structure a table(s) to make this the most efficient. Also, bandwidth and storage is not issue.
While reviewing the relevant schema for phpBB, I found the following:
# Table: 'phpbb_topics_track'
CREATE TABLE phpbb_topics_track (
user_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
topic_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
forum_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
mark_time int(11) UNSIGNED DEFAULT '0' NOT NULL,
PRIMARY KEY (user_id, topic_id),
KEY topic_id (topic_id),
KEY forum_id (forum_id)
) CHARACTER SET `utf8` COLLATE `utf8_bin`;
And:
# Table: 'phpbb_forums_track'
CREATE TABLE phpbb_forums_track (
user_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
forum_id mediumint(8) UNSIGNED DEFAULT '0' NOT NULL,
mark_time int(11) UNSIGNED DEFAULT '0' NOT NULL,
PRIMARY KEY (user_id, forum_id)
) CHARACTER SET `utf8` COLLATE `utf8_bin`;
Then I look here in their wiki:
This table keeps record for visited topics in order to mark them as
read or unread. We use the mark_time timestamp in conjunction with
last post of topic x's timestamp to know if topic x is read or not.
In order to accurately tell whether a topic is read, one has to also
check phpbb_forums_track.
So essentially they have a lookup table to store the data associated with a user's viewing of a topic (thread), and then check it against the timestamp in the forum view table, to determine whether the topic has been viewed by the user.
Just create a simple cross-reference table (read_posts or something):
user_id|post_id
----------------
2 | 132
53 | 43
....
Make sure that both of these columns are indexed (especially important that the user_id be indexed) and then use a join (or a sub-query) to select unread posts for the logged in user. If you're just trying to show a list of unread posts, for example, you just run:
SELECT * FROM `posts` WHERE `post_id` NOT IN (
SELECT `post_id` FROM `read_posts` WHERE `user_id`='[$USER ID]')
ORDER BY [your ordering clause]
Based on this description I would use a simple table with maybe 3 columns.
User ID
Post ID
Timestamp First Viewed
When a user views a post, add a row to the table. If a row does not exist in the table for a given user/post id combo, then they have not viewed the post.
I am building a commenting system where people can comment on uploaded files, messages and to-do items. What is the best way to connect the comment table table to the other various tables?
Possible Solutions
Solution one - use a two field foreign key.
CREATE TABLE `comments`(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
foreign_key INT NOT NULL,
table_name enum('files','messages','to-do'),
user_id INT NOT NULL,
comment TEXT NOT NULL);
Solution two - Each table would have a primary key unique to the database. So I would use php's uniqid($prefix) as the primary keys for each table.
CREATE TABLE `comments`(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
foreign_key char(23) NOT NULL,
table_name enum('files','messages','to-do'),
user_id INT NOT NULL,
comment TEXT NOT NULL);
Solution Three - Have multiple foreign keys in the comment table
CREATE TABLE `comments`(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
files_id INT NOT NULL,
messages_id INT NOT NULL,
to_do_id INT NOT NULL,
user_id INT NOT NULL,
comment TEXT NOT NULL);
What is the best solution? I appreciate your input and please let me know if I can clarify anything
EDIT removed table_name from solution three as it was a copy_paste error
As to Joe's Response
Assume:
1) all data is already escaped. Do we really need to see that?
2) $fileId = "146".
3) $userId = "432".
4) $comment = "Stackoverflow is so awesome!"
INSERT
$link = mysql_connect('localhost', 'mysql_user', 'mysql_password');
if (!$link) {
die('Could not connect: ' . mysql_error());
}
mysql_select_db('mydb');
mysql_query("INSERT INTO `comments` (user_id,comment) VALUES($userId,$comment)");
$commentId = mysql_insert_id();
mysql_query("INSERT INTO `comments_files_xref` (file_id,comment_id) VALUES($fileId,$commentId)");
Personally, I would normalize the design a bit more. Perhaps something like:
Multiple remarks :
You shouldn't call your foreign key foreign_key because a foreign key is a constraint, not a field in a way. it references a field in a table to an index on another, call it the same way you called the PK on the table you reference, or something recognizable.
Foreign keys constraints only works on innodb, if you use MyISAM forget about them and do a lot of checks with PHP.
read http://dev.mysql.com/doc/refman/5.0/en/innodb-foreign-key-constraints.html
You should make a sketch of what you want in your DB or use tools like mysql workbench (which is free) that helps seeing the schema better.
As I see your problem and if you want to use constraint here, I'll use solution one or another solution :
1-
CREATE TABLE `comments`(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, // Which is your comment index
idTable INT NOT NULL, // ID of the message
table_name enum('files','messages','to-do'), // which it comes from
user_id INT NOT NULL, // etc...
comment TEXT NOT NULL);
But there are conditions :
The PK of files, messages _to-do_ must have the same format (INT)
If you want to add a module (to files, messages _to-do_) it'll be difficult
2-
Create tables joining comments and other tables :
CREATE TABLE `comments`(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, // Which is your comment index
user_id INT NOT NULL, // etc...
comment TEXT NOT NULL);
CREATE TABLE `comments-files`(
id_comments INT NOT NULL PRIMARY KEY,
id_files INT NOT NULL PRIMARY KEY);
etc. Hope you see the point here. You add constraint thanks to http://dev.mysql.com/doc/refman/5.0/en/innodb-foreign-key-constraints.html if needed.
I just learn Ruby on Rails in my current company, and solution 1 is preferred because RoR's Active Record can handle it as polymorphic relation.
Back to the topic, that you are using PHP, I prefer either solution 1 or 3. Solution 1 is preferable if there are possibilities that the comment table will be used for other table in the future.
One note, in solution 3, I think the table_name column is not needed. You can determine for which table the comment is by fill either files_id, messages_id, or to_do_id with the id, then set 2 other foreign key with 0.
I would create a join table for each thing that can be commented on, so an files_comments table and a todo_comments table. But solution 1 would be an alternative. I would avoid solutions two or three... could get messy if things change in the future.