SQL table association - php

I have 3 database tables and I need to normalize them. The association between tables like this:
Tables:
1. question
2. answers
3. comments
(question can contain many answers and many comments)
(answers can contains many comments)
I know I could use questions and answers in one table and then I solve the problem, but I want to use it this way, so what's the best way to associate 'comments' with 'questions' and 'answers'?
I tried to put question id and answer id into the table of comments and use only one of them for each row, is this right? this way I can use them in php script but I don't know if there is another better way because that I asked.
Thank you.

I would set them up as follows:
Posts (both Questions and Answers would be types of "Posts"):
Post_ID
[Fields Common to Posts, e.g. "Post_Content", "Poster_ID", "Post_TimeStamp" etc.]
Questions:
Question_ID
Post_ID
[Question-specific fields, e.g. "Question_Title"]
Answers:
Answer_ID
Post_ID
[Answer-specific fields]
Comments:
Comment_ID
Post_ID
[Comment fields]
The main idea of having a seperate Posts table is to normalize your database, and allow for a logical structure for comments to point to both. Like APC commented, it also gives you a good setup for developing your site/database going into the future (when you might want other types of commentable "Posts."
While the enum method listed below would work, it's not good database design. In general, I would suggest only using ENUMs when you're representing a real world list of items.. not for organizing your database structure.

Use an extra type field in the comments table which holds "question" or "answer" for a polymorphic association. Queries then just include that field:
SELECT * FROM `comments` WHERE `type` = 'question' AND `foreign_key` = 42

Related

Multi Threaded Comments PHP?

I have a script I wrote I while back for comments, but it is only single threaded. I would like it to be multi-threaded, but only as so a user can reply to a comment, not so a user can reply to a comment of a comment. So the threads would only be two deep.
Currently I store a comment_id against a user_id in my database.
The only way I can think of to do the multi threaded comments, is to have a parent field in the comments table. But if I do this then when I am selecting the comments with PHP, I will have to do another SELECT command to select the comments children (if any) for each comment. Seems like a lot of work on the database.
There has to be a better way. Any ideas on this? Or tutorials?
There are three (four) alternative possibilities:
A recursive query to select all comments based on their parent ids. This is supported by many database products and the syntax depends on the database type. Check the docs for more info (search for 'recursive').
If you store the article id in each (sub-)comment, you can just select all comments with the article id in one regular select query. You can use the parent ids to properly display the comments on the page under the right parent comment:
SELECT * FROM comments WHERE article_id = :article_id
If you only need two levels of comments, you can use an extended where to include both first level and second level comments:
SELECT * FROM comments
WHERE parent_id = :article_id
OR parent_id IN (SELECT id FROM comments WHERE parent_id = :article_id)
It is also possible to use union all to combine two queries that have the same columns, but since I assume that all data are from the same table, there is probably no need for it (see the extended where-clause above):
SELECT * FROM comments WHERE parent_id = :article_id
UNION ALL
SELECT * FROM comments WHERE parent_id IN
(SELECT id FROM comments WHERE parent_id = :article_id)
Personally, I would go for option 2 because it is simple (no exotic SQL construct required), efficient (1 query) and flexible (supports as many levels of comments as you like).
1 query is enough for this, you just need to loop the data and store it into an array correctly, then you loop the array and display the comments.
This is a common use for hierarchical, or tree-structure data. I wrote a popular answer to this Stack Overflow question: What is the most efficient/elegant way to parse a flat table into a tree?
I also wrote a presentation describing alternatives for tree-structured data: Models for Hierarchical Data with SQL and PHP.
Another solution that is not included in my presentation is the way Slashdot does threaded comments. They use a parent column like you do, so each comment references the comment it replies to. But then they also include a root column so each comment knows the post it belongs to. There are seldom more than a few hundred comments on a given post, and usually you want to get the whole tree of comments for a given post starting at the top of the comment thread:
SELECT * FROM comments WHERE root = 1234;
Then as you fetch the comments, you can write PHP code to process them into arrays of arrays according to the parent columns (this is what #JanL's answer alluded to). I posted code to do this in my answer to another Stack Overflow question, Convert flat array to the multi-dimentional.
This query might work for you (I did not know your structure, so I guessed at it):
SELECT * FROM comments a
LEFT JOIN comments b ON a.comment_id = b.parent_coment_id
LEFT JOIN comments c ON b.comment_id = c.parent_coment_id
WHERE a.comment_id <> b.comment_id
AND a.comment_id <> c.comment_id
AND b.comment_id <> c.comment_id;

PHP/MySQL: Handling Questionnaire Input

I have a questionnaire for users to be matched by similar interests: 40 categories, each with 3 to 10 subcategories. Each of the subcategories has a 0 - 5 value related to how interested they are in that subcategory (0 being not even remotely interested, 5 being a die-hard fan). Let's take an example for a category, sports:
<input type="radio" name="int_sports_football" value="0">0</input>
<input type="radio" name="int_sports_football" value="1">1</input>
<input type="radio" name="int_sports_football" value="2">2</input>
<input type="radio" name="int_sports_football" value="3">3</input>
<input type="radio" name="int_sports_football" value="4">4</input>
<input type="radio" name="int_sports_football" value="5">5</input>
With so many of these, I have a table with the interest categories, but due to the size, have been using CSV format for the subcategory values (Bad practice for numerous reasons, I know).
Right now, I don't have the resources to create an entire database devoted to interests, and having 40 tables of data in the profiles database is messy. I've been pulling the CSV out (Which looks like 0,2,4,1,5,1), exploding them, and using the numbers as I desire, which seems really inefficient.
If it were simply yes/no I could see doing bit masking (which I do in another spot – maybe there's a way to make this work with 6-ary values? ). Is there another way to store this sort of categorized data efficiently?
You do not do this by adding an extra field per question to the user table, but rather you create a table of answers where each answer record stores a unique identifier for the user record. You can then query the two tables together using joins in order to isolate only those answers for a specific user. In addition, you want to create a questions table so you can link the answer to a specific question.
table 1) user: (uniqueID, identifying info)
table 2) answers: (uniqueID, userID, questionID, text) links to unique userID and unique questionID
table 3) question: (uniqueID, subcategoryID, text) links to uniqueID of a subcategory (e.g. football)
table 4) subcategories: (uniqueID, maincategoyID, text) links to uniqueID of a mainCategory (e.g sports)
table 5) maincategories: (uniqueID,text)
An individual user has one user record, but MANY answer records. As the user answers a question, a new record is created in the answers table, storing the uniqueID of the user, the uniqueID of the question, and the value of their answer.
An answer record is linked to a single user record (by referencing the user's uniqueID field) and a single question record (via uniqueID of question).
A question record is linked to a single subcategory record.
A subcategory record is linked to a single category record.
Note this scheme only handles two levels of categories: sports->football. If you have 3 levels, then add another level in the same manner. If your levels are arbitrary, there may be some other scheme more suited.
okay, so, given that you have 40 categories and let's assume 10 subcategories, that leaves us with 400 question-answer pairs per user.
now, in order to design the best intermediary data storage, I would suggest starting out with a few questions:
1) what type of analysis will I need
2) what resources do I have
3) is this one time solution or should it be reused in future
Well, if I were you, I would stick to very simple database structure e.g.:
question_id | user_id | answer
if I would foresee more this kind of polls going on with same questions and probably having same respondents, I would further extend the structure with "campaign_id". This would work as raw data storage which would allow quick and easy statistics of any kind.
now, you said database is no option. well, you can mimic this very same structure using arrays and create your own statistical interface that would work based on the array storage type, BUT, you would save their and your time if you could get sql. as others suggest, there is always sqlite (file based database engine), which, is easy to use and setup.
now, if all that does not make you happy, then there is another interesting approach. if data set is fixed, meaning, that there are pretty much no conditional questions, then, given that you could create question index, you could further create funny 400byte answer chunk, where each byte would represent answer in any of the given values. then what you do is you create your statistical methods that, based on the question id, can easily operate with $answer[$user][$nth] byte (or $answer[$nth][$user] -- again, based on the type of statistics you need)
this should help you get your mind set on the goal you want to achieve.
I know you said you don't have the resources to create a database, but I disagree. Using SQL seems like your best bet and PHP includes SQLite (http://us2.php.net/manual/en/book.sqlite.php) which means you wouldn't need to set up a MySQL database if that were a problem.
There are also tools for both MySQL and SQLite which would allow you to create tables and import your data from the CSV files without any effort.
maybe I am confused but it seems like you need a well designed relational database.
for example:
tblCategories (pkCategoryID, fldCategoryName)
tblSubCategory (pkSubCategoryID, fkdSubCategoryName)
tblCategorySubCategory(fkCategoryID,fkSubCategoryID)
then use inner joins to populate the pages. hopefully this helps you :)
i consider NoSQL architecture as a solution to scaling MySQL field in agile solutions.
To get it done asap, I'd create a class for "interest" category that constructs sub-categories instance which extends from category parent class, carrying properties of answers, which would be stored as a JSON object in that field, example:
{
"music": { // category
"instruments": { // sub category
"guitar": 5, //intrest answers
"piano": 2,
"violin": 0,
"drums": 4
},
"fav artist":{
"lady gaga": 1,
"kate perry": 2,
"Joe satriani": 5
}
}
"sports": {
"fav sport":{
"soccer": 5,
"hockey": 2,
}
"fav player":{
"messi": 5,
"Jordan": 5,
}
}
}
NOTE that you need to use "abstraction" for the "category" class to keep the object architecture right

php: Mysql Database Design and Workflow, need more creativity !

i was wondering if any one can help me with my php-mysql design
my current app. (is a more or less survey app) it let users store questions about targeting specific features in other products also saved in other table in database !
for example , a user can post a car: and then ask users about there opion in safty elements of his car.
car db : Id,brand,safety
brand = Fast
saftety = ABS=ABS (Anti lock braking System),DriverAirBag=Air bags
questions db: ID,Question,Answer,Target,type
eg of data:
Question:safety options you like
Answer:ABS=ABS (Anti lock braking System),DriverAirBag=Air bags"
target:saftey
type=checkbox
problem is that to display questions stored, i have to .
1) loop through all questions, echo Question and echo target in hidden input,
2) explode Answer field twice(1st w/ "," to get each answer and other with "=" to differ > between whats inside the database[0] and a user friendly text[1]
3) check type to chose display type (3 options checkbox,select,text)
4) set this display type of [0] and show [1] for user !!! (stupid i know:()
eg:
< checkbox
value=$expolde[0]>$explode[1]
All these steps make it very hard to maintain, not flexable by any mean cause display is embeded in code :(,
any ideas :) ?
I would separate the tables into a one-to-many type design like:
CarTable
ID
Brand
Model
CarInfo
CarID # Foreign key to Car Table
Category # Optional: Safety, Performance, Looks, etc...
Value # Specific Info Value: ABS, Air Bags, etc...
In this design you can have 0 to many CarInfo records for each Car making it easier to add/remove info records for a car without having to parse a potentially complex field like in your original design.
Your question table design could be similar depending on what your ultimate goal is:
Question
ID
Description
QuestionInfo
QuestionID
Category
Value
Some other things you should be considering and questions you should be asking yourself:
How are you handling custom user inputs? If user1 enters "Air Bags" and user2 requests "Driver Side AirBag" how are you going to match the two?
Make sure you understand the problem before you attempt to solve it. It was not clear to me from your question what you are trying to do (which could be just me or limited size of the question here).
Be careful when outputting raw database values (like the type field in your question table). This is fine as long as the database values cannot be input by the user or are properly sanitized. Search for "SQL Injection" if you are not familiar with it.
If you want a survey PHP application, I suppose, to be clear, that you need something where:
one user can add a subject (a car in your example).
there can be an arbitrary number of questions attached to a subject by that user
each question can accept several types of answers: yes/no (checkbox input), a number (text input, or say 10 radiobuttons with values 1 to 10 attached etc), single or multiple choice (select with or without multi attribute), arbitrary data (textarea). Moreover, some questions may accept comments / "other, please explain" field.
any other user can answer all the questions, and all of them are stored.
A more sophisticated version will require different sets of questions based on what was replied previously, but it's out of the scope of this question, I hope.
For that I think you need several tables:
Subjects
id int pri_key and anything that can come to mind: brand, type etc.
Questions
id int pri_key, text varchar, subject int f_key, type int/enum/?
QuestionOptions
id int pri_key, question int f_key, option varchar
Users
id int pri_key + whatever your authentication structure is
UserReplies
user int f_key, question int f_key, answer varchar, comments varchar
The user-creator sets up a subject and attaches several questions to it. Each question knows which type it is - the field 'type' may be an integer, or an enum value, I myself prefer storing such data as integer and defining constants in php, using something like QTYPE_MULTISELECT or QTYPE_BOOLEAN for readability.
For single/multiselect questions a user-creator also populates the QuestionOptions table, where the options for select-tag are stored.
To display all the questions there'll be something like
SELECT Questions.id, Questions.text, Questions.type, GROUP_CONCAT(CONCAT(questionOptions.id, questionOptions.option)) AS options
FROM Questions LEFT JOIN QuestionsOptions ON (Questions.type = $select AND Questions.id = QuestionsOptions.question)
WHERE Questions.subject = $subject
GROUP BY Questions.id
The GROUP_CONCAT and CONCAT here should be modified to return something like 5:Option_One;6:Option_Two etc, so that exploding the data won't be much hassle.
I realize this is not the cleanest approach in terms of performance and optimization, but It should do for a non-large-scale project.
There is also a drawback in in the above design in that the answers to the "multiple answer question" are stored imploded in the "answer" field of the UserReplies table. Better add another table, where every record holds an option value the user selected for this or that question. That way there will be no unnecessary denormalization in the database and queries for statistics will be much easier (i.e. querying which options were most popular in a single question)

Mysql database design for online exam

Im creating an optimized database for online exam module.
I have two choices:
Schema with which Im storing the question, options and answer in same row.
Table: questions
Col: q_id, question, opt1, opt2, opt3, opt4, opt5, ans
Schema with which Im having 3 different tabled for each operation.
Table: questions
Col: q_id, question
Table: options
Col: q_id, opt_id, option
Table answers
Col: q_id, ans
Schema 2 is more along the lines of what you should be aiming for.
There could be a 3rd option:
q_id | question | options | answer
where the option column is of type text and stores a serialized array or json.
For the 2 options you provided: the 2nd one world be the weapon of choice
I would use schema 2, as it allows you to add an arbitrary amount of answers.
You could however incorporate the answers table in the options table.
q_id | opt_id | option | correct
In this way you can also have multiple correct answers per question.
Schema 2 would be the better option if you want to stay flexible in your exam options.
However, if you are sure you can set a fixed amount of questions every time, you might want to consider putting table "questions" and "options" into one and just having the answers vary for each user.
If there is a fixed number of answers schema 1 would be better
but if the question may have any number of answers this would be better
question_id,question,answer_id_of_the_solution
answer_id,question_id,answer

Storing and Displaying Questionnaire Data, Easier Solution?

Basically this is a questionnaire, but that does not only ask Yes/No type of questions. There are questions that are asked in the form of a table. Here is an example of one of the step pages for the questionnaire:
The questionnaire allows for a client to save what they have entered, log out, come back at a later point in time and continue filling out the rest, then submit. An admin will also be reviewing the questionnaire and allowing the vendor access to only some of the questions in case any corrections are required.
The following text describes my solution to store the data, but i was wondering if there was a simpler way of doing this. Here is also the Database Design for the Questions and Answers table, located on the right side of the image.
The column names for the question should be stored as separate questions as well, except now for instance questions 4.a, will have
4.a.1, "Standard"
4.a.2, "Certifying Organization"
4.a.3, "Date of Last Certification"
So to display this would be pretty simple. If we set the type for the question as a new type, for instance TABLECOL, we would know to create a table and table column". Also since the data is going to be pulled out in ascending order then it should not be a problem to create the html for this. Anyhow, right now I think we will be fine with all the cells as text input types. (Maybe in the future, if and when the time comes, one of the columns in a table might not be a text input field, it could for instance be a drop down, so we would then need a way to describe what to use).
Now also when displaying the table in html, question 4.a has three rows as a default. Other questions have a different number. Also i was thinking about validation as well for table columns. So for all of this, i was thinking about creating a new table called QuestionAttributes. This will serve as a way to store many attributes for a question id. So an example of this use would be, question 4.a is a TABLE and should display 3 rows. In the attributes table there would be an entry such as:
idof 4.a, "MINROWS", 3
For storing the data in the answers table, we would have to put a new field in the answers table that would give the answer uniqueness and ability to be sort as well. So instead of using an autoincrement value, i would say storing UTC time stamp which would also describe when the answer was given, if need be. This will allow sorting to help us display the data in the correct order in the table of the web page. Basically, in the answers table we should have a different integer value for every answer.
The query to retrieve the answers should have a sort on the Questions table sort_order, and the Answers table utc_timestamp. The result of this query will look something like:
4.a.1, "Answer1", 9878921
4.a.2, "Answer2", 9878923
4.a.3, "Answer3", 9878925
4.a.1, "Answer1", 9878926
4.a.2, "Answer2", 9878928
4.a.3, "Answer3", 9878929
Any help would be greatly appreciated.
You're probably going to disagree, but I think the design is way overengineered, especially for a first version. I'd go with as simple a design as possible:
QuestionaireId
Status f.e. "Pre-Approval"
StatusDate
Answerer f.e., "Mike Mayhem"
Question1 f.e., "Yes"
Question2 f.e., "Option6"
Question3 f.e., "Blah Blah Blah"
...
Then you can have a log table that says when someone approved an item, answered a question, and so on:
LogId
QuestionaireId
LogDate
LogEntry f.e., "Questionaire approved by Bill"
For new iterations past the first version, add one-to-many or many-to-many relations only when it adds huge business value. Relations are expensive in terms of complexity, and keeping complexity to a minimum is the essence of a good design.

Categories