SQL query with most recent name and total count - mysql

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;

Related

How can I get customer data based on the number of users they have?

I want to get customer data from all the businesses with more than 1 user.
For this I think I need a subquery to count more than 1 user and then the outer query to give me their emails.
I have tried subqueries in the WHERE and HAVING clause
SELECT u.mail
FROM users u
WHERE count IN (
SELECT count (u.id_business)
FROM businesses b
INNER JOIN users u ON b.id = u.id_business
GROUP BY b.id, u.id_business
HAVING COUNT (u.id_business) >= 2
)
I believe that you do not need a subquery, everything can be achieved in a joined aggregate query with a HAVING clause, like :
SELECT u.mail
FROM users u
INNER JOIN businesses b on b.id = u.id_business
GROUP BY u.id, u.email
HAVING COUNT (*) >= 2
NB : in case several users may have the same email, I have added the primary key of users to the GROUP BY clause (I assumed that the pk is called id) : you may remove this if email is a unique field in users.

SQL Returning Double Rows

I have 2 tables in:
users (user_id, fname, lname, department) and clock (id, punchType, punchTime, comment, user_id).
The SQL query below pulls 2 rows for some records and I can't figure out why. Any insight would be helpful.
SELECT user.user_id, user.fname, user.lname, user.department, punchType, punchTime, comment
FROM user
INNER JOIN (
SELECT *
FROM clock
WHERE punchTime IN (
SELECT MAX(punchTime)
FROM clock
GROUP BY user_id
)
) AS a
ON user.user_id = a.user_id
Because different users can have the same punch time. One user's punchtime could be another users maximum punchtime. Here is one fix:
SELECT *
FROM clock
WHERE (user_id, punchTime) IN (
SELECT user_id, MAX(punchTime)
FROM clock
GROUP BY user_id
);
This could also be fixed with correlated subqueries and other methods.
You will notice that when you subquery by punchTime alone, you can end up with duplicate records per user. What happens is if any of a user's punchTimes match a max punch time, they stay in the set. So, if a user has a max time that matches someone else's max time, or the users has two+ records that represent their own max punch time, you will be joining multiple rows of the same user_id from clock with user table.
For example:
SELECT
user_id,
MAX(punchTime) as real_max_time,
COUNT(1) as dupe_count,
COUNT(DISTINCT(punchTime)) as unique_punchTimes
COUNT(DISTINCT(punchType)) as unique_punchTypes
FROM clock
WHERE punchTime IN (
SELECT MAX(punchTime)
FROM clock
GROUP BY user_id
)
GROUP BY
user_id
HAVING COUNT(1) > 1
Otherwise you could have a duplicate user_id within your user table. Maybe one user has been in multiple departments? or changed names?
Find duplicated user_ids with the following:
SELECT
user_id,
COUNT(1) as duplicate_user_count
FROM user
GROUP BY user_id
HAVING COUNT(1) >1
Putting it all back together - find where the duplication is happening and then add other columns to you care about once resolved:
SELECT
users.user_id,
users.dupe_users,
max_time.distinct_punchtimes,
max_time.distinct_punchtypes,
max_time.max_punchTime
FROM (
SELECT
user_id,
COUNT(1) as dupe_users
FROM user
GROUP BY
user_id
) as users
INNER JOIN (
SELECT
user_id,
COUNT(1) as clock_rows,
COUNT(DISTINCT(punchTime)) as distinct_punchtimes,
COUNT(DISTINCT(punchType)) as distinct_punchtypes,
MAX(punchTime) max_punchTime
FROM clock
GROUP BY user_id
) as max_time
ON users.user_id = max_time.user_id

Group by gives error "Invalid use of group function"

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

MySQL finding the most similar user

Let's say I have a table that looks like this:
Mark - Green
Mark - Blue
Mark - Red
Adam - Yellow
Andrew - Red
Andrew - Green
And my objective is to compare the user "Mark" with all the other users in the database, to find out which other user he is most similar to. In this case, he would be most similar to Andrew (2/3 matches), and least similar to Adam (0/3) matches. After I've found out which user is most similar to Mark I would like to extract the entires that Andrew got but Mark doesn't.
Would this be possible in MySQL? I appreciate all help, thank you guys!
Edit: OVERWHELMED by all the good help! THANK you SO MUCH guys! I will be sure to check out all of your contributions!
The following query attempts to list all the users with the number of matches to Mark. It basically joins the table with Mark's entries and counts the common entries for all users.
SELECT ours.user, theirs.user, count(*) as `score`
FROM tableName as `theirs`, (SELECT *
FROM tableName
WHERE user = 'Mark') as `ours`
WHERE theirs.user != 'Mark' AND
theirs.color = ours.color
GROUP BY theirs.user
ORDER BY score DESC
The query, however, wouldn't work if there's duplicate data (i.e. one person picks the same color twice). But that shouldn't be a problem as you mention in the comments that it wouldn't occur.
The query can be modified to show the score for all users:
SELECT ours.user as `myUser`, theirs.user as `theirUser`, count(*) as `score`
FROM tableName as `ours`, tableName as `theirs`
WHERE theirs.user != ours.user AND
theirs.color = ours.color
GROUP BY ours.user, theirs.user
ORDER BY score DESC
Let Q be the above query that gives you the most similar user. Once you have that user, you can use it to show the distinct entries between them. Here's what we're trying to do:
SELECT *
FROM tableName as theirs
WHERE user = 'Andrew'
AND NOT EXISTS (SELECT 1
FROM tableName as ours
WHERE ours.user = 'Mark'
AND ours.color = theirs.color)
Replacing the inputs Andrew and Mark from Q:
SELECT similar.myUser, theirs.user, theirs.color
FROM tableName as theirs JOIN (Q) as similar
ON theirs.user = similar.theirUser
WHERE NOT EXISTS (SELECT 1
FROM tableName as ours
WHERE ours.user = similar.myUser
AND ours.color = theirs.color)
Here's the final query up and running. Hope this makes sense.
Use FULLTEXT INDEXES. And your query will be like:
SELECT * FROM user WHERE MATCH (name,color) AGAINST ('Mark blue');
Or simplest way, is using LIKE search
SELECT * FROM user WHERE name LIKE '%Mike%' OR color = 'blue'
You can choose which way more suitable for you
select
name,
sum(case when t2.cnt > t1.cnt then t1.cnt else t2.cnt end) matches
from (
select name, color, count(*) cnt
from table
where name <> 'Mark'
group by name, color
) t1 left join (
select color, count(*) cnt
from table
where name = 'Mark'
group by color
) t2 on t2.color = t1.color
group by name
order by matches desc
The derived table t1 contains the # of colors each user (except Mark) has, t2 contains the same for Mark. The tables are then left joined on the color and the smaller of the 2 counts is taken i.e. if Amy has 2 reds and Mark has 1 red, then 1 is taken as the number of matches. Finally group by name and return the largest sum of matches.
select match.name, count(*) as count
from table
join table as match
on match.name <> table.name
and table.name = 'mark'
and match.color = table.color
group by match.name
order by count(*) desc
Below query returns matching score between name and matching_name and the maximum score it could get, so that you know what % value your matching has.
This code counts duplicate values in color column as only one, so that if you have record Mark - Red twice, it will only count as 1.
select
foo.name, foo.matching_name, count(*) AS matching_score, goo.color_no AS max_score
from
(
select
distinct a.name, a.color, b.name AS matching_name
from
(
select name, color from yourtable
) a
left join yourtable b on a.color = b.color and a.name <> b.name
where b.name is not null
) foo
left join ( select name, count(distinct color) AS color_no from yourtable group by name ) goo
on foo.name = goo.name
group by foo.name, foo.matching_name
Attaching SQLFiddle to preview the output.
This should get you close. The complexity comes from the fact that you allow each user to pick each color multiple times and require that each otherwise-identical pair be matched in the other user you're comparing to. Therefore, we're really interested in knowing how many total color picks a user per color and how that number compares to the compared-to users' count for that same color.
First, we create a derived relation that does the simple math for us (counting up each users' number of picks by color):
CREATE VIEW UserColorCounts (User, Color, TimesSeen)
AS SELECT User, Color, COUNT(*) FROM YourTable GROUP BY User, Color
Second, we need some kind of relation that compares each color-count for the primary user to the color-counts of each secondary user:
CREATE VIEW UserColorMatches (User, OtherUser, Color, TimesSeen, TimesMatched)
AS SELECT P.User, S.User, P.Color, P.TimesSeen, LEAST(P.TimesSeen, S.TimesSeen)
FROM UserColorCounts P LEFT OUTER JOIN UserColorCounts S
ON P.Color = S.Color AND P.User <> S.User
Finally, we total up the color-counts for each primary user and compare against the matching color counts for each secondary user:
SELECT User, OtherUser, SUM(TimesMatched) AS Matched, SUM(TimesSeen) AS OutOf
FROM UserColorMatches WHERE OtherUser IS NOT NULL
GROUP BY User, OtherUser

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