Group by gives error "Invalid use of group function" - mysql

I'd like to get the number of likes each user got from the following tables:
Users table: contains the userid, email, contact no
Like table: which contains picture ids, and userids who liked it
Picture posted table*: contains picture ids, user id who posted it
I am using the following query which is giving the error "Invalid use of group function":
select sum(count(pid)) from p_like where pid in (
select pid from p_picuser where userid in (
SELECT userid from p_users
)
) GROUP BY pid
What am I doing wrong?

You can't aggregate (sums, counts, etc) the same variables on which you're grouping by. If you'd like counts by user, group on that. So maybe try something like:
SELECT userid, count(*) from p_like GROUP BY user
to get the like-count's by userId from your p_like table. Strictly-speaking, this your answer.
To add more user details then, you can make that a sub-query and join to you p_user table, e.g.
SELECT email, like_count from p_users pusers, (
SELECT userid, count(*) like_cound from p_like GROUP BY user
) group_sub
WHERE p_users.userid = group_sub.userid

Related

SQL query with most recent name and total count

I already have a table, "table_one", set up on phpMyAdmin that has the following columns:
USER_ID: A discord user ID (message.author.id)
USER_NAME: A discord username (message.author.name)
USER_NICKNAME: The user's display name on the server (message.author.display_name)
TIMESTAMP: A datetime timestamp when the message was entered (message.created_at)
MESSAGE CONTENT: A cleaned input keyword to successful completion of content, just for this example consider "apple" or "orange" as the two target keywords.
What I'd like as a result is a view or query that returns a table with the following:
The user's most recent display name (USER_NICKNAME), based off the most recent timestamp
The total number of times a user has entered a specific keyword. Such as confining the search to only include "apple" but not instances "orange"
My intention is that if a user entered a keyword 10 times, then changed their server nickname and entered the same keyword 10 more times, the result would show their most recent nickname and that they entered the keyword 20 times in total.
This is the closest I have gotten to my desired result so far. The query correctly groups instances where user has changed their nickname based on the static discord ID, but I would like it to retain this functionality while instead showing the most recent USER_NICKNAME instead of a USER_ID:
SELECT USER_ID, COUNT(USER_ID)
FROM table_one
WHERE MESSAGE_CONTENT = 'apple'
GROUP BY USER_ID
I don't think there is an uncomplicated way to do this. In Postgres, I would use the SELECT DISTINCT ON to get the nickname, but in MySQL I believe you are limited to JOINing grouped queries.
I would combine two queries (or three, depending how you look at it).
First, to get the keyword count, use your original query:
SELECT USER_ID, COUNT(USER_ID) as apple_count
FROM table_one
WHERE MESSAGE_CONTENT = 'apple'
GROUP BY USER_ID;
Second, to get the last nickname, group by USER_ID without subsetting rows and use the result as a subquery in a JOIN statement:
SELECT a.USER_ID, a.USER_NICKNAME AS last_nickname
FROM table_one a
INNER JOIN
(SELECT USER_ID, MAX(TIMESTAMP) AS max_ts
FROM table_one
GROUP BY USER_ID) b
ON a.USER_ID = b.USER_ID AND TIMESTAMP = max_ts
I would then JOIN these two, using a WITH statement to increase the clarity of what's going on:
WITH
nicknames AS
(SELECT a.USER_ID, a.USER_NICKNAME AS last_nickname
FROM table_one a
INNER JOIN
(SELECT USER_ID, MAX(TIMESTAMP) AS max_ts
FROM table_one
GROUP BY USER_ID) b
ON a.USER_ID = b.USER_ID AND TIMESTAMP = max_ts),
counts AS
(SELECT USER_ID, COUNT(USER_ID) AS apple_count
FROM table_one
WHERE MESSAGE_CONTENT = 'apple'
GROUP BY USER_ID)
SELECT nicknames.USER_ID, nicknames.last_nickname, counts.apple_count
FROM nicknames
INNER JOIN counts
ON nicknames.USER_ID = counts.USER_ID;

Trying to create an sql subquery

I'm new to mysql and am trying to write a subquery where I find the player and their phonenumber with the earliest creation date. I'm stuck with getting the subquery to work as the error 'Invalid use of group function' keeps displaying. What am I doing wrong?
SELECT username, phoneNumber
FROM phonenumber
WHERE username = (SELECT username
FROM player
WHERE MIN(creationDateTime));
if you want to use subquery then get user with min creation date. Then use this user name to get phone number. you can use below query. I am assuming that that will be only one user with min creation date as mentioned by you.
SELECT username, phoneNumber
FROM phonenumber
where username =
(
select username from player
where creationDateTime =
(
SELECT min(creationDateTime) from player
)
)
There are other better option than subquery.
SELECT PN.username, PN.phoneNumber, PL.min_creationDateTime FROM phonenumber AS PN
LEFT JOIN (SELECT username, MIN(creationDateTime) AS min_creationDateTime FROM player GROUP BY username) AS PL
ON PN.username = A.username
It should give you the result you're looking for.
If you want one player, I would suggest order by and limit:
SELECT pn.username, pn.phoneNumber
FROM phonenumber pn
WHERE pn.username = (SELECT p.username
FROM player p
ORDER BY creationDateTime
LIMIT 1
);

Cannot get last item from table with mysql group by

I am trying to make a mysql query that receives the last item in a group by clause. However, when I group by, it gives me the first record. I tried many joins, and many searches, but cannot find the right way to go about it.
My database is designed like so:
messages:
message_id *
thread_id
user_id
message
created_at
users:
user_id*
name
threads:
thread_id*
name
usersthreads:
id*
user_id
thread_id
The query I am trying to make should be, provided a user_id,:
threads.thread_id
users.name
messages.message
messages.created_at
The catch here is this, for users.name, I need not the provided user's name, but the other user's name that is in the thread. My current stab at it right now stands as this:
select t.thread_id as thread, t.name as name, messages.message as message, max(messages.created_at) as date
from (SELECT threads.thread_id, nt.name
FROM threads,messages,
(SELECT users.name as name, usersthreads.thread_id as threadID
from users,usersthreads
where users.user_id=usersthreads.user_id
and users.user_id!='$userID') as nt
WHERE threads.thread_id=messages.thread_id
and nt.threadID=threads.thread_id
AND messages.user_id=$userID
GROUP BY messages.thread_id) as t
inner join messages
on t.thread_id = messages.thread_id
group by t.thread_id
order by date DESC
And the problem with this query is that it returns what I need, but the message that is returned per each thread is the first message of the thread, not the last ("most recent") message. Thank you.

mysql group and sort without using order by on outter most

I am trying to return a list of users and their last login date. I need to get something that doesn't effect my outter order by statement because its dynamically populated by parameters the users chooses to sort the list. The other thing is the table that stores the login times requires 2 fields to link to the user table a user_id and a user_type.
table_admin
field: id, name
table_logs
field: id, user_id, user_type, login_date
*table_admin.id = table_logs.user_id
table_logs.user_type needs to be "admin"*
I need to pull all the admin users from table_admin with their last login date (it also needs to work if there is no record in the log table), the user_type in the log table would be "admin".
Thank you for your time.
SELECT SQL_CALC_FOUND_ROWS admins.*,ld2.ip,ld2.login_date as last_login
FROM admins
LEFT JOIN (
SELECT * FROM log_logins WHERE user_type = "admin" ORDER BY login_date DESC
) as ld2 ON (ld2.user_id = admins.id)
WHERE 1
GROUP BY user_id
ORDER BY admins.id DESC LIMIT 0,40
This is what I have so far but it doesn't grab results if they do not have an entry in the log table.
This is a join with an aggregation:
select a.name, 'admin' as userType, max(l.login_date) as lastLoginDate
from admin a left outer join
logs l
on a.id = l.id and
l.user_type = 'admin'
group by a.name

Help optimizing this mysql query

Well friends, I have got this query which works but is very long for the execution. I was wondering whether there is a faster way to achieve this.
SELECT id, email FROM member WHERE email IN
(SELECT email FROM member GROUP BY email HAVING count( * ) >1 )
ORDER BY `email` ASC
Basically, there are entries where the same email is appearing more than once, and I just wanted to have those rows returned where there is duplicate entries of 'email'.
The above query works in that direction, but is painfully long.
Cheers.
You can group the results first, then join them to the member table to insure only rows with duplicate emails will show.
SELECT m.id, m.email
FROM member m JOIN (
SELECT email
FROM member
GROUP BY email
HAVING COUNT(*) > 1
) g ON m.email = g.email
ORDER BY m.email ASC
Your query is slow because of the nested select, which gets recomputed for every row. The best solution is to rewrite your algorithm a bit so that you can use a query like this:
SELECT id, email
FROM member GROUP BY email
HAVING count( * ) >1
ORDER BY `email`
Unfortunately, the id you get back will be a random choice among each group. This may be a more useful query:
SELECT GROUP_CONCAT(id), email
FROM member GROUP BY email
HAVING count( * ) >1
ORDER BY `email`
Can you do this in two stages? First create a temporary table containing all the emails with > 1 occurance, then join the main table to the temp table through the email field...
If your member table has an index on the email field, this should be pretty fast.
CREATE TEMPORARY TABLE ddd
SELECT email, count(*) as cnt FROM member GROUP BY email HAVING cnt>1;
SELECT * FROM ddd
INNER JOIN member USING (email);
You are doing two queries when you only need to do one
SELECT id, email
FROM member
GROUP BY email
HAVING count( * ) > 1
ORDER BY `email` ASC
select id,email,count(*) as n from member group by id having n > 1;