mysql combine two selects? - mysql

The first select is
select user_id, count(*) as count
from users
where referrer IS NOT NULL
group by referrer
order by count DESC
Then based off the records returned by that query I need to get the date for the user who referred the users in the above query.
select user_id from users where token = IDS_FROM_LAST_QUERY
I know I could use a sub query and say where IN (subquery) but I'm getting tripped up trying to keep the count from the subquery.
So in the end I need the following info
user_id, count

select o.user_id user_id, count(*) count
from users o
join users i on o.token = i.user_id
where i.referrer is not null
group by referrer
order by count desc

I would use a CTE (common table expression). CTE is super handy to look to get one population and then query the same or slightly different population from the CTE.
WITH Referrer (user_id, count) AS
(
select user_id, count(*) as count
from users
where referrer IS NOT NULL
group by referrer
order by count DESC
)
select
users.user_id
,Referrer.count
from users
inner join Referrer.user_id = users.user_id

Related

Filter data on group by query

I've got my MySQL query below:
select * from messages
where id in (select max(id) from messages group by Name)
Why does this query not work?:
select * from messages
where id in (select max(id) from messages where field <> 'value' group by Name)
It would be helpful to know what you mean by "doesn't work" - for example, whether you get an error or whether it produces unexpected results. However, going by your comment, and assuming you define "last item sales" by the maximum ID per user, I would recommend JOINing your table to a subquery that selects the maximum ID per user. For example, given the sample sales data set mentioned in your comment, you could write a query like so:
select s.*
from sales s
join (
select user_name, max(id) as max_id
from sales
where sale_item <> 'Ship'
group by user_name
) q
on q.user_name = s.user_name
and q.max_id = s.id
I have created a SQL Fiddle demonstrating the output of the query.

How to select values by the count of a filed?

How to select values in MySQL table by the count of a field in the same table ?
Example:
Table users has id, name, password, country
I want to select all ids of only top 3 countries with highest users count to look like this:
country, id
When I try this
WHERE country IN (SELECT country FROM USERS GROUP BY country ORDER BY COUNT(id) DESC LIMIT 3)
I get This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
Server version: 5.6.33-79.0 Percona Server
Join with a subquery that gets the top 3 countries.
SELECT u.country, u.id
FROM users AS u
JOIN (SELECT country
FROM users
GROUP BY country
ORDER BY COUNT(*) DESC
LIMIT 3) AS u1
ON u.country = u1.country
Practically any use of WHERE column IN (SELECT ...) can be changed to a JOIN like this, and in many cases the performance is better.
How to select values in MySQL table by the count of a field in the
same table?
Use the GROUP BY field HAVING COUNT(*)=123 aggregation expression after the WHERE clause if there is.
Try this:
SELECT country, id
FROM
(
SELECT country,count(Id) as [NumberofUsers]
FROM USERS
GROUP BY country
) X
INNER JOIN USERS U ON X.Country=U.Country
ORDER BY X.[NumberofUsers] DESC LIMIT 3

MYSQL AVG Score Of First Attempt Of 3 Different Tests - IN GROUP BY

So I have 2 tables
Users: id, name
Results: id, test_id, user_id, score
I need to get the average of the user's first attempt over all three test_id's.
The user may not have completed all 3 tests.
The query I have here does work but is extremely slow, is there a way of speeding this up?
SELECT AVG(score)
FROM results
WHERE id IN(SELECT MIN(id)
FROM results
WHERE complete = 1
GROUP
BY test_id)
This approach uses a subquery to get the minimum id for each user/test combination. It then joins back to results to get the score and uses that for the aggregation:
SELECT u.*, AVG(r.score)
FROM user u LEFT JOIN
(SELECT user, testid, MIN(id) as minid
FROM results r
WHERE complete = 1
GROUP BY user_id, test_id
) ut
ON ut.user_id = u.id LEFT JOIN
results r
ON r.id = ut.minid
GROUP BY u.id;
This produces the average for each user (which is how I interpret the question). If you want the average over all users of the first of each test, then remove the group by and user table from the query.
Try to use a subquery and JOIN it instead of the correlated subquery in the WHERE clause:
SELECT
r1.id,
r2.test_id,
r2.avgstore
FROM results AS r1
INNER JOIN
(
SELECT test_id, MIN(id) AS MinId, AVG(Store) AS AvgStore
FROM results
WHERE complete = 1
GROUP BY test_id
) AS r2 ON r1.id = r2.MinId AND r1.test_id = r2.test_id;
SQL Fiddle Demo

Subquery - records are not in ordered form

I have three tables:
user: id, name
keyword: id, name
userkeyword: id, user_id, keyword_id
I want to execute query in following way:
Display those users whose keyword/s are matched with the login user's
keywords. In the order of maximum number of keyword matched user
should display first
e.g : If userA having 4 matched keywords, userB having 8, userC having 1, userD having 6 then the result should be in the order of,
userB
userD
userA
userC
For that I have done with this query (assume login user's id is 1):
select *
from user
where id IN (
select user_id
from userkeywords
where keyword_id IN (
select keyword_id
from userkeywords
where user_id=1)
group by user_id
order by count(keyword_id) desc)
AND id != 1
Here the result is getting perfect but the order is not correct. I have merged two queries in following manner"
select *
from user
where id IN (?)
AND id!=1
+
select user_id
from userkeywords
where keyword_id IN (
select keyword_id
from userkeywords
where user_id=1)
group by user_id
order by count(keyword_id) desc
Second query returns user_id in correct order but when I merged both queries, order was changed (wrong).
Hope I have mentioned my query properly with enough detail.
A subquery returns an unordered set, so the order by in a subquery only matters for its limit clause, if there is any. Any database other than MySQL would give an error message for a purely decorative sort order.
There's no way to sort on a column that only exists in the where clause. You'd have to rewrite the query. One option is to replace your in conditions with joins:
select uk2.name
from userkeywords uk1
join userkeywords uk2
on uk1.keyword_id = uk2.keyword_id
and uk1.user_id <> uk2.user_id
join user u2
on u2.id = uk2.user_id
where uk1.user_id = 1
group by
uk2.name
order by
count(*) desc
This should do it.
select uk.user_id, u.name
from userkeywords uk
left join user u on u.id = uk.user_id
where uk.keyword_id IN (
select keyword_id
from userkeywords
where user_id=1)
group by uk.user_id
order by count(uk.keyword_id) desc) AND uk.user_id != 1
Also, JOIN provides better performance.
I would use an inner join to select the correct rows:
SELECT *
FROM user
INNER JOIN (
SELECT * FROM userkeyword
WHERE keyword_id IN (
SELECT keyword_id
FROM userkeyword
WHERE user_id=1
)
) uk
ON user.id = uk.user_id
GROUP BY u.id
ORDER BY count(*) DESC;

SQL: select all the rows from the two userid's with great number of rows

I would like to get all the rows from the two users with the greatest number of rows, that is, the two users with the greatest activity in a log table.
I have only found next solution: first, get the number of rows for every user, an limit it to 2:
SELECT userid, count(*) AS n_of_rows FROM my_table GROUP BY userid LIMIT 2;
Then, from the source code I'm querying the database (Python for example), query the database to get the rows of each user:
SELECT * FROM my_table where userid = $userid
Is it the best/elegant solution, taking into account SQL language itself and database performance?
Thanks!
I think what you're looking for is something like
select * from my_table where userid in
(select userid from my_table
group by userid
order by count(*) desc
limit 2)
To get the rows and keep the order, use a join with aggregation:
select t.*
from my_table t join
(select userid, count(*) as cnt
from my_table
group by userid
order by count(*) desc
limit 2
) top2
on t.userid = top2.userid
order by top2.cnt desc, userid;
Try this:
SELECT TOP 2 userid, count(*) AS n_of_rows
FROM my_table
GROUP BY userid
ORDER BY count(*) desc