i'm trying to do age analysis on some data and need to do a conditional summation i.e my table is :
ID | Date | Amount |
===+=========+========+
1 | 1/1/10 | 100 |
2 | 1/2/10 | 100 |
3 | 1/5/10 | 100 |
4 | 15/5/10 | 100 |
5 | 20/5/10 | 100 |
Say the date today is 1/6/10 I'd like to sum the amount depending on their age as used in age analysis. i.e i'd like this out
Age | Total
===========+======
<30 days | 300
30-60 days | 0
60-90 days | 0
90 days+ | 200
Essentially its a conditional summation so I want to sum all the values (<30 days, then 30-60 days, then 60-90 days, then 90days+)
You can do:
select case
when datediff(now(), date) >= 90 then '90 days+'
when datediff(now(), date) >= 60 and datediff(now(), date) < 90 then '60-90 days'
when datediff(now(), date) >= 30 and datediff(now(), date) < 60 then '30-60 days'
else '< 30 days'
end case f,
sum(amount)
from your_table
group by f;
Related
I have this working SQL code (MySql), that returns number of Seconds a MState value have been eg. 4
SELECT SUM(Seconds_In_State) From
(SELECT
Time_Stamp,
MState,
(-1 * TIMESTAMPDIFF(Minute, LEAD(Time_Stamp) OVER(ORDER BY Time_Stamp), Time_Stamp))
AS Seconds_In_State
FROM Mstate
WHERE DATE(`Time_Stamp`) = CURDATE()
AND TIME(`Time_Stamp`) >= '08:05' /*ShiftStart input tag*/
ORDER BY Time_Stamp) AS T
Where MState = 4;
The result will not include the 300 seconds, where MState have been 4, from 08:05 to 08:10. Since I look at timestamps > 08:05. But I would like to include the time from 08:05 to 08:10 where the MState have been 4.
So it finds how many seconds from Time_Stamp where MState is 4, to next timestamp. From 08:05, and sums them together. If MState 4 have timestamp 08:00, it is not included. But I would like to include the seconds from 08:05 to 08:10, where MState is 4.
So if the MState prior to Time_Stamp >= 08:05, have value 4, add seconds from 08:05 to 08:10, to Seconds_In_State.
Data
|Time_Stamp | MState |
|------------------------|------- |
|2021-04-23 07:50:00 | 3 |
|2021-04-23 08:00:00 | 4 |
|2021-04-23 08:10:00 | 1 |
|2021-04-23 08:22:00 | 2 |
|2021-04-23 08:30:00 | 3 |
|2021-04-23 08:40:00 | 4 |
|2021-04-23 08:50:00 | 1 |
|2021-04-23 09:01:00 | 2 |
|2021-04-23 09:10:00 | 3 |
Result from current code:
|SUM(Seconds_In_State) |
|600 |
Result I would like to get:
|SUM(Seconds_In_State) |
|900 |
You would seem to want lag() and a comparison to enforce the timeframe:
SELECT SUM(Seconds_In_State)
FROM (SELECT s.*,
TIMESTAMPDIFF(Minute,
GREATEST(LAG(Time_Stamp) OVER (ORDER BY Time_Stamp), addtime(cast(curdate() as datetime), '08:05')),
Time_Stamp
) AS Seconds_In_State
FROM Mstate s
WHERE DATE(Time_Stamp) = CURDATE() AND
TIME(Time_Stamp) >= '08:05' /*ShiftStart input tag*/
) s
WHERE MState = 4
ORDER BY Time_Stamp
I have a table called primeWeek. Im trying to get weekly avg depends on dates.
Example of my table
id | count | date
1 | 70 | 2020-08-29
2 | 67 | 2020-08-30
3 | 69 | 2020-08-31
4 | 82 | 2020-09-01
5 | 73 | 2020-09-02
I tried few things but results are not correct.
count and date are both keywords in SQL, so you should surround them with backticks.
SELECT
AVG(`count`) AS primeCount,
CONCAT(`date`, '-', `date` + INTERVAL 6 DAY) AS week
FROM primeWeek
GROUP BY WEEK(`date`)
ORDER BY WEEK(`date`);
I have a table like a table below.
I want to select count and group by day.
But the data in 1 day will start counts at 7:00:00 until tomorrow at 6:59:59 (24hr.).
For example
Day 1 data between '2019/06/01 7:00:00' and '2019/06/02 06:59:59'
Day 2 data between '2019/06/02 7:00:00' and '2019/06/03 06:59:59'
How can I code the where condition?
id | create_date | judge |
-----+---------------------+---------+
1 | 2019-06-02 8:00:00 | ok |
2 | 2019-06-02 9:00:00 | ok |
3 | 2019-06-02 10:00:00 | ok |
4 | 2019-06-02 11:00:00 | ok |
5 | 2019-06-02 15:00:00 | ok |
6 | 2019-06-03 4:00:00 | ok |
7 | 2019-06-03 5:00:00 | ok |
8 | 2019-06-03 8:00:00 | ok |
9 | 2019-06-03 9:00:00 | ok |
10 | 2019-06-03 9:00:00 | fail |
I've tried below but the result is not as expected.
SELECT COUNT(*),DAY(create_date)
FROM mytable
WHERE judge = 'ok' and MONTH(create_date) = '6' and YEAR(create_date) = '2019' and TIME(create_date) > '07:00:00'
Group by DAY(create_date) order by DAY(create_date) ASC
Expected results
COUNT(*) | DAY(create_date) |
-----------+---------------------+
7 | 2 | (from id 1 to 7)
2 | 3 | (from id 8 and 9)
You could subtract seven hours from each date, truncate them to show the date only and then group them:
SELECT DATE(DATE_SUB(create_date, INTERVAL 7 HOUR)), COUNT(*)
FROM mytable
-- Where clause if you need it...
GROUP BY DATE(DATE_SUB(create_date, INTERVAL 7 HOUR))
Just subtract 7 hours for the aggregation and the date/time comparisons:
SELECT DATE(create_date - interval 7 hour) as dte, COUNT(*)
FROM mytable
WHERE judge = 'ok' and
create_date >= '2019-06-01 07:00:00' AND
create_date < '2019-07-01 07:00:00'
GROUP BY DATE(create_date - interval 7 hour)
ORDER BY dte;
Try this-
SELECT
CAST(DATE_SUB(create_date, INTERVAL 7 HOUR) AS DATE),
COUNT(*)
FROM YOUR_TABLE
GROUP BY CAST(DATE_SUB(create_date, INTERVAL 7 HOUR) AS DATE)
My sale Table
ID amount created_at
48 10 2018-10-15 10:57:24
49 20 2018-10-16 10:58:14
50 25 2018-10-22 14:07:31
51 24 2018-10-24 12:13:15
52 36 2018-10-24 12:13:21
53 40 2018-10-30 09:46:37
54 40 2018-10-28 09:46:37
55 40 2018-11-1 09:46:37
56 40 2018-11-2 09:46:37
57 40 2018-11-2 09:46:37
58 40 2018-11-2 09:46:37
59 40 2018-11-2 09:46:37
60 40 2018-11-2 09:46:37
My qyery
SELECT Date(created_at),
Count(*)
FROM sale
WHERE Date(created_at) BETWEEN ( Now() - INTERVAL 7 day ) AND Now()
GROUP BY Date(created_at)
My result
date(created_at) count
2018-10-28 12:13:15 1
2018-10-1 09:46:37 1
2018-10-2 09:46:37 5
Suppose Week Start from 2018-10-28 and i need result like below if there is no record of particular day then it will 0.
day count
mon 1
tue 0
wed 0
thu 1
fri 5
sat 0
sun 0
Firstly, you can create a Master Table representing all the Abbreviated Weekday name(s).
Now, we can use Date_format() function with %a specifier to get the abbreviated weekday name for a particular created_at date. We can use this as our Left Join condition.
Left Join allows us to consider all the Weekdays, even if there is no matching created_at for a particular day.
Count(*) will not work here, as it counts all the rows in a group. However, we don't want to count the rows where there is no matching rows. So, we use Count(created_at), as it will ignore the null values.
Finally, query is made sargable by removing Date() function usage in the Where clause.
You will need to use a master table for week days.
Query:
SELECT week_table.day_order,
week_table.wkday AS `day`,
Count(created_at) AS `count`
FROM
(
SELECT 'Mon' AS wkday, 1 AS day_order UNION ALL
SELECT 'Tue', 2 UNION ALL
SELECT 'Wed', 3 UNION ALL
SELECT 'Thu', 4 UNION ALL
SELECT 'Fri', 5 UNION ALL
SELECT 'Sat', 6 UNION ALL
SELECT 'Sun', 7
) AS week_table
LEFT JOIN sale AS s
ON DATE_FORMAT(created_at, '%a') = week_table.wkday AND
created_at >= ( CURDATE() - INTERVAL 7 day ) AND
created_at < ( CURDATE() + INTERVAL 1 day )
GROUP BY week_table.day_order, week_table.wkday
ORDER BY week_table.day_order;
Result
| day_order | day | count |
| --------- | --- | ----- |
| 1 | Mon | 0 |
| 2 | Tue | 1 |
| 3 | Wed | 0 |
| 4 | Thu | 1 |
| 5 | Fri | 5 |
| 6 | Sat | 0 |
| 7 | Sun | 1 |
View on DB Fiddle
This question has been asked before but I am facing a slightly different problem.
I have a table which logs events and stores their timestamps (as datetime). I need to be able to break up time into chunks and get number of events that occurred in that interval. The interval can be custom (Say from 5 minutes to 1 hour and even beyond).
The obvious solution is to convert the datetime to unix_timestamp divide it by number of seconds in the interval, take its floor function and multiply it back by the number of seconds. Finally convert the unix_timestamp back to the datetime format.
This works fine for small intervals.
select
from_unixtime(floor(unix_timestamp(event.timestamp)/300)*300) as start_time,
count(*) as total
from event
where timestamp>='2012-08-03 00:00:00'
group by start_time;
This gives the correct output
+---------------------+-------+
| start_time | total |
+---------------------+-------+
| 2012-08-03 00:00:00 | 11 |
| 2012-08-03 00:05:00 | 4 |
| 2012-08-03 00:10:00 | 4 |
| 2012-08-03 00:15:00 | 7 |
| 2012-08-03 00:20:00 | 8 |
| 2012-08-03 00:25:00 | 1 |
| 2012-08-03 00:30:00 | 1 |
| 2012-08-03 00:35:00 | 3 |
| 2012-08-03 00:40:00 | 3 |
| 2012-08-03 00:45:00 | 5 |
~~~~~OUTPUT SNIPPED~~~~~~~~~~~~
But if I increase the interval to say 1 hour (3600 sec)
mysql> select from_unixtime(floor(unix_timestamp(event.timestamp)/3600)*3600) as start_time, count(*) as total from event where timestamp>='2012-08-03 00:00:00' group by start_time;
+---------------------+-------+
| start_time | total |
+---------------------+-------+
| 2012-08-02 23:30:00 | 35 |
| 2012-08-03 00:30:00 | 30 |
| 2012-08-03 01:30:00 | 12 |
| 2012-08-03 02:30:00 | 18 |
| 2012-08-03 03:30:00 | 12 |
| 2012-08-03 04:30:00 | 4 |
| 2012-08-03 05:30:00 | 3 |
| 2012-08-03 06:30:00 | 13 |
| 2012-08-03 07:30:00 | 269 |
| 2012-08-03 08:30:00 | 681 |
| 2012-08-03 09:30:00 | 1523 |
| 2012-08-03 10:30:00 | 911 |
+---------------------+-------+
The reason, as far as I could gauge, for the boundaries not being set properly is that unix_timestamp will convert time from my local timezone (GMT + 0530) to UTC and then output the numerical value.
So a value like 2012-08-03 00:00:00 will actually be 2012-08-02 18:30:00. Dividing and using floor will set the minutes part to 00. But when I use from_unixtime, it will convert it back to GMT + 0530 and hence give me intervals that begin at 30 mins.
How do I ensure the query works correctly irrespective of the timezone? I use MySQL 5.1.52 so to_seconds() is not available
EDIT:
The query should also fire correctly irrespective of the interval (can be hours, minutes, days). A generic solution would be appreciated
You can use TIMESTAMPDIFF to group by intervals of time:
For a specified interval of hours, you can use:
SELECT '2012-08-03 00:00:00' +
INTERVAL FLOOR(TIMESTAMPDIFF(HOUR, '2012-08-03 00:00:00', timestamp) / <n>) * <n> HOUR AS start_time,
COUNT(*) AS total
FROM event
WHERE timestamp >= '2012-08-03 00:00:00'
GROUP BY start_time
Replace the occurances of 2012-08-03 00:00:00 with your minimum input date.
<n> is your specified interval in hours (every 2 hours, 3 hours, etc.), and you can do the same for minutes:
SELECT '2012-08-03 00:00:00' +
INTERVAL FLOOR(TIMESTAMPDIFF(MINUTE, '2012-08-03 00:00:00', timestamp) / <n>) * <n> MINUTE AS start_time,
COUNT(*) AS total
FROM event
WHERE timestamp >= '2012-08-03 00:00:00'
GROUP BY start_time
Where <n> is your specified interval in minutes (every 45 minutes, 90 minutes, etc).
Be sure you're passing in your minimum input date (in this example 2012-08-03 00:00:00) as the second parameter to TIMESTAMPDIFF.
EDIT: If you don't want to worry about which interval unit to pick in the TIMESTAMPDIFF function, then of course just do the interval by seconds (300 = 5 minutes, 3600 = 1 hour, 7200 = 2 hours, etc.)
SELECT '2012-08-03 00:00:00' +
INTERVAL FLOOR(TIMESTAMPDIFF(SECOND, '2012-08-03 00:00:00', timestamp) / <n>) * <n> SECOND AS start_time,
COUNT(*) AS total
FROM event
WHERE timestamp >= '2012-08-03 00:00:00'
GROUP BY start_time
EDIT2: To address your comment pertaining to reducing the number of areas in the statement where you have to pass in your minimum parameter date, you can use:
SELECT b.mindate +
INTERVAL FLOOR(TIMESTAMPDIFF(SECOND, b.mindate, timestamp) / <n>) * <n> SECOND AS start_time,
COUNT(*) AS total
FROM event
JOIN (SELECT '2012-08-03 00:00:00' AS mindate) b ON timestamp >= b.mindate
GROUP BY start_time
And simply pass in your minimum datetime parameter once into the join subselect.
You can even make a second column in the join subselect for your seconds interval (e.g. 3600) and name the column something like secinterval... then change the <n>'s to b.secinterval, so you only have to pass in your minimum date parameter AND interval one time each.
SQLFiddle Demo
the easier method would be:
Method1
select date(timestamp) as date_timestamp, hour(timestamp) as hour_timestamp, count(*) as total
from event
where timestamp>='2012-08-03 00:00:00'
group by date_timestamp, hour_timestamp
if you would like to use your original approach.
Method2
select from_unixtime(floor(unix_timestamp(event.timestamp-1800)/3600)*3600+1800) as start_time,
count(*) as total
from event
where timestamp>='2012-08-03 00:00:00'
group by start_time;
EDIT1
for the first method, it also allows user to set different interval.
For example, if user wants the log to group by 15 minutes,
select date(time) as date_timestamp,
hour(time) as hour_timestamp,
floor(minute(time) as minute_timestamp / 15) * 15 as minute_timestamp
count(*) as total
from event
group by date_timestamp, hour_timestamp, minute_timestamp