MySql Query: Getting a record count within query - mysql

I got my query working but it doesn't count the rows... in my left outer join.
SELECT mUserId,mUserName,COALESCE(x.likeId,0) AS likeCount
FROM likes
LEFT JOIN members ON likes.likeMember = members.mUserId
LEFT OUTER JOIN (SELECT likeId, count(*) n FROM likes WHERE likeMember = likes.likeMember) x ON likes.likeMember = x.likeId
WHERE likeDate > '2014-11-16 07:44:47'
GROUP BY likeMember
ORDER BY `likeCount` DESC
Any suggestions?

This is your query:
SELECT mUserId,mUserName,COALESCE(x.likeId,0) AS likeCount
FROM likes LEFT JOIN
members
ON likes.likeMember = members.mUserId LEFT OUTER JOIN
(SELECT likeId, count(*) n
FROM likes
WHERE likeMember = likes.likeMember
) x
ON likes.likeMember = x.likeId
WHERE likeDate > '2014-11-16 07:44:47'
GROUP BY likeMember
ORDER BY `likeCount` DESC;
It is a bit absurd. Either do an aggregation in the subquery. Or do an aggregation in the outer query. But not both. I suspect you want something more like this:
SELECT m.mUserId, m.mUserName, COUNT(*) AS likeCount
FROM likes l LEFT JOIN
members m
ON l.likeMember = m.likeId
WHERE l.likeDate > '2014-11-16 07:44:47'
GROUP BY l.likeMember
ORDER BY `likeCount` DESC;
The problem with your subquery is the WHERE clause. You think it is correlated to the outer query. But it is really interpreted as:
WHERE likes.likeMember = likes.likeMember
In other words, the condition is true whenever likes.likeMember is not NULL.

Related

Sub Query Not IN With Left Join Alternative?

I have Query Like This
SELECT f.ACCOUNT_ID,
f.TGL,
p.ACCOUNT_EMAILADDRESS
FROM distributor_kokola.forecast f
inner join distributor_kokola.push_distributor p on p.ACCOUNT_ID = f.ACCOUNT_ID
where f.ACCOUNT_ID NOT IN(
select ACCOUNT_ID
from distributor_kokola.forecast
where DATE_FORMAT(TGL, "%Y-%m") = DATE_FORMAT(CURDATE(), "%Y-%m")
group by ACCOUNT_ID
)
group by f.ACCOUNT_ID;
That Sub Query work but To Slow so I change it with Left Join, it work faster
SELECT f.ACCOUNT_ID,
f.TGL,
p.ACCOUNT_EMAILADDRESS
FROM distributor_kokola.forecast f
left join(
select ACCOUNT_ID
from distributor_kokola.forecast
where DATE_FORMAT(TGL, "%Y-%m") = DATE_FORMAT(CURDATE(), "%Y-%m")
group by ACCOUNT_ID
)subb on subb.ACCOUNT_ID = f.ACCOUNT_ID
inner join distributor_kokola.push_distributor p on p.ACCOUNT_ID = f.ACCOUNT_ID
group by f.ACCOUNT_ID;
But, My issue is Still Contain wrong Result,
With Not IN, query 1 select where NOT IN query 2.
How can I get like NOT IN query with left join.
Can Anyone Help Me?
thanks.
You have to add a WHERE clause to filter by the results of your LEFT JOIN. If you add an appropriate WHERE clause WHERE subb.ACCOUNT_ID IS NULL, it should work as expected (since you used a GROUP BY in your subquery, there's no danger of duplicate rows):
SELECT f.ACCOUNT_ID,
f.TGL,
p.ACCOUNT_EMAILADDRESS
FROM distributor_kokola.forecast f
left join(
select ACCOUNT_ID
from distributor_kokola.forecast
where DATE_FORMAT(TGL, "%Y-%m") = DATE_FORMAT(CURDATE(), "%Y-%m")
group by ACCOUNT_ID
) subb on subb.ACCOUNT_ID = f.ACCOUNT_ID
inner join distributor_kokola.push_distributor p on p.ACCOUNT_ID = f.ACCOUNT_ID
WHERE sub.ACCOUNT_ID IS NULL
group by f.ACCOUNT_ID;
Update
The goal of our LEFT JOIN is to find all rows in our forecast table that don't have a matching row in the subquery. Therefore, we need a WHERE clause that removes all rows with a matching row - WHERE sub.ACCOUNT_ID IS NULL fits quite nicely.
SO user #quassnoi has written a wonderful comparison of different methods to achieve this goal.

MySQL LEFT JOIN only last row painfully slow

I have three tables, which looks like this
forts
|id|lat|lon|
fort_sightings
|id|fort_id|team|
fort_raids
|id|fort_id|raid_level|
I need a query that fetches all the rows from forts and then select the latest information from fort_sightings and fort_raids, if any. There might be several rows where fort_id has the same value, so I need the latest information.
Currently, I have this, which might not be the prettiest
SELECT
*
FROM
forts c
LEFT JOIN fort_sightings o ON o.id = (
SELECT
id
FROM
fort_sightings
WHERE
fort_id = c.id
ORDER BY
id DESC
LIMIT 1
)
LEFT JOIN fort_raids r ON r.id = (
SELECT
id
FROM
fort_raids
WHERE
fort_id = c.id
ORDER BY
id DESC
LIMIT 1
)
But it's painfully slow, the query takes over 10 seconds. There's only ~350 rows in forts, so it really shouldn't take this long. I believe it's from all the SELECT queries in the JOIN, but I don't know any alternative.
EXPLAIN
This is your query:
SELECT *
FROM forts c LEFT JOIN
fort_sightings o
ON o.id = (SELECT fs2.id
FROM fort_sightings fs2
WHERE fs.fort_id = c.id
ORDER BY fs2.id DESC
LIMIT 1
) LEFT JOIN
fort_raids r
ON r.id = (SELECT fr2.id
FROM fort_raids fr2
WHERE fr2.fort_id = c.id
ORDER BY fr2.id DESC
LIMIT 1
);
I find it strangely structured. But, see if this works:
fort_sightings(fort_id, id)
fort_raids(fort_id, id)
It is important that the fort_id be the first key in the indexes.
If this doesn't work, then you might need to modify the query.
Aside from the index recommendation from Gordon, you are doing a correlated subquery which means that for every record in the forts table, you are re-running the query to the sightings and raids. I would change slightly to pull all max() per fort from each THEN join... like
SELECT
c.id,
c.lat,
c.lon,
fs2.team,
fr2.raid_level
FROM
forts c
LEFT JOIN
( select fs.fort_id, max( fs.id ) as MaxSightID
from fort_sightings fs
group by fs.fort_id ) S
on c.id = s.fort_id
LEFT JOIN fort_sightings fs2
on s.MaxSightID = fs2.id
LEFT JOIN
( select fr.fort_id, max( fr.id ) as MaxRaidID
from fort_raids fs
group by fr.fort_id ) R
on c.ID = r.fort_id
LEFT JOIN fort_raids fr2
on r.MaxRaidID = fr2.id
The second part of the left-joins goes back to the original raid and sighting tables to pull the corresponding team and raid level in the final results if any such are found.
If you need the most efficient query, you should add columns latest_fort_sighting_id latest_fort_raid_id to table forts. MySQL does not have powerful features such as Materialized Views or Hash Joins like PostgreSQL, we need to handle them manually. Do not forget using transaction for updates.
If you limit range of forts, alternatively, you can run optimized query only using LEFT JOIN.
select - SQL join: selecting the last records in a one-to-many relationship - Stack Overflow
SELECT forts.*, fs1.team, fr1.raid_level FROM forts
LEFT JOIN fort_sightings fs1 ON fs1.fort_id = forts.id
LEFT JOIN fort_sightings fs2 ON fs2.fort_id = forts.id AND fs1.id < fs2.id
LEFT JOIN fort_raids fr1 ON fr1.fort_id = forts.id
LEFT JOIN fort_raids fr2 ON fr2.fort_id = forts.id AND fr1.id < fr2.id
WHERE fs2.id IS NULL AND fr2.id IS NULL AND forts.id > 5 ORDER BY forts.id LIMIT 5;

Mysql Nested/Multiple Query handling

I have three queries gives me result individually correct but my requirement is i need all result in single query only so how should i proceed?
select * from user_post_like
inner join user_post on user_post_like.postID = user_post.postID
inner join Users on Users.userID=user_post_like.userID
where (user_post.poster='$uid' AND user_post_like.userID!='$uid')
ORDER BY likeID DESC;
select * from user_post_comment
inner join user_post on user_post_comment.postID = user_post.postID
inner join Users on Users.userID=user_post_comment.commenter
where (user_post.poster='$uid' AND user_post_comment.commenter!='$uid')
ORDER BY commentID DESC;
select * from user_post_share
inner join user_post on user_post_share.postID = user_post.postID
inner join Users on Users.userID=user_post_share.Share_user_id
where (user_post.poster='$uid' AND user_post_share.Share_user_id!='$uid')
ORDER BY shareID DESC;
Since you're joining the tables anyway, you can put columns from all in your select - and keep your statement readable. If you have duplicate column names (from different tables) you may need to aggregate them with functions and group by.
SELECT s.*, p.*, u.*
FROM user_post_share s
INNER JOIN user_post p ON s.postID = p.postID
INNER JOIN Users u ON u.userID = p.poster
WHERE (p.poster='$uid' AND s.Share_user_id != '$uid')
ORDER BY shareID DESC
try sumthing like this
select * from user_post_like,user_post_comment,user_post_share <inner joins> <where conditions>

Mysql SUM Float give wrong value [duplicate]

I'm looking for help using sum() in my SQL query:
SELECT links.id,
count(DISTINCT stats.id) as clicks,
count(DISTINCT conversions.id) as conversions,
sum(conversions.value) as conversion_value
FROM links
LEFT OUTER JOIN stats ON links.id = stats.parent_id
LEFT OUTER JOIN conversions ON links.id = conversions.link_id
GROUP BY links.id
ORDER BY links.created desc;
I use DISTINCT because I'm doing "group by" and this ensures the same row is not counted more than once.
The problem is that SUM(conversions.value) counts the "value" for each row more than once (due to the group by)
I basically want to do SUM(conversions.value) for each DISTINCT conversions.id.
Is that possible?
I may be wrong but from what I understand
conversions.id is the primary key of your table conversions
stats.id is the primary key of your table stats
Thus for each conversions.id you have at most one links.id impacted.
You request is a bit like doing the cartesian product of 2 sets :
[clicks]
SELECT *
FROM links
LEFT OUTER JOIN stats ON links.id = stats.parent_id
[conversions]
SELECT *
FROM links
LEFT OUTER JOIN conversions ON links.id = conversions.link_id
and for each link, you get sizeof([clicks]) x sizeof([conversions]) lines
As you noted the number of unique conversions in your request can be obtained via a
count(distinct conversions.id) = sizeof([conversions])
this distinct manages to remove all the [clicks] lines in the cartesian product
but clearly
sum(conversions.value) = sum([conversions].value) * sizeof([clicks])
In your case, since
count(*) = sizeof([clicks]) x sizeof([conversions])
count(*) = sizeof([clicks]) x count(distinct conversions.id)
you have
sizeof([clicks]) = count(*)/count(distinct conversions.id)
so I would test your request with
SELECT links.id,
count(DISTINCT stats.id) as clicks,
count(DISTINCT conversions.id) as conversions,
sum(conversions.value)*count(DISTINCT conversions.id)/count(*) as conversion_value
FROM links
LEFT OUTER JOIN stats ON links.id = stats.parent_id
LEFT OUTER JOIN conversions ON links.id = conversions.link_id
GROUP BY links.id
ORDER BY links.created desc;
Keep me posted !
Jerome
Jeromes solution is actually wrong and can produce incorrect results!!
sum(conversions.value)*count(DISTINCT conversions.id)/count(*) as conversion_value
let's assume the following table
conversions
id value
1 5
1 5
1 5
2 2
3 1
the correct sum of value for distinct ids would be 8.
Jerome's formula produces:
sum(conversions.value) = 18
count(distinct conversions.id) = 3
count(*) = 5
18*3/5 = 9.6 != 8
For an explanation of why you were seeing incorrect numbers, read this.
I think that Jerome has a handle on what is causing your error. Bryson's query would work, though having that subquery in the SELECT could be inefficient.
Use the following query:
SELECT links.id
, (
SELECT COUNT(*)
FROM stats
WHERE links.id = stats.parent_id
) AS clicks
, conversions.conversions
, conversions.conversion_value
FROM links
LEFT JOIN (
SELECT link_id
, COUNT(id) AS conversions
, SUM(conversions.value) AS conversion_value
FROM conversions
GROUP BY link_id
) AS conversions ON links.id = conversions.link_id
ORDER BY links.created DESC
I use a subquery to do this. It eliminates the problems with grouping.
So the query would be something like:
SELECT COUNT(DISTINCT conversions.id)
...
(SELECT SUM(conversions.value) FROM ....) AS Vals
How about something like this:
select l.id, count(s.id) clicks, count(c.id) clicks, sum(c.value) conversion_value
from (SELECT l.id id, l.created created,
s.id clicks,
c.id conversions,
max(c.value) conversion_value
FROM links l
LEFT JOIN stats s ON l.id = s.parent_id
LEFT JOIN conversions c ON l.id = c.link_id
GROUP BY l.id, l.created, s.id, c.id) t
order by t.created
This will do the trick, just divide the sum with the count of conversation id which are duplicate.
SELECT a.id,
a.clicks,
SUM(a.conversion_value/a.conversions) AS conversion_value,
a.conversions
FROM (SELECT links.id,
COUNT(DISTINCT stats.id) AS clicks,
COUNT(conversions.id) AS conversions,
SUM(conversions.value) AS conversion_value
FROM links
LEFT OUTER JOIN stats ON links.id = stats.parent_id
LEFT OUTER JOIN conversions ON links.id = conversions.link_id
GROUP BY conversions.id,links.id
ORDER BY links.created DESC) AS a
GROUP BY a.id
Select sum(x.value) as conversion_value,count(x.clicks),count(x.conversions)
FROM
(SELECT links.id,
count(DISTINCT stats.id) as clicks,
count(DISTINCT conversions.id) as conversions,
conversions.value,
FROM links
LEFT OUTER JOIN stats ON links.id = stats.parent_id
LEFT OUTER JOIN conversions ON links.id = conversions.link_id
GROUP BY conversions.id) x
GROUP BY x.id
ORDER BY x.created desc;
I believe this will give you the answer that you are looking for.

How to rewrite SQL query that has subquery with joins

I have a SQL query that has a subquery that has joins. I would like to rewrite the query without the subquery so that I can create a view. MySQL does not allow SELECT statements where the FROM is a subquery.
Is this possible? I've tried removing the outer select and moving the group by inside the subs query. This partially works but some of the data is incorrect.
select *
from (SELECT r.id, r.dateAdded, r.listingId, r.rating, r.username, r.valid, tbl_data.nameShort, tbl_data.desk, d.model, d.hardware, d.serial, l.appVersion, r.photoUrl, r.comment
FROM tbl_ratings r
JOIN tbl_data on r.listingId = vi_data.id
JOIN tbl_devices d on r.serial = d.serial
JOIN tbl_log l on l.serial = d.serial
ORDER BY d.serial, l.dateAdded DESC) x
group by id
order by dateAdded DESC
Thanks in advance!
Is it as simple as:
SELECT r.id, r.dateAdded, r.listingId, r.rating, r.username, r.valid,
tbl_data.nameShort, tbl_data.desk, d.model, d.hardware,
d.serial, l.appVersion, r.photoUrl, r.comment
FROM tbl_ratings r
JOIN tbl_data on r.listingId = vi_data.id
JOIN tbl_devices d on r.serial = d.serial
JOIN tbl_log l on l.serial = d.serial
GROUP BY r.id
ORDER BY r.dateAdded DESC
Also, you have a reference to "vi_data" that isn't anywhere else in the query
Change your group by clause to be group by r.id. Since you're selecting from a derived table (the subquery), the db can't tell that there's only one "id" field in that derived table - it only sees the column headers as specified in the subquery, which is r.id.