MySQL count results after MAX timestamp - mysql

I have two tables
mts
id
customer_id
created_at
mos
id
customer_id
created_at
I want to get the last time of an entry in mts and count the results of mos after MAX(mts.created_at). All should be GROUP BY customer_id.
I had the idea of a simple query like this, but this wont work.
SELECT id, created_at, COUNT(id)
FROM mos
WHERE created_at > (SELECT MAX(created_at) FROM mts)
GROUP BY customer_id
LIMIT 10

This SQL-Statement will just give you 1 single line in the result set (not matter what). Everything that is not part of an aggregate-function should by contained in GROUP BY.
Your SQL-Statement should look like this:
SELECT id, created_at, COUNT(id)
FROM mos
WHERE created_at > (SELECT MAX(created_at) FROM mts)
GROUP BY customer_id, id, created_at
LIMIT 10

Related

SQL query for listing values based on a column

I have a table with the following columns member_id, status and created_at (timestamp) and i want to extract the latest status for each member_id based on the timestamp value.
member_id
status
created_at
1
ON
1641862225
1
OFF
1641862272
2
OFF
1641862397
3
OFF
1641862401
3
ON
1641862402
Source: Raw data image
So, my ideal query result would be like this:
member_id
status
created_at
1
OFF
1641862272
2
OFF
1641862397
3
ON
1641862402
Expected query results image
My go to process for doing things like that is to assign a row number to each data and get row number 1 depending on the partition and sorting.
For mysql, this is only available starting mysql 8
SELECT ROW_NUMBER() OVER(PARTITION BY member_id ORDER BY created_at DESC) as row_num,
member_id, status, created_at FROM table
This will generate something like this.
row_num
member_id
status
created_at
1
1
OFF
1641862272
2
1
ON
1641862225
1
2
OFF
1641862397
1
3
ON
1641862402
2
3
OFF
1641862401
Then you use that as a sub query and get the rows where row_num = 1
SELECT member_id, status, created_at FROM (
SELECT ROW_NUMBER() OVER(PARTITION BY member_id ORDER BY created_at DESC) as row_num,
member_id, status, created_at FROM table
) a WHERE row_num = 1
MySQL has support for Window Function since v8.0. the solution from crimson589 is preferred for v8+, this solution applies for earlier versions of MySQL or if you need an alternate solution to window queries.
After grouping by member_id we can either join back into the original set to gain the corresponding status value to the MAX(created_at)
SELECT ByMember.member_id
, status.status
, ByMember.created_at
FROM (
SELECT member_id, max(created_at) as created_at
FROM MemberStatus
GROUP BY member_id
) ByMember
JOIN MemberStatus status ON ByMember.member_id = status.member_id AND ByMember.created_at = status.created_at;
Or you could use a sub query instead of the join:
SELECT ByMember.member_id
, (SELECT status.status FROM MemberStatus status WHERE ByMember.member_id = status.member_id AND ByMember.created_at = status.created_at) as status
, ByMember.created_at
FROM (
SELECT member_id, max(created_at) as created_at
FROM MemberStatus
GROUP BY member_id
) ByMember
The JOIN based solution allows you to query additional columns from the original set instead of having multiple sub-queries. I would almost always advocate for the JOIN solution, but sometimes the sub-query is simpler to maintain.
I've setup a fiddle to compare these options: http://sqlfiddle.com/#!9/0edb931/11
You can group by member_id and max of created_at, then a self join with member_id and created_at will give you the latest status.

SQL query to get the number of customers who purchased on multiple dates

I'm having trouble coming up with a query to get the number of customers who purchased on multiple dates.
We're given a table of product purchases. Each row in the table represents an individual user product purchase.if the customer purchased two things on the same day that does not count as an upsell as they were purchased within a similar timeframe.
'transactions' table:
column
type
id
integer
user_id
integer
created_at
datetime
product_id
integer
quantity
integer
I tried in this way
select count(*)
from
( select user_id
, count(date)
from
( SELECT user_id
, DATE(created_at) AS date
FROM transactions
GROUP BY 1,2
) S
group
by 1
having count(date)>1
) A
I think you want:
SELECT COUNT(*)
FROM
(
SELECT user_id
FROM transactions
GROUP BY user_id
HAVING COUNT(DISTINCT DATE(created_at)) > 1
) t;
The subquery finds all users having transacted on more than one date, the outer query finds the count of such users.
Count the distinct dates per user, first, then count from table when the count is > 1.
See below:
WITH T as (select user_id,
count(distinct DATE(created_at)) as count
from transactions
GROUP BY user_id)
select count(*) from T where count > 1

MySQL Group Grouped By Result

I have a very simple table which consists of the following columns:
id | customer_id | total | created_at
I was running this query to get the results per day for the last ten days:
SELECT SUM(total) AS total, DATE_FORMAT(created_at, "%d/%m/%Y") AS date
FROM table
WHERE created_at BETWEEN "2017-02-20" AND "2017-03-01"
GROUP BY created_at
ORDER BY created_at DESC
This works fine, but I've just noticed that there's an issue with imported rows being duplicated for some reason so I'd like to update the query to be able to handle the situation if it ever happens again, in other words select one row instead of all when the date and customer id are the same (the total is also identical).
If I add customer_id to the group by that seems to work but the trouble with that is then the query returns a result per day for each customer when I only want the overall total.
I've tried a couple of things but I haven't cracked it yet, I think it will be achievable using a sub query and/or an inner join, I have tried this so far but the figures are very wrong:
SELECT
created_at,
(
SELECT SUM(total)
FROM table test
WHERE test.created_at = table.created_at
AND test.customer_id = table.customer_id
GROUP BY customer_id, created_at
LIMIT 1
) AS total
FROM table
WHERE created_at BETWEEN "2017-02-20" AND "2017-03-01"
GROUP BY created_at
ORDER BY created_at DESC
It's also a large table so finding a performant way to do this is also important.
First, are you sure that created_at is a date and not a datetime? This makes a big difference.
You can do what you want using two levels of aggregation:
SELECT SUM(max_total) AS total, DATE_FORMAT(created_at, '%d/%m/%Y') AS date
FROM (SELECT t.customer_id, t.created_at, MAX(total) as max_total
FROM table t
WHERE t.created_at BETWEEN '2017-02-20' AND '2017-03-01'
GROUP BY t.customer_id, t.created_at
) t
GROUP BY created_at
ORDER BY created_at DESC;

sort on two parameters

I have a table with checkin records like this:
userID checkin_date
1 '2014-01-28 08:00:00'
1 '2014-01-27 09:10:00'
1 '2014-01-26 12:24:00'
2 '2014-01-26 08:17:00'
3 '2014-01-26 09:33:00'
2 '2014-01-28 10:28:00'
.. .........
and i want with a single request sort the ten users who checkin the most since a specific date (order by nb visite DESC) (that easy) but for each one i also want to know the date of their last checkin.
i do something like this:
SELECT
userID,
count(*) as nbVisit,
checkin_date
FROM(
SELECT
userID,
checkin_date
FROM checkin_table
WHERE checkin_date > '2014-01-25'
ORDER BY checkin_date DESC )as sub
GROUP BY userID
ORDER BY nbVisit DESC
LIMIT 10
is it the best way to do it ? will it work in any time ? is it efficient with lots of data ?
SQLFIDDLE
You don't need a subquery for this, just use max() along with count(*):
SELECT userID, max(checkin_date), count(*) as nbVisit,
FROM checkin_table
WHERE checkin_date > '2014-01-25'
GROUP BY userId
ORDER BY nbVisit desc
LIMIT 10 ;

MYSQL query to get records with value that increased most between two dates

I have a MySQL table ScoreArchive with following fields:
ID (int), primary key
Date (date)
Score (int)
I record in the table the Score of each ID every day.
Now I wish to find the IDs that have the top score increase between, for example, 2011-04-22 and 2011-05-31.
How can I find these using a MySQL query?
Try something like:
select id, max(score) - min(score) as diff ... group by id order by diff desc
Edit (following up on the comment):
Or something like:
select id, final_scores.score - start_scores.score as diff
from (
select id, min(date) as min_date, max(date) as max_date
from scores
where date between ...
group by id
) as ranges
join scores as final_scores
on final_scores.date = ranges.min_date
join scores as start_scores
on start_scores.date = ranges.max_date
where ...
order by diff desc
SELECT score FROM ScoreArchive WHERE date BETWEEN 2011-04-22 AND 2011-05-31 ORDER BY score DESC;
That's how i would do it in pgsql i am guessing that mysql is the same