What I am trying to achieve: a quiz system with user account and cumulative results table.
Process:
-User sets up an account
-User logs in
-User completes quiz
-Answers go into results table
-Results table displayed
My database structure:
Table 1: users
user_id
username
password
email
Table 2: quizzes
quiz_id
title
Table 3: questions
question_id
quiz_id
question
question_notes
Table 4: answers
answer_id
question_id
user_id
answer
answer_notes
Table 5: responses
response_id
quiz_id
user_id
submit_time
The questions will be output from Table 3 via a SELECT.
What I am looking for some pointers for is how I can ensure the relationships for each quiz entry is consistent, so when I run the INSERT statements the IDs are consistent (so the "question_id" for Table 3 and Table 4 are the same, for example)
I am thinking I will have 2 INSERT statements for Table 4 and Table 5. In these insert statements, is there a way to ensure the relationships match?
I am having some trouble visualising how this will work for entering the data into the database, once I've got this figured out I can tackle using the data.
Any pointers to decent tuts or a bit of insight into possible form processing would be much appreciated.
Many Thanks
There's LAST_INSERT_ID() function in MySQL ( mysql_insert_id() in PHP ) that will return auto_increment id from last insert query. This will let you keep consistency.
See here for more details:
http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_last-insert-id
Foreign keys and transactions enforce referential integrity...
For example:
Answers
question_id is a foreign key to question.id
user_id is a foreign key to user.id
(These are set in the table definition)
You insert in to answers like so (sloppy pseudocode):
begin transaction
int qid = select id from question
int uid = select id from user
insert (qid,uid,...) into answers
commit transaction
Related
I'm building a web application where users can answer questions. I'm trying to run a query where these answers are inserted in a table but where a user can only reply once the same question. In my case the query must check on duplicates at question_id & reply_user. (reply_id is already defined as the primary key).
For example when I have in my table answers : question_id = 1 & reply_user = John, John cannot reply anymore on question_id 1. But another user can of course.
I'm currently running this:
INSERT INTO replies (question_id, reply_user, reply_content, reply_anwer)
VALUES (:questionid, :replyuser, :replycontent, :replyanswer)
SELECT question_id, reply_user FROM replies WHERE NOT EXISTS (
SELECT question_id FROM replies
WHERE question_id = question_id AND reply_user = reply_user
)
I tried out with WHERE NOT EXISTS but I couldn't find a solution with that.
Thank's for your help
You need to unique 2 filed:
ALTER TABLE `replies` ADD UNIQUE (`question_id`,`reply_user`);
And then use this query:
INSERT INTO replies (question_id, reply_user, reply_content, reply_anwer)
VALUES (:questionid, :replyuser, :replycontent, :replyanswer)
IF question_id , reply_user exist query not run else run.
create a index unique on the two keys
ALTER TABLE replies ADD UNIQUE `preventDoubleAnswer` (`question_id`, `reply_user`) COMMENT '';
and insert the data as:
INSERT IGNORE ...
The INSERT IGNORE will insert a new data only if the unique key is satisfied.
NB: is the IGNORE param is omitted, it will throw a MYSQL error
Is there any way to stop duplicate entries only if two columns are repeating the same value.
I am using MySQL.
for example
I have one table "voting" and fields are
id
vote
user_id
message_id
In this user can enter up or down vote for a message.
I don't want the user to add multiple vote for the same message
i.e if user_id 1 votes up to message_id 1
then if the same user votes up again for same message , i don't want to allow this repeating process .
I mean is there any way to set unique constraints for fields user_id and message_id and don't allow to insert a row if user_id and message_id is repeating.
I know we can stop it using logical code using php.
I am expecting answers is there any way to do this using mysql only.?
This should work:
ALTER TABLE `table`
ADD CONSTRAINT uc_user_message UNIQUE (user_id ,message_id )
in MySQL, I have a row for each user, with a column that contains friends names separated by \n.
eg.
Friend1
Friend2
Friend3
I'd like to be able to quickly search all the users where the Friends field contains Friend2.
I've found FIND_IN_SET but that only works for commas and the data can contains commas and foreign characters.
Obviously searching with regular expressions and the such will be slow. I'm new to the whole cross referencing so I'd love some help on the best way to structure the data so that it can be found quickly.
Thanks in advance.
Edit: Ok, I forgot to mention a point that the data is coming from a
game where friends names are stored locally and there are no links to
another users ID. Thus the strings. Every time they connect I am given
a dump of their friends names which I use in the background to help match games.
The most commonly used structure for this kind of data is usually adding an extra table. I.e.
user
id,
name
email,
e.t.c.
user_friend
user_id
friend_id
Querying this is a matter of querying the tables. I.e.
List all of a users friends names:
SELECT friend_id
FROM user_friend
WHERE user_id = :theUser
Edit: Regarding OPs edit. Just storing the names is possible too. In this case the table structure would become:
user_friend
user_id
friend_name
and the query:
SELECT friend_name
FROM user_friend
WHERE user_id = :theUser
Why are you keeping friend names as text? This will be inefficient to edit uf say a user removes a friend or changes their name. That's another thing, you should store friend names by some auto_increment id key in your database. It's much faster to search for an integer than a string, especially in a very large database. You should set up a friends table which is like
Column 1: connectionid auto_increment key
Column 2: user1id int
Column 3: user2id int
Column 4: date added date
ect...
Then you can search the connection table above for all rows where user is user1id or user2id and get a list of the other users from that.
My database hasn't been filled yet so I can easily change the format and structure in which the data will be stored.
Yes, you need to normalize your database a bit. With current structure, your searches will be quite slow and consume more space.
Check out this wiki for detailed help on normalization.
You can have the friends table and users table separate and link them both by either foreign key constraint or inner joins.
The structure would be:
Users table
id: AUTO_INCRMENT PK
name
other columns
Friends table
id: AUTO_INCREMENT(not required, but good for partitioning)
UserID:
FriendsID
DateAdded
OtherInfo if required.
table
ID|owner_id|work_id|lorem|etc|
1 |00123 | 00213 |XXXXX|XXX|
2 |00124 | 00213 |XXXXX|XXX|
owner_id (fk) owners.id (owners[id,name,etc])
work_id (fk) work.id (work[id,name,etc])
question is can I set codeigniter that when I
select(table.*,work.name as work,owners.name as owner) from table
it automatically handle joins since that table already contain the fk-ref ? or I must include join('owner','owner.id=table.owner_id) ?
actually all what I want is that when I select a table that contains a fk this fk column is replaced with one column from ref row by just passing the column name on ref table without having to worry about creating a specific function in my module for that each query.
My current solution:
was to create a view for each table that contain a relation and replace all fk columns with desired ref value, but since i have 6 tables 5 of them with fk,i now have 6 tables and 5 view (11 tables)in db which is really kind confusing for me, so any smarter way to do this ?
I think you are making some confusion on what FK is and what it does within a table.
FK constraint grants data integrity when it's present and relates data within tables. It doesn't join anything.
If you want to select records across related tables, you either use a
SELECT * FROM table1,table2 WHERE table1.K1 = table2.FK1
or
SELECT * FROM table1 JOIN table2 ON table1.K1 = table2.FK1
AND YES, you need to tell CodeIgniter to do those queries
Ok so a user comes to my web application and gets points and the like for activity, sort of similar (but not as complex) as this site. They can vote, comment, submit, favorite, vote for comments, write description etc and so on.
At the moment I store a user action in a table against a date like so
Table user_actions
action_id - PK AI int
user_id - PK int
action_type - varchar(20)
date_of_action - datetime
So for example if a user comes along and leaves a comment or votes on a comment, then the rows would look something like this
action_id = 4
user_id = 25
action_type = 'new_comment'
date_of_action = '2011-11-21 14:12:12';
action_id = 4
user_id = 25
action_type = 'user_comment_vote'
date_of_action = '2011-12-01 14:12:12';
All good I hear you say, but not quite, remember that these rows would reside in the user_actions table which is a different table to the ones in which the comments and user comment votes are stored in.
So how do I know what comment links to what row in the user_actions?
Well I could just link to the unique comment_id in the comments table to a new column, called target_primary_key in the user_actions table?
Nope. Can't do that because the action could equally have been a user_comment_vote which has a composite key (double key)?
So the thought I am left with is, do I just add the primary keys in a column and comma deliminate them and let PHP parse it out?
So taking the example above, the lines below show how I would store the target primary keys
new_comment
target_primary_keys - 12 // the unique comment_id from the comments table
user_comment_vote
target_primary_keys - 22,12 // the unique comment_id from the comments table
So basically a user makes an action, the user_actions is updated and so is the specific table, but how do I link the two while still allowing for multiple keys?
Has anyone had experience with storing user activity before?
Any thoughts are welcome, no wrong answers here.
You do not need a user actions table.
To calculate the "score" you can run one query over multiple tables and multiply the count of matching comments, ratings etc. with a multiplier (25 points for a comment, 10 for a rating, ...).
To speed up your page you can store the total score in an extra table or the user table and refresh the total score with triggers if the score changes.
If you want to display the number of ratings or comments you can do the same.
Get the details from the existing tables and store the total number of comments and ratings in an extra table.
The simplest answer is to just use another table, which can contain multiple matches for any key and allow great indexing options:
create table users_to_actions (
user_id int(20) not null,
action_id int(20) not null,
action_type varchar(25) not null,
category_or_other_criteria ...
);
create index(uta_u_a) on users_to_actions(user_id, action_id);
To expand on this a bit, you would then select items by joining them with this table:
select
*
from
users_to_actions as uta join comments as c using(action_id)
where
uta.action_type = 'comment' and user_id = 25
order by
c.post_date
Or maybe a nested query depending on your needs:
select * from users where user_id in(
select
user_id
from
users_to_actions
where
uta.action_type = 'comment'
);