How to use ORDER BY in a query which is containing JOIN? - mysql

I have a query which selects all comments for each post. Here is my query:
SELECT c.id, c.content, u.name, u.reputation, SUM(v.value) AS total_vote_comments
FROM comments c
INNER JOIN users u ON c.user_id = u.id
LEFT JOIN votes_comments v ON c.id = v.comment_id
WHERE c.post_id = :id;
Now I want to add ORDER BY c.id to that query. How?

An order by is irrelevant because this query returns one row:
SELECT c.id, c.content, u.name, u.reputation, SUM(v.value) AS total_vote_comments
FROM comments c INNER JOIN
users u
ON c.user_id = u.id LEFT JOIN
votes_comments v
ON c.id = v.comment_id
WHERE c.post_id = :id;
This is an aggregation query (because of the SUM()) without a GROUP BY. Such a query always returns one row, even when no rows match the join.
You probably want a GROUP BY. My best guess is:
SELECT c.id, c.content, u.name, u.reputation, SUM(v.value) AS total_vote_comments
FROM comments c INNER JOIN
users u
ON c.user_id = u.id LEFT JOIN
votes_comments v
ON c.id = v.comment_id
WHERE c.post_id = :id
GROUP BY c.id, c.content, u.name, u.reputation
ORDER BY c.id;

You can just add the ORDER BY clause at the end:
SELECT c.id, c.content, u.name, u.reputation, SUM(v.value) AS total_vote_comments
FROM comments c
INNER JOIN users u ON c.user_id = u.id
LEFT JOIN votes_comments v ON c.id = v.comment_id
WHERE c.post_id = :id
ORDER BY c.id;

By using this query you get only one row which id you put in where clause.
If you want to get one post ordered then you write order by in last.
If you want to get last's comment first so use desc with order by.
SELECT c.id, c.content, u.name, u.reputation, SUM(v.value) AS total_vote_commentFROM comments c INNER JOIN users u ON c.user_id = u.id LEFT JOIN votes_comments v ON c.id = v.comment_id WHERE c.post_id = :id order by id desc;
desc = descending order
asc = ascending order

Related

Display results which have no count/zero as well

I am trying to get a count of the number of logins during a given timeframe, currently my SQL query displays only results that had at least one login, I'd like it to display even those which have zero logins.
Query i'm using:
SELECT c.FullName, COUNT(l.Id)
FROM LoginsTable l JOIN UsersTable u ON u.Email = l.Email JOIN Organisations c ON c.Id = u.OrganisationId
WHERE l.AttemptTime > "2019-10-01" AND l.AttemptTime < "2019-11-01" AND l.Success = 1
GROUP BY c.Name
ORDER BY c.Name ASC;
You have a few issues. Firstly, you either need to use a RIGHT JOIN from LoginsTable or reorder the JOINs to put the JOIN to LoginsTable last and use a LEFT JOIN. Given the nature of your query the latter probably makes more sense.
Secondly, you need to put any conditions on fields from a table which has been LEFT JOINed into the join condition, otherwise MySQL converts the LEFT JOIN into an INNER JOIN (see the manual). Finally, you should GROUP BY the same fields as specified in your SELECT. This should work:
SELECT c.FullName, COUNT(l.Id)
FROM Organisations c
JOIN UsersTable u ON u.OrganisationId = c.Id
LEFT JOIN LoginsTable l ON u.Email = l.Email AND l.AttemptTime > "2019-10-01" AND l.AttemptTime < "2019-11-01" AND l.Success = 1
GROUP BY c.FullName
ORDER BY c.FullName
I found 2 issues here:
your group by column is not listed on your column
date condition is using double quotes.
try below query.
SELECT c.FullName, COUNT(l.Id)
FROM LoginsTable l
LEFT JOIN UsersTable u ON u.Email = l.Email
LEFT JOIN Organisations c ON c.Id = u.OrganisationId
WHERE l.AttemptTime between '2019-10-01' AND '2019-11-01' AND l.Success = 1
GROUP BY c.FullName
ORDER BY c.FullName ASC;
As Roman Hocke said you need to use left join as below :
SELECT c.FullName, COUNT(l.Id)
FROM UsersTable u
JOIN Organisations c ON c.Id = u.OrganisationId
LEFT JOIN LoginsTable l ON u.Email = l.Email
WHERE l.AttemptTime > "2019-10-01" AND l.AttemptTime < "2019-11-01" AND l.Success = 1
GROUP BY c.Name
ORDER BY c.Name ASC;
Moreover, you should fix your group by or select using the same field : SELECT c.Name or GROUP BY c.FullName ORDER BY c.FullName
EDIT : Nick's answer is the one. As he said perfectly well, you need to put your conditions in the on clause of your left join.
SELECT c.FullName, COUNT(l.Id)
FROM UsersTable u
JOIN Organisations c ON c.Id = u.OrganisationId
LEFT JOIN LoginsTable l ON (u.Email = l.Email AND l.AttemptTime > "2019-10-01" AND l.AttemptTime < "2019-11-01" AND l.Success = 1)
GROUP BY c.FullName
ORDER BY c.FullName ASC;

SQL - select for each user the total articles and the total article comments

I am trying to get for each user the total number of articles and for each article the total number of comments, something like this:
username | total_articles | total_comments
John Doe | 3 | 10
This is my SQL until now, I am using MySQL:
SELECT u.id, u.username, COUNT(a.id) AS total_articles, COUNT(c.id) AS total_comments FROM users u
LEFT JOIN articles a ON u.id = a.user_id
LEFT JOIN comments c ON a.id = c.article_id
GROUP BY u.id;
I tried to group by u.id, a.id, c.id at the same time but it's not working correctly.
Thanks.
In the first query there are all the articles by user, in the second all the comments joined by user
edited: use LEFT JOIN insted JOIN
SELECT id_total_articles, username, total_articles, total_comments
FROM
(
SELECT u.id as id_total_articles, u.username, COUNT(a.id) AS total_articles FROM users u
LEFT JOIN articles a ON u.id = a.user_id
GROUP BY u.id, u.username
) as AC
left join
(
SELECT u.id as id_total_comments, COUNT(c.id) AS total_comments FROM users u
LEFT JOIN comments c ON u.id = c.user_id
GROUP BY u.id, u.username
) as CC
ON AC.id_total_articles = CC.id_total_comments;
use u.id, u.username both column in group by
SELECT u.id, u.username, COUNT(a.id) AS total_articles,
COUNT(c.id) AS total_comments FROM users u
LEFT JOIN articles a ON u.id = a.user_id
LEFT JOIN comments c ON a.id = c.article_id
GROUP BY u.id, u.username
If what you want is the number of articles for a user, and the total number of comments on all the articles of the user - then this is your query:
SELECT
u.id,
u.username,
COUNT(DISTINCT a.id) AS total_articles,
COUNT(c.id) AS total_comments
FROM users u
LEFT JOIN articles a
ON u.id = a.user_id
LEFT JOIN comments c
ON a.id = c.article_id
GROUP BY
u.id,
u.username;
But - if you are looking for the number of comments (just a thought here) of the user - then you want to join the comments table on the user id, and not the article id.
You are missing the u.username in the group by, also COUNT(a.id) must change into COUNT(distinct a.id):
SELECT u.id, u.username, COUNT(distinct a.id) AS total_articles, COUNT(c.id) AS total_comments FROM users u
LEFT JOIN articles a ON u.id = a.user_id
LEFT JOIN comments c ON a.id = c.article_id
GROUP BY u.id, u.username;
Update:
However, I guess that what you actually need is something other than your proposed query. You said that you need the total number of articles for each user and the total number of comment for each article. That means you need two separate queries:
SELECT a.id article_id , COUNT(c.id) AS total_comments
FROM articles a
LEFT JOIN comments c ON a.id = c.article_id
GROUP BY a.id
SELECT u.id, u.username, COUNT(distinct a.id) AS total_articles
FROM users u
LEFT JOIN articles a ON u.id = a.user_id
GROUP BY u.id, u.username;
You are aggregating along related dimensions and getting overcounting.
One approach is to use multiple aggregations:
SELECT u.id, u.username, COUNT(a.id) AS total_articles,
SUM(c.num_comments) AS total_comments
FROM users u LEFT JOIN
articles a
ON a.user_id = a.id LEFT JOIN
(SELECT c.article_id, COUNT(c.id) as num_comments
FROM comments c
GROUP BY c.article_id
) c
ON a.id = c.article_id
GROUP BY u.id, u.username;

mysql join with multiple tables and count query

I have total 6 tables in which different info has been saved
Now i need a result in which get count from 5 tables and select all info from main table but if record does not exist than it must be need to return 0 instead of no row found that's the problem here
I have tried below query but didn't get success
SELECT
u.*,
COUNT(DISTINCT c.id) as comments,
COUNT(DISTINCT d.id) as dislikes,
COUNT(DISTINCT l.id) as likes,
COUNT(DISTINCT s.id) as shares,
COUNT(DISTINCT t.id) as tags
FROM
job_details as u
JOIN job_comments as c ON u.id = c.job_id
JOIN job_dislike as d ON u.id = d.job_id
JOIN job_like as l ON u.id = l.job_id
JOIN job_share as s ON u.id = s.job_id
JOIN job_tags as t ON u.id = t.job_id
WHERE
u.id = c.job_id AND
u.id = d.job_id AND
u.id = l.job_id AND
u.id = s.job_id AND
u.id = t.job_id
GROUP BY
u.id
This query is executed, but didn't get exact result.
I don't quite understand why.
I was hoping somebody here could help me out?
Thanks!
You probably didn't get the exact result because some tables may be missing values.
Although you can solve this problem with a LEFT JOIN, the safer solution is to pre-aggregate the data:
SELECT u.*, c.comments, d.dislikes, l.likes, s.shares, t.tags
FROM job_details as u LEFT JOIN
(select c.job_id, count(*) as comments from job_comments group by c.job_id
) c
ON u.id = c.job_id LEFT JOIN
(select d.job_id, count(*) as dislikes from job_dislike d group by d.job_id
) d
ON u.id = d.job_id LEFT JOIN
(select l.job_id, count(*) as likes from job_like l group by l.job_id
) l
ON u.id = l.job_id LEFT JOIN
(select s.job_id, count(*) as shares from job_share s group by s.job_id
) s
ON u.id = s.job_id LEFT JOIN
(select t.job_id, count(*) as tags from job_tags t group by t.job_id
) t
ON u.id = t.job_id;
Why is this better? Consider an id that has 5 comments, likes, dislikes, shares and tags. The JOIN approach produces an intermediate result with 5*5*5*5*5 = 3,125 intermediate rows. Things can really get out of hand for popular ids.
Use LEFT JOIN instead of JOIN. and you don't need WHERE clause since you have joined those tables. And, use IFNULL function to return 0 for null values. You need to modify you query like this :
SELECT u.id,
IFNULL(COUNT(DISTINCT c.id),0) as comments,
IFNULL(COUNT(DISTINCT d.id),0) as dislikes,
IFNULL(COUNT(DISTINCT l.id),0) as likes,
IFNULL(COUNT(DISTINCT s.id),0) as shares,
IFNULL(COUNT(DISTINCT t.id),0) as tags
FROM job_details as u
LEFT JOIN job_comments as c ON u.id = c.job_id
LEFT JOIN job_dislike as d ON u.id = d.job_id
LEFT JOIN job_like as l ON u.id = l.job_id
LEFT JOIN job_share as s ON u.id = s.job_id
LEFT JOIN job_tags as t ON u.id = t.job_id
GROUP BY u.id

Add additional joins to already joined table in SQL

I have a existing query where I am using joins (thanks RADAR) to get my data.
Working SQL
SELECT
IFNULL(f.field_full_name_value, 'No Value'), u.name, u.uid, n.title, n.nid, a.timestamp, d.field_video_duration_value AS duration
FROM
db_node_view_count a
join db_node n
ON a.nid = n.nid
JOIN db_field_data_field_video_duration d
ON n.nid = d.entity_id
JOIN db_users u
ON a.uid = u.uid
AND u.uid <> 1
LEFT JOIN db_field_data_field_full_name f
ON u.uid = f.entity_id
ORDER BY u.uid desc
What I want to do is that I want to extend the query to show roles of a db_users u. There is a table called db_roles, which contain all role names with a primary key rid. Then the second table is db_users_roles, which contains a matching uid (from db_users u) and rid to show which user selected which role.
So what I did was that under ON a.uid = u.uid, I added JOIN db_users_roles ur ON u.uid = ur.uid JOIN db_role r ON ur.rid = r.rid. It works fine but it shows duplicate rows. Any idea why it's happening?
SELECT
IFNULL(f.field_full_name_value, 'No Value'), r.name, u.name, u.uid, n.title, n.nid, a.timestamp, d.field_video_duration_value AS duration
FROM
db_node_view_count a
join db_node n
ON a.nid = n.nid
JOIN db_field_data_field_video_duration d
ON n.nid = d.entity_id
JOIN db_users u
ON a.uid = u.uid
LEFT JOIN db_users_roles ur
ON u.uid = ur.uid
LEFT JOIN db_role r
ON ur.rid = r.rid
AND u.uid <> 1
LEFT JOIN db_field_data_field_full_name f
ON u.uid = f.entity_id
ORDER BY u.uid desc
UPDATE
With help from Joe, here is a slight update:
SELECT
IFNULL(f.field_full_name_value, 'No Value'), GROUP_CONCAT(r.name), u.name, u.uid, n.title, n.nid, a.timestamp, d.field_video_duration_value AS duration
FROM
db_node_view_count a
join db_node n
ON a.nid = n.nid
JOIN db_field_data_field_video_duration d
ON n.nid = d.entity_id
JOIN db_users u
ON a.uid = u.uid
LEFT JOIN db_users_roles ur
ON u.uid = ur.uid
LEFT JOIN db_role r
ON ur.rid = r.rid
AND u.uid <> 1
LEFT JOIN db_field_data_field_full_name f
ON u.uid = f.entity_id
GROUP BY f.field_full_name_value
ORDER BY u.uid desc
Some users in your (Drupal) database may have more than one role. So where your original query had (simplified a bit) one row per node, the modified query will duplicate the rows for each role of the user.
You might want to modify the query to include
GROUP BY n.nid, u.uid
and change the SELECT field list to include GROUP_CONCAT(r.name) rather than r.name.

SQL Join with MAX().

I have two tables, users and contestants. I'm trying to select the max contestant ID that has a profile picture(which is on the user table)
Heres my terrible SQL:
SELECT u.thumbnail, u.id FROM users AS u
INNER JOIN
(
SELECT c.id, c.user_id FROM contestants AS c
WHERE u.id = c.users_id
AND c.id = (select max(c.id))
) WHERE u.thumbnail IS NOT NULL
The error currently is: #1248 - Every derived table must have its own alias.
This confuses me since Users has an alias of u, and contestants has an alias of c..
What am I doing wrong here? I'm guessing a lot so some help would be really appreciated!
Whenever you are performing a join operation, you are actually joining two table. The subquery you wrote here, for instance, is working as a separate table. Hence, you have to use an alias to this table. That's the reason behind your error message.
Your query:
SELECT u.thumbnail, u.id FROM users AS u
INNER JOIN
(
SELECT c.id, c.user_id FROM contestants AS c
WHERE u.id = c.users_id
AND c.id = (select max(c.id))
) WHERE u.thumbnail IS NOT NULL
It should contain an alias for the subquery:
SELECT c.id, c.user_id FROM contestants AS c
WHERE u.id = c.users_id
AND c.id = (select max(c.id))
Let's say, it's T.
So, your query now becomes:
SELECT u.thumbnail, u.id FROM users AS u
INNER JOIN
(
SELECT c.id, c.user_id FROM contestants AS c
WHERE u.id = c.users_id
AND c.id = (select max(c.id))
) AS T
WHERE u.thumbnail IS NOT NULL
But what you are trying to achieve, can actually be done in a neater way:
SELECT u.thumbnail, u.id, max(c.id),
FROM users as u
LEFT JOIN contestants as c
on u.id = c.user_id
WHERE u.thumbnail IS NOT NULL
Why make all the fuss when you have a better and neater approach at your disposal?
try this:
SELECT u.thumbnail, u.id
FROM users AS u
INNER JOIN
(
SELECT c.id, c.user_id FROM contestants AS c
WHERE u.id = c.users_id
AND c.id = (select max(c.id))
)A
WHERE u.thumbnail IS NOT NULL
i think this should be simple,
SELECT u.thumbnail, u.id
FROM users u
INNER JOIN contestants c
ON u.id = c.users_id
WHERE u.thumbnail IS NOT NULL
ORDER BY c.id DESC
LIMIT 1
This is very simple.
SELECT user.thumbnail, user.id
FROM users user
INNER JOIN contestants cont ON cont.id = cont.users_id
WHERE cont.thumbnail IS NOT NULL
ORDER BY user.id DESC