MySQL table/query design - multiples of one item, many comments - php

I have a site which display images, up to 30 per page.
Users can comment on the images and these comments, or at least the first few, appear under the image if there are comments.
I have a table of image references linked to a folder on my server.
e.g.
image_id // image id
user_id // user who added
image_url // ref to image
Then a separate table for all comments
comment_id
image_id // link to images table
comm_poster_id // id of user who posted comment
Now, the question is what the best way to call the information together? Ideally in one select
I can't really ajax call under each image as that would be 30 db calls per page which would kill it so whats the alternative/best method?
To clarify, in the select there would only ever be 1 image but there could of course be multiple comments for an image
Hope i've given enough info
EDIT To clarify, the question is what is the best way to collate all this information together for display - can I run one query which pulls all the images in on the page also somehow pulls the comments for images in if they exist.
As for how I would like the data to look... I don't know. This is the first time I've done anything like this so guidance needed if possible.

Ok, well I'm not a php expert, but I got you started on the sql side of things. I CAN help you with php, but there are others here that are more versed in it that I am.
I started this sqlFiddle for you, go have a look and you can tinker with the query to get what you want.
http://sqlfiddle.com/#!2/79ecf/1/0
From the php side, until you know how you want to display your data, it's difficult to say what your query needs to look like. I went with this for the time being:
select *
from images i
inner join comments c on i.image_id=c.image_id;
This is a VERY simple query and you will probably end up needing to add to it.
I'll assume you are using mysql as most people using php choose mysql. From my understanding there are 2 ways to connect, mysqli and pdo. PDO seems to be emerging as the preferred method, but I know nothing about it. Here are references for both. Just DO NOT USE mysql_query(), it is deprecated so don't bother learning any part of it.
PDO: http://dev.mysql.com/doc/refman/5.6/en/apis-php-pdo-mysql.html
MYSQLI: http://php.net/manual/en/mysqli.query.php
Either of these should give enough of a tutorial to show you how to query your database and then loop through the results to get to your data. It is then up to you how you want to display it on your page.
Hopefully this is enough to point you in the right direction.
Good Luck!

Your easiest approach is to just join the two tables together, sorting first by image and sub-sorting by comment.
SELECT i.*, c.comment_id, c.comm_poster_id
FROM images i LEFT JOIN comments c ON i.image_id=c.image_id
WHERE {whatever where clause selects your set of 30 images}
ORDER BY i.image_id, c.comm_poster_id
Use a LEFT JOIN or images without comments won't display.

Related

Most efficient way to query MySQL for latest comments + total number of comments

On Facebook, when a wall post has a lot of comments, you see only the last two comments. Right above them, however, is a link that says something like "View all 15 comments."
In MySQL, would this require two queries? One to get the total comment count, and the other to get the contents of the last two comments? Then, clicking that "View all" link requires a third in order to get the content of the remaining comments.
If this is the case, wouldn't it be more efficient to simply get the contents of all the comments in a single MySQL query? This would allow you to find the total number using PHP, and you wouldn't need to make the third query if you simply output the contents of all the comments to the page, using css to hide those that you don't want initially shown and JavaScript to show them when the user clicks the "View All" link.
I assume I'm wrong. Surely the Facebook developers are better at this than I am. But I'm developing a site that has a similar requirement and I'm trying to understand the most efficient way to achieve this kind of functionality.
In MySQL you could use a limited select to retrieve the latest comments specifying SQL_CALC_FOUND_ROWS followed by a FOUND_ROWS() query to fetch the count of all comments.
ie. First do :
select SQL_CALC_FOUND_ROWS title, description from comments order by post_date desc limit 2;
to retrieve the 2 most recent comments, then do :
select FOUND_ROWS();
to retrieve the total number of comments.
Refer to the FOUND_ROWS() documentation on mysql.com for more information.
As far as performance is concerned, it should be faster then executing another COUNT(*) query (according to the docs :p)...
They probably know that something like 90% of all page viewers will never click on the "view all" link, so by doing it that way, they conserve the extra database traffic, and the extra web server to client usage.
Performing two database queries back-to-back between the webserver the the database has neglible overhead. There is no need to set up another connection or for the db server to create another thread to service the connection. This isn't really something ripe for optimization. Reducing the amount of data retrieved is prudent use of resources.
Ultimately, the best/fastest way to get the comments count is to store them within the news post table, your almost caching the "COUNT(*)" query to the comments column.
You can then chose to load the comments with a second query if you need to or skip it completely if there are no comments.
You could also opt for a LEFT JOIN query which will count the comments within a subquery, and return 0 if it needs to, keeping it as 1 query instead of 2.

a question about mysql select query!

I have a list of events for example I wanna show on a page with the users that have created them which is all in a table and the user who has created them's unique id, now if I wanna show their username and avatar I would have to run 100 queries inorder to show 100 events! but I'm sure their is a easier way I don;t know!
i have a table (user_table) with fields user_id INT(8) and user_photo VARCHAR(255)
and I have another table (user_event_table) with event_id INT(8), event_user_id INT(8), event_details TEXT
so I want to show a list of all these events but I want to next to it show the user_photo !
Learn to join with SQL. It's fundamental to relational databases.
SELECT * FROM user_event_table uet
LEFT JOIN user_table ut
ON ut.user_id = uet.user_id
Now each record will have a username and photo string.
Show us your tables, along with the query you're currently using, in a different question and people will help you with the SQL.
Yes. Get the accurate current time at the start of your PHP script, and get the time at the end, and log the page name and the difference in times.
If you're worried about this, you need to conduct a security audit of your scripts. There's no easy way to tell what someone who has access to your page's contents will leak.
Again, you need a real security audit. Someone will have to read and understand all the code in order to be sure. There's no easy way.

using joins or multiple queries in php/mysql

Here i need help with joins.
I have two tables say articles and users.
while displaying articles i need to display also the user info like username, etc.
So will it be better if i just use joins to join the articles and user tables to fetch the user info while displaying articles like below.
SELECT a.*,u.username,u.id FROM articles a JOIN users u ON u.id=a.user_id
OR can this one in php.
First i get the articles with below sql
SELECT * FROM articles
Then after i get the articles array i loop though it and get the user info inside each loop like below
SELECT username, id FROM users WHERE id='".$articles->user_id."';
Which is better can i have explanation on why too.
Thank you for any reply or views
There is a third option. You could first get the articles:
SELECT * FROM articles
Then get all the relevant user names in one go:
SELECT id, username FROM users WHERE id IN (3, 7, 19, 34, ...)
This way you only have to hit the database twice instead of many times, but you don't get duplicated data. Having said that, it seems that you don't have that much duplicated data in your queries anyway so the first query would work fine too in this specific case.
I'd probably choose your first option in this specific case because of its simplicity, but if you need more information for each user then go with the third option. I'd probably not choose your second option as it is neither the fastest nor the simplest.
It depends how much data the queries are returning - if you'll be getting a lot of duplicate data (i.e. one user has written many articles) you are better off doing the queries separately.
If you don't have a lot of duplicated data, joins are always preferable as you only have to make one visit to the database server.
The first approach is better if applicable/possible:
SELECT a.*, u.username, u.id
FROM articles a
JOIN users u ON u.id = a.user_id
You have to write less code
There is no need to run multiple queries
Using joins is ideal when possible
Get the articles with one query, then get each username once and not every time you display it (cache them in an array or whatever).

SQL query to collect entries from different tables - need an alternate to UNION

I'm running a sql query to get basic details from a number of tables. Sorted by the last update date field. Its terribly tricky and I'm thinking if there is an alternate to using the UNION clause instead...I'm working in PHP MYSQL.
Actually I have a few tables containing news, articles, photos, events etc and need to collect all of them in one query to show a simple - whats newly added on the website kind of thing.
Maybe do it in PHP rather than MySQL - if you want the latest n items, then fetch the latest n of each of your news items, articles, photos and events, and sort in PHP (you'll need the last n of each obviously, and you'll then trim the dataset in PHP). This is probably easier than combining those with UNION given they're likely to have lots of data items which are different.
I'm not aware of an alternative to UNION that does what you want, and hopefully those fetches won't be too expensive. It would definitely be wise to profile this though.
If you use Join in your query you can select datas from differents tables who are related with foreign keys.
You can look of this from another angle: do you need absolutely updated information? (the moment someone enters new information it should appear)
If not, you can have a table holding the results of the query in the format you need (serving as cache), and update this table every 5 minutes or so. Then your query problem becomes trivial, as you can have the updates run as several updates in the background.

PHP join help with two tables

I am just learning php as I go along, and I'm completely lost here. I've never really used join before, and I think I need to here, but I don't know. I'm not expecting anyone to do it for me but if you could just point me in the right direction it would be amazing, I've tried reading up on joins but there are like 20 different methods and I'm just lost.
Basically, I hand coded a forum, and it works fine but is not efficient.
I have board_posts (for posts) and board_forums (for forums, the categories as well as the sections).
The part I'm redoing is how I get the information for the last post for the index page. The way I set it up is that to avoid using joins, I have it store the info for latest post in the table for board_forums, so say there is a section called "Off Topic" there I would have a field for "forum_lastpost_username/userid/posttitle/posttime" which I woudl update when a user posts etc. But this is bad, I'm trying to grab it all dynamically and get rid of those fields.
Right now my query is just like:
`SELECT * FROM board_forums WHERE forum_parent='$forum_id''
And then I have the stuff where I grab the info for that forum (name, description, etc) and all the data for the last post is there:
$last_thread_title = $forumrow["forum_lastpost_title"];
$last_thread_time = $forumrow["forum_lastpost_time"];
$lastpost_username = $forumrow["forum_lastpost_username"];
$lastpost_threadid = $forumrow["forum_lastpost_threadid"];
But I need to get rid of that, and get it from board_posts. The way it's set up in board_posts is that if it's a thread, post_parentpost is NULL, if it's a reply, then that field has the id of the thread (first post of the topic). So, I need to grab the latest post_date, see which user posted that, THEN see if parentpost is NULL (if it's null then the last post is a new thread, so I can get all the info of the title and user there, but if it's not, then I need to get the info (title, id) of the first post in that thread (which can be found by seeing what post_parentpost is, looking up that ID and getting the title from it.
Does that make any sense? If so please help me out :(
Any help is greatly appreciated!!!!
Updating board___forums whenever a post or a reply is inserted is - regarding performance - not the worst idea. For displaying the index page you only have to select data from one table board_forums - this is definitely much faster than selecting a second table to get the "last posts' information", even when using a clever join.
You are better off just updating the stats on each action, New Post, Delete Post etc.
The other instances would not likely require any stats update (deletion of a thread would trigger a forum update, to show one less topic in the topic count).
Think about all the actions the user would do, in most cases, you dont need to update any stats, therefore, getting the counts on the fly is very inefficient and you are right to think so.
It looks like you've already done the right thing.
If you were to join, you'd do it like this:
SELECT * FROM board_forums
JOIN board_posts ON board_posts.forum_id = board_forums.id
WHERE forum_parent = '$forum_id'
The problem with that, is that it gets you every post, which is not useful (and very slow). What you would want to do is something like this
SELECT * FROM board_forums
JOIN board_posts ON board_posts.forum_id = board_forums.id ORDER BY board_posts.id desc LIMIT 1
WHERE forum_parent = '$forum_id'
except SQL doesn't work like that. You can't order or limit on a join (or do many other useful things like that), so you have to fetch every row and then scan them in code (which sucks).
In short, don't worry. Use joins for the actual case where you do want to load all forums and all posts in one hit.
The simple solution will result in numerous queries, some optional, as you're already discovered.
The classic approach to this is to cache the results, and only retrieve it once in a while. The cache doesn't have to live long; even two or three seconds on a busy site will make a significant difference.
De-normalizing the data into a table you're already reading anyway will help. This approach saves you figuring out optional queries and can be a bit of a cheap win because it's just one more update when an insert is already happening. But it shifts some data integrity to the application.
As an aside, you might be running into the recursive-query problem with your threads. Relational databases do not store heirarchical data all that well if you use a "simple" algorithim. A better way is something sometimes called 'set trees'. It's a bit hard to Google, unfortunately, so here are some links.

Categories