I got multilevel comment system, I store comments in mysql database table with such fields:
id
article_id
user_id
date
content
comment_id
Where comment_id is parent comment's id.
how can i count number of replies to user comments after some specific date for all articles?
e.g:
- comment1
-- comment1.1
--- comment1.1.1
-- comment1.2
-- comment1.3
--- comment1.3.1
if user posted comment1, i need query to return 5. If user posted comment 1.3 - return 1.
See Managing Hierarchical Data in MySQL for some ideas. One simple approach is to store the path in the comment tree like you listed above and do a LIKE query. E.g.:
SELECT COUNT(*) WHERE comment_path LIKE 'comment1.%'
You'll of course want an index on the comment_path column, which will be used as long as a % is only used on the end.
if it is possible, you can change your data schema to Nested Sets. With this schema you can count the answers in every hierarchy with a simple addition/substraction. Unfortunately I know only good tutorials in German :-/ for example this.
Related
Description:
I am building a rating system with mysql/php. I am confused as to how I would set up the database.
Here is my article setup:
Article table:
id | user_id | title | body | date_posted
This is my assumed rating table:
Rating table:
id | article_id | score | ? user_id ?
Problem:
I don't know if I should place the user_id in the rating table. My plan is to use a query like this:
SELECT ... WHERE user_id = 1 AND article_id = 10
But I know that it's redundant data as it stores the user_id twice. Should I figure out a JOIN on the tables or is the structure good as is?
It depends. I'm assuming that the articles are unique to individual users? In that case, I could retain the user_id in your rating table and then just alter your query to:
SELECT ... WHERE article_id = 10
or
SELECT ... WHERE user_id = 1
Depending on what info you're trying to pull.
You're not "storing the user_id twice" so much as using the user_id to link the article to unique data associated to the user in another table. You're taking the right approach, except in your query.
I don't see anything wrong with this approach. The user id being stored twice is not particularly relevant since one is regarding a rating entry and the other, i assume, is related to the article owner.
The benefit of this way is you can prevent multiple scores being recorded for each user by making article_id and user_id unique and use replace into to manage scoring.
There are many things to elaborate on this depending on whether or not this rating system needs to be intelligent to prevent gaming, etc. How large the user base is, etc.
I bet for any normal person, this setup would not be detrimental to even a relatively large scale system.
... semi irrelevant:
Just FYI, depending on the importance and gaming aspects of this score, you could use STDDEV() to fetch an average factoring the standard deviation on the score column...
SELECT STDDEV(`score`) FROM `rating` WHERE `article_id` = {article_id}
That would factor outliers supposing you cared whether or not it looked like people were ganging up on a particular article to shoot it down or praise it without valid cause.
you should not, due to 3rd normal form, you need to keep the independence.
"The third normal form (3NF) is a normal form used in database normalization. 3NF was originally defined by E.F. Codd in 1971.[1] Codd's definition states that a table is in 3NF if and only if both of the following conditions hold:
The relation R (table) is in second normal form (2NF)
Every non-prime attribute of R is non-transitively dependent (i.e. directly dependent) on every superkey of R."
Source here: http://en.wikipedia.org/wiki/Third_normal_form
First normal Form: http://en.wikipedia.org/wiki/First_normal_form
Second normal Form: http://en.wikipedia.org/wiki/Second_normal_form
you should take a look to normalization and E/R model it will help you a lot.
normalization in wikipedia: http://en.wikipedia.org/wiki/Database_normalization
I have one table called cf_posts
ID pk
user INT
subject VARCHAR
body TEXT
datetime TIMESTAMP
parent INT
category INT
mod INT
When a post is submitted to the forum the default parent is 0, when a post is submitted as a reply, then its parent is the ID of the original post.
How can I make it so that the default view of the forum main page is ordered that the most recently updated post (including the latest replies) would be at the "top" of the pile, and working down in date order? What would be the PHP/MySQL query?
The only workarounds I have seen for this are separate topics and reply tables, but I'd like to stay away from this approach if possible.
One workaround that I tried and failed was GROUP BY parent.
But this grouped all topics that had no replies together as one.
Another idea that I have yet to try is to make the parent id of the original post match the post ID, and not include matching ID and parent IDs in the output.
I look forward to hearing your thoughts.
SELECT mainPost.subject, lastPost.datetime
FROM cf_posts cfp,
(
SELECT *
FROM cf_posts subPost
WHERE subPost.parent = mainPost
ORDER BY subPost.datetime DESC
LIMIT 1
)lastPost
WHERE mainPost.parent IS NULL
This is done briefly, so there may be some syntax issues but I think this should help.
You can do the following: query each separate thing that you need, so maybe a query for each topic, Then you can use UNION to bunch all of them together to get one list. Now the trick to preserve an order is as followed. For each separate query append a column to the returned result called sort and set each instance of that to a higher int value, then you can guarantee that the final result is properly sorted. Go review UNION for select statements to get a better understanding of what I'm talking about.
Ill try to keep this simple and to the point. Essentially I have a news feed, and a comments section. The comments section has two tiers: responses and then replies to responses. Basically structured like so for a given news post:
-> comment
---> reply
---> reply
Each comment can have multiple replies. Obviously, the WRONG way to do this is to do an SQL query for every comment to check for replies and list them out. EDIT Comments only have 1 tier of replies, ie replies CANNOT have replies. - Thanks JohnP
My Questions for this kind of query:
Should I keep the comments and replies in separate tables and use a JOIN, or can I keep the replies and comments in the same table and use a qualifier to separate the type?
Should I attempt to sort them using the query or pull all the data into an array and sort & display that way?
My table currently is as follow:
ID (unique, auto increment)
NEWS_ID (ties the comment to a particular news post)
REPLY_ID (ties the comment to a parent comment if it is a reply to another comment)
USER_ID
BODY
PUBLISHED_DATE
Any suggestions from those wiser than me would be greatly appreciated! Im still in the very early stages of fully understanding JOINS and other higher level mysql query structures. (IE: I suck at mysql, but im learning :)
Since you said replies are one level deep..
I would make comments 1 table and have a comment_id field to denote ownership and a news_id field to add the relationship to the news item. This way you can simply query for all comments that match the news_id and sort it by comment_id. And then a wee bit of PHP array magic will get you a sorted list of comments/replies.
So having a look at your current table, you're on the correct path.
This is hopefully a quick php question...
I have stored a number of id's in a field on a user table like '1,2,3'. I now want to query another table against those numbers. I think my brain has gone to mush this morning because can't seem to get it right...
Could 'IN' be used in the query?
Update -
Realised I didn't explain my self well enough...
I have two tables, one for users (id, username, password, products) The 'products' field has comma separated id's in which related to another table which holds product information.
I am basically trying to filter out what a user can see, via a query to the database with that users privileges.
You should fix your database schema; then, querying will be obvious and efficient.
Every column in your database should contain atomic values. If you've stored multiple values in a single column, it means you should have created a table with a one-to-many relation.
CREATE TABLE user_whatever (user_id int, whatever_id int)
...with one row per item related to the user. To query another table against those numbers, you simply JOIN this new table in the query.
You may be better off using a join, hard to tell from your question what sort of data you're working with.
Join's would be far more efficient with large amounts of data.
without knowing your database structure it's impossible to help further
SELECT column_names
FROM table_one
INNER JOIN table_two
ON table_one.column_name=table_two.column_name
is the basic syntax.
You can use IN in SQL without any problem, but you will need to convert the PHP array to a compatible list for example using the implode(separator,array) function...
[Update] Seeing the question update: The technique you chose for saving the "products" is not very good and WILL cause you problems in the future. the standard solution for storing these values is creating an additional table (userid,productid) and the primary key is both fields in it you create multiple rows for each user/product permission.
To actually use the specific solution you created you will need to run an SQL getting the values from the table, and then using a SECOND SQL use the WHERE IN $ids to actually get the data (as far as I know you can not use the IN on a string result like this (no such thing as EVAL in SQL)
Like everybody said, you should use atomic values. But since everybody already explained that, I'm only going to clarify in detail what you should do.
Use a table for users, which contains the id, username and password, but without product
id - username - password
1 - mike - XXXX
2 - joe - XXXX
Then you have your products table
id - name - price
1 - Aluminum bat - 19.99
2 - Scattergun - 39.99
3 - BONK (with isotopes) - 14.99
Now, you introdouce a third table, which represents what users have bought.
user - product
1 - 2
1 - 3
2 - 1
Now, if you want to select the products that mike has bought, you just select all rows from the third table where the user id is equal to 1. In conjunction with foreign keys and indexes, the queries should also have a better performance. For clarification, this is how my example would look like in your current implementation.
id - username - password - products
1 - mike - XXXX - 2,3
2 - joe - XXXX - 1
$sql = "SELECT * FROM table
WHERE id IN (";
$i=1;
$count = count($array);
foreach($array as $a){
if($i==$count){
$sql .= $a['id'];
}else{
$sql .= $a['id'].','
}
$i++;
}
$sql .= ")";
Please try this..
Get the field with the ids ('1,2,3') from your first table into some variable -> $ids
$sql = sprintf(SELECT * FROM second_table WHERE id IN (%s),$ids);
Hope this helps.
ps.: I don't recommend storing id this way.
I have a table which would contain information about a certain month, and one column in that row would have mysql row id's for another table in it to grab multiple information from
is there a more efficent way to get the information than exploding the ids and doing seperate sql queryies on each... here is an example:
Row ID | Name | Other Sources
1 Test 1,2,7
the Other Sources has the id's of the rows from the other table which are like so
Row ID | Name | Information | Link
1 John | No info yet? | http://blah.com
2 Liam | No info yet? | http://blah.com
7 Steve| No info yet? | http://blah.com
and overall the information returned wold be like the below
Hi this page is called test... here is a list of our sources
- John (No info yet?) find it here at http://blah.com
- Liam (No info yet?) find it here at http://blah.com
- Steve (No info yet?) find it here at http://blah.com
i would do this... i would explode the other sources by , and then do a seperate SQL query for each, i am sure there could be a better way?
Looks like a classic many-to-many relationship. You have pages and sources - each page can have many sources and each source could be the source for many pages?
Fortunately this is very much a solved problem in relational database design. You would use a 3rd table to relate the two together:
Pages (PageID, Name)
Sources (SourceID, Name, Information, Link)
PageSources (PageID, SourceID)
The key for the "PageSources" table would be both PageID and SourceID.
Then, To get all the sources for a page for example, you would use this SQL:
SELECT s.*
FROM Sources s INNER JOIN PageSources ps ON s.SourceID = ps.SourceID
AND ps.PageID = 1;
Not easily with your table structure. If you had another table like:
ID Source
1 1
1 2
1 7
Then join is your friend. With things the way they are, you'll have to do some nasty splitting on comma-separated values in the "Other Sources" field.
Maybe I'm missing something obvious (been known to), but why are you using a single field in your first table with a comma-delimited set of values rather than a simple join table. The solution if do that is trivial.
The problem with these tables is that having a multi-valued column doesn't work well with SQL. Tables in this format are considered to be normalized, as multi-valued columns are forbidden in First Normal Form and above.
First Normal Form means...
There's no top-to-bottom ordering to the rows.
There's no left-to-right ordering to the columns.
There are no duplicate rows.
Every row-and-column intersection contains exactly one
value from the applicable domain (and
nothing else).
All columns are regular [i.e. rows have no hidden components such as
row IDs, object IDs, or hidden timestamps].
—Chris Date, "What First Normal Form Really Means", pp. 127-8[4]
Anyway, the best way to do it is to have a many to many relationship. This is done by putting a third table in the middle, like Dominic Rodger does in his answer.