Mysql most outer table visibility in nested loops - mysql

Let's assume i have 4 tables:
'users' (id, username),
'photos' (id, user_id, name),
'photos_comments' (id, photo_id, user_id, text),
'photos_likes' (id, photo_id, user_id, test).
I want to calculate sum of all comments and likes for every user in all of his uploaded photos. For that i'm trying to build a query:
SELECT users.*,
(SELECT SUM(count) as rating FROM(
SELECT COUNT(*) as count FROM photos_likes
WHERE photos_likes.photo_id IN (SELECT photos.id FROM photos WHERE photos.user_id = users.id)
UNION
SELECT COUNT(*) as count FROM photos_comments
WHERE photos_comments.photo_id IN (SELECT photos.id FROM photos WHERE photos.user_id = users.id)
) as total_rating) as rating FROM users
It returns 'Unknown users.id column in WHERE clause' error. So it looks like it can't see users table in most inner query.I can't understand why it happens,because another similar query works ok:
SELECT users.*,
(SELECT COUNT(*) as count FROM photos_likes
WHERE photos_likes.photo_id IN (SELECT photos.id FROM photos WHERE photos.user_id = users.id)) as likes_count,
(SELECT COUNT(*) as count FROM photos_comments
WHERE photos_comments.photo_id IN (SELECT photos.id FROM photos WHERE photos.user_id = users.id)) as comments_count FROM users
In this query it can grab id from users table row in most inner query. Why is it working like that? Thanks for help.

Look into Subqueries in the FROM Clause:
Subqueries in the FROM clause cannot be correlated subqueries, unless used within the ON clause of a JOIN operation.
In your second example, you use the subquery in a where clause. That's the difference.
See also Correlated Subqueries.

select
photos.userid,
photos.photoid,
count(distinct commentid),
count(distinct likeid),
count(distinct commentid) + count(distinct likeid) as total
from
photos
left join photos_comments on photos.photoid=photos_comments.photoid
left join photos_likes on photos.photoid=photos_likes.photoid
group by photos.userid, photos.photoid

Related

Optimizing MySQL Join and Group By with intermediate table

Simplifying but I have three tables:
users (user_id, team_id)
results (user_id, result)
user_signups (user_id, team_id, event_id)
results.user_id is a foreign key.
Tables have large number of rows in. If I do
select sum(result)
from results
inner join users on users.id = results.user_id
group by team_id
It is fast. "Explain" has results with 150k rows, users with 1 row.
If I do
select sum(result)
from results
inner join user_signups on user_signups.user_id = results.user_id
where event_id = 1
group by team_id
It is very slow (from 1 second to 14). "Explain" has results with 28 rows, user_signups with 5345 rows.
Things I have tried:
A unique index on event_id and user_id on user_signups.
An index on event_id, user_id, team_id on user_signups.
Rewriting as
select sum(result)
from results
inner join (select * from user_signups where event_id = 1) user_signups on user_signups.user_id = results.user_id
group by team_id
Rewriting as
select sum(result)
from results
inner join users on users.id = results.user_id
inner join user_signups on user_signups.user_id = users.id
where event_id = 1
group by user_signups.team_id
Any other suggestions?
By grouping on the team_id, I assume that you want one row for each record in results.
Is this what you're looking for?
SELECT *, sum(result) FROM results
LEFT JOIN users ON (users.user_id=results.user_id)
LEFT JOIN user_signups ON (user_signups.users_id=users.user_id)
GROUP BY table.field
From here, you can group on whatever you like. This structure assumes that most of your data will be present in the results table and will join users to the results table and user_signups to the users table.
Make the multicolumn index on (event_id, user_id, team_id) in user_signups table and try to run the following query.
If this doesn't work then post your explain here.
select sum(result) from results inner join(select
event_id,user_id,team_id from user_signups where event_id = 1)
user_signups on user_signups.user_id = results.user_id group by
team_id

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

How to count number of distinct values when joining multiple tables?

I have following database tables
categories
id, name
products
category_id, id, product_name, user_id
product_comments
product_id, id, comment_text, user_id
I need a count of number of different users in both products and product_comments tables. I have got the following query where I select all those categories where there is atleast one product and each product may have zero or some comments ... but what I can't figure out is that how to get the sum of these different user ids .. if it were just from one table I would try COUNT(products.user_id) ... .here is my query ..
SELECT
c.*
FROM categories c
INNER JOIN products p ON p.category_id = c.id
LEFT JOIN product_comments pc ON pc.product_id = p.id
I need total number of different users IDs from both products and product_comments tables.
I would expect the result data somewhat like below:
Category_id, Category_name, TotalUsers
1, Test Category, 10
2, Another Category, 5
3, Yet another cat, 3
This will give you an overall count rather than a list of all distinct id's:
SELECT COUNT(DISTINCT user_id)
FROM products
LEFT JOIN product_comments comm ON products.id = comm.product_id
If you want distinct users, then you could try something like:
select distinct user_id from (
select user_id from products
UNION
select user_id from product_comments
) as allusers;
You can then count them:
select count(distinct user_id) from (
select user_id from products
UNION
select user_id from product_comments
) as allusers;
select user_id from products
UNION
select user_id from product_comments
This will give you the distinct list of user ids that exist in the tables. The users could be present in just one of the tables, or both and will still be included in the list.
You can use the DISTINCT keyword and COUNT() function
SELECT DISTINCT
COUNT(c.*)
FROM categories c
INNER JOIN products p ON p.category_id = c.id
LEFT JOIN product_comments pc ON pc.product_id = p.id

mysql combine two selects?

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

Mysql - getting distinct records

I have the following MySql table (user_actions) with the following columns:
id, user_id, created_at, action_type
I want a sql query that will get the latest actions that the user performed with no duplication of the actions.
for example:
user_id 1 has 3 records that has the action_type "follow"
and 2 records that has the action_type "unfollow"
in this case i want the query to return two records, one with action_type "follow" and one with "unfollow"
any thoughts?
You can use SQL's group by clause for this:
select user_id, action_type, max(created_at)
from user_actions
group by user_id, action_type
try this:
select ua.*
from user_actions ua
join (select max(id) as max_id,user_id,action_type from user_action group by user_id,user_action) ua_max
on ua.id=ua_max.max_id and ua.user_id=ua_max.user_id and ua.action_type=ua_max.action_type
Try this query:
select * from user_actions where action_type, created_at in
(select action_type, max(created_at) from user_actions group by action_type);