use COUNT(*) values from one table to another - mysql

Suppose I have two tables, users and posts. Posts has the following fields, userid, postid, etc and userid can appear multiple times as one user can write multiple posts....I'm just trying sort the users table based off the # of occurrences per userid in the posts table. I can get the # of occurrences per user using this
SELECT userid, COUNT(*)
FROM posts
GROUP BY userid;
I would like to use the values under COUNT(*) column, maybe add it to my other table because then I can simply to something like this
SELECT * FROM users
ORDER BY newcolumn ASC;
but I'm having trouble doing that. Or can I do it without having to add an extra column? Hints please. Thanks

Left join is the key here!
SELECT users.userid,count(posts.userid) AS total_count
FROM users
LEFT JOIN posts on posts.userid = users.userid
GROUP BY users.userid
ORDER BY total_count DESC;
We are taking the left join on two tables with same user_id and we are counting the total number of posts per user using group by. Finally sort by count and show results.

try an left join:
select users.userid, [user fields],count(postid) as posts_count
from users
left join posts on posts.userid = users.userid
group by users.userid,[user fields]
order by posts_count desc.

You want to select users (FROM users) but you want to sort based on criteria in another table (COUNT(*) FROM posts) -- therefore you need to use a JOIN
Off-hand I can't seem to recall if "JOIN" or "RIGHT JOIN" or "FULL JOIN" is what you need if you wanted to get a cartesian product of the tables then group and aggregate on a single field, but I can avoid the need to remember with a subquery (hopefully someone will soon post a smaller and smarter answer):
SELECT users.* FROM users
JOIN (
SELECT userid, COUNT(*) as count
FROM posts
GROUP BY userid
) as subquery ON users.id = subquery.userid
ORDER BY subquery.count
Note: I haven't tested this query, but it looks good to me. Again: hopefully someone will post a better answer soon as I'm not doing my due dilligence, but you definitely need a JOIN :)

You could add a post_count column to the users table, but you would also have to update that count column every time a user creates a new post and you would have to build that logic into your application.
Otherwise, it looks like the answer from FallAndLearn will get you what you need.

Related

SQL Query: Must go through table, COUNT certain entries, and use that result as Column name in rest of Query

I am having trouble making a query which must behave as following:
Imagine a table named "Users" with these column names: id, name, vote.
The vote column holds the id of a row. For example, if I am id = 0, and you are id = 1, I wish to vote for you so my vote entry holds "1", you wish to vote for yourself so your's too holds "1".
I want a query which returns these columns: id, name, vote, totalVotes.
So it should count all the votes that have your id and place that number under total votes.
My "totalVotes" would be "0", and your's would be "2".
The problem is that I do not understand how to go through the entire table, calculate the total number of votes for a user, and then repeat for every user.
Any tips would be greatly appreciated and if it is difficult to understand feel free to tell me what part I should word better.
select
u1.id, u1.name, u1.vote,
ifnull(u2.totalVotes, 0) as totalVotes
from Users as u1
left outer join (
select u2.vote as id, count(*) as totalVotes
from Users as u2
group by u2.vote
) as u2 on u2.id = u1.id
sql fiddle demo
a bit of explanation:
I'm using a subquery, grouping records from Users by vote, so I'll have a table where I have count of votes for each user
After that join Users with this table
in the final recordset I'm using ifnull to display 0 for users who have no votes.
This, I believe, the fastest way to do this query in MySQL (without window functions), here you can see sql fiddle for only 1280 rows with my query and Carter's one using subquery.
sql fiddle demo
Results:
My query: 4ms
Carter's query with subquery: 832ms
This would be one way...
select
u1.id,
u1.name,
u1.vote,
(select count(*) from Users as u2 where u2.vote = u1.id) as totalVotes
from
Users as u1;
See: this SQL Fiddle for an exmaple.
Addendum: As Roman points out, when it comes to efficiency, his answer is clearly the better of our two. In general joins are more efficient than subqueries.

SQL query is not retrieving all the fields

I have to tables in my database, the first one (participants) look just like that:
And I have another called votes in which I can vote for any participants.
So my problem is that I'm trying to get all the votes of each participant but when I execute my query it only retrieves four rows sorted by the COUNT of votes, And the other remaining are not appearing in my query:
SELECT COUNT(DISTINCT `votes`.`id`) AS count_id, participants.name
AS participant_name FROM `participants` LEFT OUTER JOIN `votes` ON
`votes`.`participant_id` = `participants`.`id` GROUP BY votes.participant_id ORDER BY
votes.participant_id DESC;
Retrieves:
I think the problem is that you're grouping by votes.participant_id, rather than participants.id, which limits you to participants with votes, the outer join notwithstanding. Check out http://sqlfiddle.com/#!2/c5d3d/5/0
As what i have understood from the query you gave you were selecting unique id's from the votes table and I assume that your column id is not an identity. but it would be better if that would be an identity? and if so, here is my answer.replace your select with these.
Select count (votes.participant.id) as count_id ,participants.name as participant_name
from participants join votes
on participants.id = vote.participant_id
group by participants.name
order by count_id
just let me know if it works
cheers

MySQL - 3 tables, is this complex join even possible?

I have three tables: users, groups and relation.
Table users with fields: usrID, usrName, usrPass, usrPts
Table groups with fields: grpID, grpName, grpMinPts
Table relation with fields: uID, gID
User can be placed in group in two ways:
if collect group minimal number of points (users.usrPts > group.grpMinPts ORDER BY group.grpMinPts DSC LIMIT 1)
if his relation to the group is manually added in relation tables (user ID provided as uID, as well as group ID provided as gID in table named relation)
Can I create one single query, to determine for every user (or one specific), which group he belongs, but, manual relation (using relation table) should have higher priority than usrPts compared to grpMinPts? Also, I do not want to have one user shown twice (to show his real group by points, but related group also)...
Thanks in advance! :) I tried:
SELECT * FROM users LEFT JOIN (relation LEFT JOIN groups ON (relation.gID = groups.grpID) ON users.usrID = relation.uID
Using this I managed to extract specified relations (from relation table), but, I have no idea how to include user points, respecting above mentioned priority (specified first). I know how to do this in a few separated queries in php, that is simple, but I am curious, can it be done using one single query?
EDIT TO ADD:
Thanks to really educational technique using coalesce #GordonLinoff provided, I managed to make this query to work as I expected. So, here it goes:
SELECT o.usrID, o.usrName, o.usrPass, o.usrPts, t.grpID, t.grpName
FROM (
SELECT u.*, COALESCE(relationgroupid,groupid) AS thegroupid
FROM (
SELECT u.*, (
SELECT grpID
FROM groups g
WHERE u.usrPts > g.grpMinPts
ORDER BY g.grpMinPts DESC
LIMIT 1
) AS groupid, (
SELECT grpUID
FROM relation r
WHERE r.userUID = u.usrID
) AS relationgroupid
FROM users u
)u
)o
JOIN groups t ON t.grpID = o.thegroupid
Also, if you are wondering, like I did, is this approach faster or slower than doing three queries and processing in php, the answer is that this is slightly faster way. Average time of this query execution and showing results on a webpage is 14 ms. Three simple queries, processing in php and showing results on a webpage took 21 ms. Average is based on 10 cases, average execution time was, really, a constant time.
Here is an approach that uses correlated subqueries to get each of the values. It then chooses the appropriate one using the precedence rule that if the relations exist use that one, otherwise use the one from the groups table:
select u.*,
coalesce(relationgroupid, groupid) as thegroupid
from (select u.*,
(select grpid from groups g where u.usrPts > g.grpMinPts order by g.grpMinPts desc limit 1
) as groupid,
(select gid from relations r where r.userId = u.userId
) as relationgroupid
from users u
) u
Try something like this
select user.name, group.name
from group
join relation on relation.gid = group.gid
join user on user.uid = relation.uid
union
select user.name, g1.name
from group g1
join group g2 on g2.minpts > g1.minpts
join user on user.pts between g1.minpts and g2.minpts

is it possible to have group by sort in any order other than ascending?

I have a query that joins a couple of tables and produces a lot of rows, in a situation where I only wish to see one row per user. I have solved the "only one user" problem by using group by user.id, however, I'm noticing that now for each user I get the values from the joined table that represent the first entry (rather than the last).
so in other words
user:
id | phone
item:
id | user_id | timestamp
my intention is to join these tables and select the latest item (based on timestamp, or item.id desc) but to only get one item per user (rather than see all the items that each user has). group by user.id solves the problem of giving me just one item per user, but they always turn up with the first item that has the lowest item.id, whereas I want the most recent one.
Is there a better way to achieve this... I was initially noodling with distinct but that doesn't seem to do the trick.
TIA
[EDIT]
In response to Jocelin's question below:
select user.id, item.timestamp from item join user on user.id = item.user_id order by user.id
mysql has a "cheat" for getting the first row only of each group:
select *
from (select
u.id as user_id,
u.name,
i.id as item_id,
i.timestamp
from user u
join item i on i.user_id = u.id
order by timestamp desc -- "desc" = order descending
) x
group by user_id
The "cheat" is that mysql allows you not aggregate the non-group by columns (unlike every other database I know). Instead of giving an SQL syntax error, it returns the first record only of each group. Not SQL standard, but very handy.
The beauty of this is that you don't need any correlated subqueries, which are dreadfully slow.
The inner query is used to order the records in timestamp latest-first order, so the first record encountered for each user is the "most recent".
Use an ORDER BY with DESC clause:
select
user.id, item.timestamp
from
item
join
user on user.id = item.user_id
group by
user.id
order by
user.id DESC
I'm suspecting (from your comments below) that your issue isn't really what you describe. Try something like this instead (untested, because I don't use MySQL - it works in SQL Server and SQLite):
select
user.id, i.ts
from
(select id, max(timestamp) as ts from items group by id) i
join
user u on u.id = i.id
You will need to use a JOIN. First create a subselect grouping by user and selecting the MAX of timestamp. This gets you one row per user, with the latest entry (and loses all other fields).
Then join it back using the timestamp in the JOIN expression, to restore the missing fields.
Sorry, but without the table schema it isn't easy to be more specific than this. Hope that the JOIN reference points you in the right direction to modify your query.

MySQL returning results from one table based on data in another table

Before delving into the issue, first I will explain the situation. I have two tables such as the following:
USERS TABLE
user_id
username
firstName
lastName
GROUPS TABLE
user_id
group_id
I want to retrieve all users who's first name is LIKE '%foo%' and who is a part of a group with group_id = 'givengid'
So, the query would like something like this:
SELECT user_id FROM users WHERE firstName LIKE '%foo'"
I can make a user defined sql function such as ismember(user_id, group_id) that will return 1 if the user is a part of the group and 0 if they are not and this to the WHERE clause in the aforementioned select statement. However, this means that for every user who's first name matches the criteria, another query has to be run through thousands of other records to find a potential match for a group entry.
The users and groups table will each have several hundred thousand records. Is it more conventional to use the user defined function approach or run a query using the UNION statement? If the UNION approach is best, what would the query with the union statement look like?
Of course, I will run benchmarks but I just want to get some perspective on the possible range of solutions for this situation and what is generally most effective/efficient.
You should use a JOIN to get users matching your two criteria.
SELECT
user_id
FROM
users
INNER JOIN
groups
ON groups.user_id = users.users_id
AND groups.group_id = given_id
WHERE
firstName LIKE '%foo'
You don't need to use either a UNION or a user-defined function here; instead, you can use a JOIN (which lets you join one table to another one based on a set of equivalent columns):
SELECT u.user_id
FROM users AS u
JOIN groups AS g
ON g.user_id = u.user_id
WHERE g.group_id = 'givengid'
AND u.firstName LIKE '%foo'
What this query does is join rows in the groups table to rows in the users table when the user_id is the same (so if you were to use SELECT *, you would end up with a long row containing the user data and the group data for that user). If multiple groups rows exist for the user, multiple rows will be retrieved before being filtered by the WHERE clause.
Use a join:
SELECT DISTINCT user_id
FROM users
INNER JOIN groups ON groups.user_id = users.user_id
WHERE users.firstName LIKE '%foo'
AND groups.group_id = '23'
The DISTINCT makes sure you don't have duplicate user IDs in the result.