Best way to select SUM and a unique value from table - mysql

I have an SQL query which calculates a sum of values for a certain column and also needs to retrieve a single value, both from the same table.
I was wondering, since the whole table will be read to calculate the sum, is there a way I can select the wanted value at the same moment ?
Is this possible or do I need to make 2 distinct SELECTs on the table as shown below?
The current code from my query is :
SELECT [columns],
(SELECT SUM(value) FROM votes WHERE postid=p.postid) AS charge,
(SELECT value FROM votes WHERE postid=p.postid AND userid=u.userid) AS currentVote
FROM posts p, users u WHERE p.userid=u.userid
Thanks !

In your specific case it is possible as you want the SUM of the value for votes related to the posts for a specific user.
Instead of using nested queries you could use something like this:
Based on the Comments I have updated the query:
SQL Fiddle: http://www.sqlfiddle.com/#!2/2a0920/9
SELECT p.postid, sum(v.value) AS charge,
v.value AS currentVote
FROM posts p,
users u,
votes v
WHERE p.userid=u.userid
AND v.postid = p.postid
group by p.postid;

Related

use COUNT(*) values from one table to another

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.

MySQL COUNT with and without a HAVING clause

I need to know the COUNT of a query, and also the same query with a HAVING clause
By way of illustration (not the real case, but illustrative), if table pupilTable contains a list of pupil and their class, and I want to know the number of classes, and also the number of classes with more than 25 children:
I know I can get the total number of classes using:
SELECT COUNT( * ) AS NumAllClasses FROM pupilTable GROUP BY class
I know I can get the number of big classes with the condition using:
SELECT COUNT( * ) AS NumBigClasses FROM pupilTable GROUP BY class HAVING COUNT(pupil) > 25
Is there a simple (single query) way of getting both at the same time? Google has not been my friend :(
Edited to add illustrative data
Without any verification:
SELECT COUNT(*) AS count1 FROM table WHERE condition UNION ALL
SELECT COUNT(*) AS count2 FROM table WHERE condition HAVING second-condition
But note that i guess the second label will get lost so you will have both count-nrs returned under the label count1
You can user the following query:
select count(users.id) count1, count(u.id) count2 from users left join users u on users.id = u.id and u.fname = "abc" where users.id > 10
Here, I have used two conditions. You need to make relevant changes in this query to get desired output.
Here, user. and u. are the conditions.
In above query,
user.<condition1> : u.fname = "abc"
u.<condition2> : users.id > 10

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

MySQL Left Joins

EDIT: OK, think I need to be clearer - I'd like the result to show all the 'names' that appear in the table acme, against the counts (if any) from the results table. Hope that makes sense?
Having a huge issue and my brain isn't working as it should.
All I want to do is, in a single statement via a join, count the number of rows for a common field.
SELECT name, COUNT(name) as Count FROM acme
SELECT name, COUNT(name) as Total FROM results
I'm sure it should be something like this...
SELECT acme.name, COUNT(acme.name) As Count,
COUNT(results.name) as Total
FROM acme
LEFT JOIN results ON acme.name = results.name
GROUP BY name
ORDERY BY name
But it doesn't bring back the correct counts.
Thoughts, where am I going wrong...this, I know, will be very very obvious.
H.
From your feedback, this will get what you want. You need to FIRST get unique names / counts from the "ACME" file first... THEN join that to the results table for count of records from that, otherwise, you would end up with a Cartesian result of counts. If ACME had Name "X" 5 times and Results had "X" 20 times, your total would be 100. The query below will actually result with a single row showing "X", 5, 20 which is what it appears you are looking for.. (for however many names exist in ACME).
I've changed to a LEFT join in case there are names in the ACME table that DO NOT exist in the RESULTS table, it won't drop them from your final answer
select
JustACME.Name,
JustACME.NameCount,
COALESCE( COUNT( * ), 0 ) as CountFromResultsTable
from
( select a.Name
count(*) as NameCount
from
acme a
group by
a.Name ) JustACME
LEFT JOIN results r
on JustACME.Name = r.Name
group by
JustACME.Name
It looks like it's because of the join, it's screwing with your counts. Try running the join with SELECT * FROM... and look at the resulting table. The problem should be obvious from there. =D
Yes, your join (inner or outer, doesn't matter) is messing with your results.
In fact, it is likely returning the product of rows with the same name, rather than the sum.
What you want to do is sum the rows from the first table, sum the rows from the second table, and join that.
Like this:
Select name, a.count as Count, r.count as Total
From (select name, count(*) from acme group by name) a
Left join (select name, count(*) from results group by name) r using (name)
I do not see why you forbid using two statements this just complicates everything.
The only reason I see for this is to get the two results into one answer.
I do not know if the latter would work but I would try this:
SET #acount = (SELECT count(DISTINCT name) FROM acme);
SELECT count(DISTINCT name) as Total, #acount as Count FROM results
I would post this as one query and (hopefully) get back the correct results. Let me note, that it is not clear from you question if you want to know how often every name doubles or if you want to count unique names.