Using count in in query with left join - php

I am currently trying to count how many dislikes and likes users have given to an image on my website, underneath you will see the database setup I did for exactly that. Vote_type keeps the like and dislike information where 1 is equal to a like, 0 is equal to a dislike.
this table is called projects
Project table
This table is called projects_votes
Now I want to include that when I make a query to drag information from my database for a blog post. This is currently the query I am making to get the necessary information to display a list of blogs on the frontpage. Here I am getting the uploader, project ID for links and so on.
$query = "
SELECT * FROM projects
LEFT JOIN users
ON fk_usr_id = usr_id
LEFT JOIN projects_maps
ON fk_project_id = project_id
ORDER BY RAND()
";
So what I have a hard time understanding is how to count my Likes and Dislikes on a specific blog, so I can display this information on my website.

I solved my problem by changing my query to this statement here.
$query = "
SELECT project_id, usr_publicname, project_name, photofile_name, map_version, usr_publicname, map_date, project_id AS project, (SELECT COUNT(*) FROM projects_votes WHERE vote_type = 1) AS likes,
(SELECT COUNT(*) FROM projects_votes WHERE vote_type = 0) AS dislikes FROM projects
LEFT JOIN projects_votes ON fk_project_id = project_id
LEFT JOIN users ON projects.fk_usr_id = usr_id
LEFT JOIN projects_maps ON projects_maps.fk_project_id = projects.project_id
LEFT JOIN photos ON photos.fk_project_id = projects.project_id
GROUP BY project_id ORDER BY RAND()
";

maybe something like this?
SELECT project_id,
sum(case when vote_type = 1 then 1 else 0 end) likeCount,
sum(case when vote_type = 0 then 1 else 0 end) dislikeCount
FROM projects
LEFT JOIN users ON fk_usr_id = usr_id
LEFT JOIN projects_maps ON fk_project_id = project_id
GROUP BY project_id

Related

PHP/MySQL: A single SQL query to fetch forum name, sum total of topics, posts and the LAST POST (post id, topic id and username)

Ok, so this is not a new problem to me but since sophisticated SQL queries is not among my best talents I would typically break it apart. Still, I'm pretty sure a single query is possible and this time around I feel like doing it right but I simply can't put my finger on it. The following is as far as I could go:
SELECT forums.forum, forums.description, topics.forum_id,
(SELECT COUNT(id) FROM topics WHERE forum_id = forums.id) AS num_topics,
(SELECT COUNT(id) FROM posts WHERE topic_id = topics.id) AS num_posts,
MAX(posts.id) AS last_post
FROM forums
LEFT JOIN topics ON topics.forum_id = forums.id
LEFT JOIN posts ON posts.topic_id = topics.id
LEFT JOIN users ON users.id = posts.author_id
So I select forum id, forum name, short description and forum id aqcuired from topics table because the column name is unique by default. Then I use subqueries to count the sum total of topics and posts per forum (is there any other way to do it?). And then comes the most tricky part. I can get the last post per forum (MAX(post.id)) but try as I might I can't get the topic id and name for it, as well as the post author, which I never even got close to. Doing something like (SELECT topic_id FROM posts WHERE id = last_post) is not working, of course, and I don't understand why.
This will not be quick
SELECT forums.
forum, forums.description, topics.forum_id,
(SELECT COUNT(id) FROM topics WHERE forum_id = forums.id) AS num_topics,
(SELECT COUNT(id) FROM posts WHERE topic_id = topics.id) AS num_posts,
MAX(posts.id) AS last_post,
(SELECT topic_id FROM posts ORDER BY id DESC LIMIT 1) LAst_topic_id,
(SELECT topic_name FROM topics WHERE id = (SELECT topic_id FROM posts ORDER BY id DESC LIMIT 1)) LAst_topic_name
FROM forums
LEFT JOIN topics ON topics.forum_id = forums.id
LEFT JOIN posts ON posts.topic_id = topics.id
LEFT JOIN users ON users.id = posts.author_id
Or if the last id is not really the last id from the post tables
SELECT
forum,description,forum_id,num_topics,num_posts,last_post,
(SELECT topic_id FROM posts WHERE id = last_post) LAst_topic_id,
(SELECT topic_name FROM topics WHERE id = (SELECT topic_id FROM posts WHERE id = last_post)) LAst_topic_name
FROM
(SELECT
forums.forum, forums.description, topics.forum_id,
(SELECT COUNT(id) FROM topics WHERE forum_id = forums.id) AS num_topics,
(SELECT COUNT(id) FROM posts WHERE topic_id = topics.id) AS num_posts,
MAX(posts.id) AS last_post
FROM forums
LEFT JOIN topics ON topics.forum_id = forums.id
LEFT JOIN posts ON posts.topic_id = topics.id
LEFT JOIN users ON users.id = posts.author_id) t1;

Extracting from multiple tables by id and ordering by created date

I`m trying to extract the activity of a user with a single query. I need to do it with one single query so I can use zend paginator.
I have 5 tables: users, replies, threads, wiki_articles and wiki_article_revisions. Each table has 2 common columns created_by and created_on.
I've tried using left join but I think what it returns is not correct and I'm unable to order all activity by created_on
Here is the join I've tried:
SELECT * FROM `users` u
LEFT join `replies` r ON u.id = r.created_by
LEFT join `threads` t ON u.id = t.created_by
LEFT join `wiki_articles` wa ON u.id = wa.created_by
LEFT join `wiki_article_revisions` war ON u.id = war.created_by
WHERE (u.`name` = 'CGeorges')
My thought process was wrong. I should have used UNION for this, like:
(SELECT id, name, created_on FROM users WHERE id = 1)
UNION
(SELECT id, name, created_on FROM threads WHERE created_by = 1)
UNION
(SELECT id, content, created_on FROM replies WHERE created_by = 1)
UNION
(SELECT id, title, created_on FROM wiki_articles WHERE created_by = 1)
ORDER by created_on DESC

Inner Join Query Advice

i need help on creating an inner join query.
I have 2 tables, blogs and followers.
In the blogs table i have all the blog information, and then in the followers i have two fields which are the user id and then the blog id that the user follows.
I want to create a query that will order the blogs by how many followers there are.
So this is an example of what i use to show what builds a user is following (for reference):
$query = "SELECT * FROM blogs INNER JOIN followers ON (blogs.id = followers.blogid) WHERE followers.userid='" .$usernamesesh. "'";
How can i go about creating a similiar query that will select all the blogs, but order them by highest to lowest followers.
Hope this makes sense, i cant get my head around this one for some reason!
The only other option is to add a number of followers field in the blogs table but that would involve changing my follow script etc.
Craig.
Without knowing the exact structure of you tables it's hard to tell. Assuming your followers table looks something like this
blogid | userid
-------+----------
1 | 2
-------+----------
1 | 1
-------+----------
1 | 3
-------+----------
2 | 2
-------+---------
SELECT blogid, COUNT(userid) AS UserCount FROM followers
GROUP BY blogid ORDER BY UserCount DESC;
You can then to join the blogs table to get what ever columns you need from that table.
SELECT blogs.*, IFNULL(f.UserCount,0) AS UserCount
FROM blogs
LEFT JOIN (
SELECT blogid, COUNT(userid) AS UserCount FROM followers
GROUP BY blogid
) f
ON f.blogid = blogs.id
ORDER BY UserCount DESC
I would recommend tweaking Mihai's suggestion a little bit, so add this string to the end of your query: GROUP BY blogs.id ORDER BY COUNT(userid) DESC
$query = "SELECT blogs.id, count(*) AS total FROM blogs INNER JOIN followers ON (blogs.id = followers.blogid) WHERE followers.userid='" .$usernamesesh. "'" GROUP BY blogs.id ORDER BY total;
Hope this helps
You could join against a subquery:
SELECT blogs.*, s.cnt
FROM blogs
LEFT JOIN (SELECT blogid, count(*) as cnt
FROM followers
GROUP BY blogid) s
ON s.blogid = blogs.id
ORDER BY s.cnt
Edit
With
CREATE TABLE blogs (id);
create TABLE followers(blogid);
INSERT into blogs values (1),(2),(3);
INSERT into followers values (1),(1),(3);
You get:
id cnt
---------- ----------
2
3 1
1 2

PHP / MySQL - Confusing Query

Im trying to construct a query that goes over 3 tables and im COMPLETELY stumped ... my knowledge limit is basic 1 table query and i need some help before i stick my head in a blender.
I have the following query
SELECT * FROM internalrole WHERE introle = $imarole
Im fine with that part .. its the next thats getting me all stressed.
That query returns the following columns ( id, user_id, introle, proven, used )
What i then need to do is take the user_id from the results returned and use it to get the following
SELECT * FROM users WHERE id = user_id(from previous query) AND archive = 0 and status = 8
I need to put that into 1 query, but wait, theres more .... from the results there, i need to check if that user's 'id' is in the availability table, if it is, check the date ( column name is date ) and if it matches todays date, dont return that one user.
I need to put all that in one query :S ... i have NO IDEA how to do it, thinking about it makes my head shake ... If someone could help me out, i would be eternaly grateful.
Cheers,
Use INNER JOIN, which links tables to each other based on a common attribute (typically a primary - foreign key relationship)
say an attribute, 'id', links table1 and table2
SELECT t1.att1, t2.att2
FROM table1 t1
INNER JOIN table2 t2
ON t1.id = t2.id --essentially, this links ids that are equal with each other together to make one large table row
To add more tables, just add more join clauses.
SELECT u.*
FROM internalrole ir
INNER JOIN users u
ON ir.user_id = u.id
AND u.archive = 0
AND u.status = 8
LEFT JOIN availability a
ON ir.user_id = a.user_id
AND a.date = CURDATE()
WHERE ir.introle = $imarole
AND a.user_id IS NULL /* User does NOT exist in availability table w/ today's date */
EDIT: This second query is based on the comments below, asking to show only users who do exist in the availability table.
SELECT u.*
FROM internalrole ir
INNER JOIN users u
ON ir.user_id = u.id
AND u.archive = 0
AND u.status = 8
INNER JOIN availability a
ON ir.user_id = a.user_id
WHERE ir.introle = $imarole
Hmm, maybe something like this
SELECT * FROM users WHERE id IN (SELECT user_id FROM internalrole WHERE introle = $imarole) AND archive = 0 and status = 8;
A handy thing for me to remember is that tables are essentially arrays in SQL.
HTH!
Nested queries are your friend.
SELECT * FROM users WHERE id in (SELECT user_id FROM internalrole WHERE introle = $imarole) AND archive = 0 and status = 8
Alternatively joins:
SELECT * FROM users INNER JOIN internalrole ON users.id = internalrole.user_id WHERE internalrole.user_id = $imarole AND users.archive = 0 and users.status = 8

joining two tables based on cookie data

I am making a cookie based favorite system and need to join data from two tables based on the unique user id stored in the cookie so I can tell what items that use has marked as favorites.
I know I need to do a JOIN but have not used them much and dont really have my head around them yet.
Existing query that selects the items from the db:
$query = mysql_query("SELECT *, UNIX_TIMESTAMP(`date`) AS `date` FROM songs WHERE date >= DATE_SUB( NOW( ) , INTERVAL 2 WEEK ) ORDER BY date DESC");
My favorites table is setup as: ID FAVORITE USERID where ID is the primary key, FAVORITE is the song ID from table songs and USERID is a hash stored in a cookie.
I need to join in all the rows from the favorites table where the USERID field matches the cookie hash variable.
I also need to gather the total number of rows in favorites that match the song id so I can display a count of the number of people who set the item as favorite so I can display how many people like it. But maybe need to do that as a separate query?
This should do it, I would imagine:
$user_id = intval($_COOKIE['user_id']);
$query = mysql_query(sprintf("
SELECT *
FROM songs s
INNER JOIN favorites f
ON f.favorite = s.id
WHERE f.userid = %s
", $user_id));
You should probably read up on the different types of joins.
And then to get the total amount of rows returned, you can just call mysql_num_rows on the result:
$favorite_song_count = mysql_num_rows($query);
EDIT: To select all songs but note which are favorited, you would do this:
$query = mysql_query(sprintf("
SELECT s.*, f.id as favorite_id
FROM songs s
LEFT JOIN favorites f
ON f.favorite = s.id AND f.userid = %s
", $user_id));
By switching it from an INNER JOIN to a LEFT JOIN we are selecting all songs even if they don't have a corresponding record in the favorites table. Any songs that are favorites of the user_id provided will have a non-NULL value for favorite_id.
You can have logical (and, or, ...) operators in join conditions:
select t1.*
from t1
join t2 on t1.id = t2.fid and t2.foo = 'blah'
If you are also querying the total number of times each song has been "favorited" then you need a group by construct also, like this way:
select *, count(f.id)
from songs as s
left join favorites as f on s.id = f.favorite and f.userid = <hash>
group by s.id

Categories