SELECT max(Date) rows BEFORE running WHERE clause? - mysql

I have this query that echoes all rows prior a particular date.
SELECT MAX(h.date), h.url
FROM HISTORY h
WHERE h.uid = '19'
AND h.date < (SELECT MAX(t.date)
FROM History t
WHERE t.url = 'canabalt.com'
AND t.uid = '19')
GROUP BY h.url
ORDER BY MAX(h.date) DESC
My problem is that I must only select rows that have max(Date).
But the where clause eliminates a number of rows that may have the max(Date) row and then looks for max(Date) in the remaining fields.
How can I first SELECT max(Date) and only afterwards run the WHERE clauses.

Try placing your criteria in the Having clause which is processed after the Group By clause.
Select Url, Max(Date) As MaxDate
From History
Where UID = '19'
Group By Url
Having Max( Date ) < (
Select Max( H1.Date )
From History As H1
Where H1.URL = 'canabalt.com'
And H1.UID = '19'
)
Order By Max( Date ) Desc

Related

MySQL group by only on specific amount of records (query inside JOIN expression)

I have a History table that represents messages that are stored by some sort of chat program.
It has a user id, message & datetime. (specified on: http://sqlfiddle.com/#!9/49fcefb/5)
Now, what I want is to have a result which contains:
The max amount of messages typed by date
What user has the most messages and howmany messages this user typed)
I got it working, but I find the query rather slow, I think its because of the last GROUP BY (GROUP BY b.cnt) as this will group over all the records found.
Query:
SELECT b.cnt as dayCount, a.cnt as userCount, a.userid as userId, b.date
FROM (
select date_added as date, user_id as userid, count(*) as cnt from history group by userid, day(date_added),month(date_added),year(date_added)
) a
INNER JOIN (
select date_added as date, count(*) as cnt from history group by day(date_added),month(date_added),year(date_added)
) b ON year(a.date) = year(b.date)
AND month(a.date) = month(b.date)
AND day(a.date) = day(b.date)
GROUP BY b.cnt
ORDER BY dayCount desc, userCount desc limit 10;
Can someone advice me on how to solve this? Maybe with another sort of query?
Thanks in advance!
You can try using windows function with Subquery to get the same result.
select SQ.daycount,SQ.usercount,SQ.user_id, SQ.date1 from (
select sum(count(Message)) over (partition by date(date_added)) as daycount, --to get max amount of msg by date
max(count(Message)) over (partition by date(date_added)) as maxuser, -- to identify user with most msg
count(Message) as usercount,
date(date_added) as date1,user_id from history
group by date1,user_id ) SQ
where SQ.usercount=SQ.maxuser
Check the Fiddle here
seems you are joining with wrong criteria.
first table is getting the # of users, 2nd table is getting the #of days per user_id. see dbfiddle
SELECT b.cnt as dayCount, a.cnt as userCount, a.userid as userId, b.date
FROM (
select
user_id as userid
, count(*) as cnt
from history
group by userid
) a
LEFT JOIN (
select cast(date_added as date) as date
, count(*) as cnt
, user_id as userid
from history
group by cast(date_added as date)
) b ON a.userid = b.userid
ORDER BY dayCount desc, userCount desc limit 10;

MySQL SELECT subquery

I have a calendar and user_result table and I need to join these two queries.
calendar query
SELECT `week`, `date`, `time`, COUNT(*) as count
FROM `calendar`
WHERE `week` = 1
GROUP BY `date`
ORDER BY `date` DESC
and the result is
{"week":"1","date":"2014-08-21","time":"15:30:00","count":"4"}, {"week":"1","date":"2014-08-20","time":"17:30:00","count":"12"}
user_result query
SELECT `date`, SUM(`point`) as score
FROM `user_result`
WHERE `user_id` = 1
AND `date` = '2014-08-20'
and the result is just score 3
My goal is to always show calendar even if the user isn't present in the user_result table, but if he is, SUM his points for that day where calendar.date = user_result.date. Result should be:
{"week":"1","date":"2014-08-21","time":"15:30:00","count":"4","score":"3"}, {"week":"1","date":"2014-08-20","time":"17:30:00","count":"12","score":"0"}
I have tried this query below, but the result is just one row and unexpected count
SELECT c.`week`, c.`date`, c.`time`, COUNT(*) as count, SUM(p.`point`) as score
FROM `calendar` c
INNER JOIN `user_result` p ON c.`date` = p.`date`
WHERE c.`week` = 1
AND p.`user_id` = 1
GROUP BY c.`date`
ORDER BY c.`date` DESC
{"week":"1","date":"2014-08-20","time":"17:30:00","count":"4","score":"9"}
SQL Fiddle
ow sorry, i was edited, and i was try at your sqlfiddle, if you want to show all date from calendar you can use LEFT JOIN, but if you want to show just the same date between calendar and result you can use INNER JOIN, note: in this case INNER JOIN just show 1 result, and LEFT JOIN show 2 results
SELECT c.`week`, p.user_id, c.`date`, c.`time`, COUNT(*) as count, p.score
FROM `calendar` c
LEFT JOIN
(
SELECT `date`, SUM(`point`) score, user_id
FROM `result`
group by `date`
) p ON c.`date` = p.`date`
WHERE c.`week` = 1
GROUP BY c.`date`
ORDER BY c.`date` DESC
I put a pre-aggreate query / group by date as a select for the one person you were interested in... then did a left-join to it. Also, your column names of week, date and time (IMO) are poor choice column names as they can appear to be too close to reserved keywords in MySQL. They are not, but could be confusing..
SELECT
c.week,
c.date,
c.time,
coalesce( OnePerson.PointEntries, 0 ) as count,
coalesce( OnePerson.totPoints, 0 ) as score
FROM
calendar c
LEFT JOIN ( select
r.week,
r.date,
COUNT(*) as PointEntries,
SUM( r.point ) as totPoints
from
result r
where
r.week = 1
AND r.user_id = 1
group by
r.week,
r.date ) OnePerson
ON c.week = OnePerson.week
AND c.date = OnePerson.date
WHERE
c.week = 1
GROUP BY
c.date
ORDER BY
c.date DESC
Posted code to SQLFiddle

ORDER BY and LIMIT in GROUP BY

I'm trying to get a subset of records in a GROUP BY, I've seen a lot of crazy solutions out there, but they just seem too complicated, is there any more efficient way to do this.
SELECT user_id, GROUP_CONCAT(item_id ORDER BY `timestamp`) AS items
FROM wb_user_book_current_item GROUP BY user_id
So this will return me all the current items for all users which is okay so far. But I only want the ten most recent items. Adding ORDER BY to the GROUP_CONCAT helps, but it still doesn't give me the last ten records.
EDIT
If I do something like this and hard code the user_id then I can get the results I want for that one user, problem is combining it so that I don't need to hard code the user_id and can for instance just get ALL users last ten items
SELECT GROUP_CONCAT(cp2.item_id) AS items
FROM (SELECT cp.user_id, cp.item_id
FROM wb_user_book_current_item cp
WHERE cp.user_id=1 ORDER BY cp.`timestamp`
LIMIT 10) AS cp2
GROUP BY cp2.user_id
This is a difficult problem, but how about this:
SELECT user_id, GROUP_CONCAT(item_id ORDER BY `timestamp`) AS items
FROM wb_user_book_current_item T
WHERE NOT EXISTS
(
SELECT 1
FROM wb_user_book_current_item T2
WHERE T2.user_id = T.user_id
ORDER BY T2.`timestamp` DESC
LIMIT 10,1
)
OR T.`timestamp` > (
SELECT T2.`timestamp`
FROM wb_user_book_current_item T2
WHERE T2.user_id = T.user_id
ORDER BY T2.`timestamp` DESC
LIMIT 10,1
)
GROUP BY user_id
This of course assumes you won't have two rows with the same timestamp for the same user.
If your timestamp field is always a positive integer, you can also replace the NOT EXISTS...OR with a COALESCE:
SELECT user_id, GROUP_CONCAT(item_id ORDER BY `timestamp`) AS items
FROM wb_user_book_current_item T
WHERE T.`timestamp` > COALESCE((
SELECT T2.`timestamp`
FROM wb_user_book_current_item T2
WHERE T2.user_id = T.user_id
ORDER BY T2.`timestamp` DESC
LIMIT 10,1
), 0)
GROUP BY user_id
Original answer, but apparently MySQL doesn't understand how to do this properly and complains the subselect returns multiple rows. Of course we want multiple rows; it's a GROUP_CONCAT. Grr.
Unfortunately, I think there's no real way around using a subquery:
SELECT T.user_id,
GROUP_CONCAT((SELECT T2.item_id
FROM wb_user_book_current_item T2
WHERE T2.user_id = T.user_id
ORDER BY T2.`timestamp`
LIMIT 10)) AS items
FROM wb_user_book_current_item T
GROUP BY user_id
Otherwise, adding LIMIT anywhere else will either limit the number of groups, or limit from the total recordset over the table (and not the group) - neither of which are what you are trying to achieve.
So came across a nice solution here that works pretty well.
http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/
It's something like this put all together:
SET #num := 0, #user_id := '';
SELECT cp2.user_id, CONCAT(cp2.item_id) AS items
FROM (
SELECT cp.user_id, cp.item_id,
#num := IF(#user_id = cp.user_id, #num + 1, 1) AS row_number,
#user_id := cp.user_id AS dummy
FROM wb_user_curent_item AS cp
ORDER BY cp.user_id ASC, cp.`timestamp` DESC
) AS cp2 WHERE cp2.row_number <= 10
GROUP BY cp2.user_id
So basically it just uses the num increment to limit the records rather than using LIMIT
SELECT
i.user_id,
GROUP_CONCAT(i.item_id ORDER BY i.timestamp) AS items
FROM
( SELECT DISTINCT user_id
FROM wb_user_book_current_item
) AS du
JOIN
wb_user_book_current_item AS i
ON i.user_id = du.user_id
AND i.timestamp <= COALESCE(
( SELECT i2.item_id
FROM wb_user_book_current_item AS i2
WHERE i2.user_id = du.user_id
ORDER BY i2.timestamp ASC
LIMIT 1 OFFSET 9
)
, '2038-01-19 03:14:07')
GROUP BY
i.user_id ;
An index on (user_id, timestamp, item_id) will help efficiency.
Try this:
SELECT
user_id,
GROUP_CONCAT(item_id ORDER BY `timestamp`) AS items
FROM wb_user_book_current_item
GROUP BY user_id
LIMIT 0, 10
UPDATE: I didn't notice the GROUP_CONCAT so you will have to use sub query in conunction with LIMIT
use LIMIT
SELECT column_name(s)
FROM table_name
LIMIT number

Ignore a WHERE clause if nested query returns nothing?

How do I ignore the second WHERE clause if my inner join doesn't return anything?
SELECT MAX( DATE ) , URL
FROM History
WHERE UID = '$uid'
AND DATE < ( SELECT MAX( DATE ) FROM History WHERE URL = '$url' AND UID = '$uid' )
GROUP BY URL
ORDER BY DATE DESC
LIMIT 1
Basically if my inner join doesn't return anything the query should be:
SELECT MAX( DATE ) , URL
FROM History
WHERE UID = '$uid'
GROUP BY URL
ORDER BY DATE DESC
LIMIT 1

How to return the most recent value with group by

I have this query :
SELECT history.trackid, tracklist.artist, tracklist.event, tracklist.data, history.date
FROM history JOIN tracklist ON history.trackid=tracklist.trackid
GROUP BY trackid
ORDER BY history.date DESC
but I'd like to return the most recent trackid (by date) when I group on history.
Tried with that GROUP BY and ORDER BY DESC, but is not the most recent. Where am I wrong?
The reason it is not sorting by most recent, is because GROUP BY creates an aggregate result. You could try:
SELECT h.trackid, t.artist, t.event, t.data
FROM history AS h
JOIN tracklist AS t
ON h.trackid = t.trackid
GROUP BY h.trackid
ORDER BY MAX(h.date) DESC
Because you are already aggregating results, the MAX(history.date) should return the maximum (or most recent) date for each group.
Assuming id is a PRIMARY KEY on history:
SELECT t.trackid, t.artist, t.event, t.data, h.date
FROM tracklist t
LEFT JOIN
history h
ON h.id =
(
SELECT hi.id
FROM history
WHERE hi.trackid = t.trackid
ORDER BY
hi.trackid DESC, hi.date DESC, hi.id DESC
LIMIT 1
)
Create an index on history (trackid, date, id) for this to work fast.