Averaging based on week count in mysql - mysql

I'm trying to finalize a query I have that is wanting an average of two metrics, inbound calls and missed calls. But I've never worked this granularly with the day of week and each hour block so I'm not sure if I'm even totalling correctly, let alone get the right average.
Basically, starting at 1/1/18, I want an average of inbound calls and missed calls for each hour from 7am to 6pm monday to friday. We were closed 7 days, and I'm getting 48 rows, so that's what I expect.
So if the last 3 mondays looked like:
creationtimestamp | Legtype1 | answered
07/23/18 08:15:00 | 2 | 0
07/23/18 08:25:00 | 2 | 1
07/23/18 08:35:00 | 2 | 1
07/30/18 08:15:00 | 2 | 0
07/30/18 08:25:00 | 2 | 0
07/30/18 08:35:00 | 2 | 0
07/30/18 08:45:00 | 2 | 1
07/30/18 08:55:00 | 2 | 0
08/06/18 08:15:00 | 2 | 0
08/06/18 08:25:00 | 2 | 1
08/06/18 08:35:00 | 2 | 0
08/06/18 08:45:00 | 2 | 0
That's a total of 12 calls, 4 missed, for monday between 8 and 9 am. If I were querying those three mondays from 8 to 9 I would expect:
Monday | 8 | 4 | 1.3
But I can't figure out how to take the sum of all calls for each individual week day, sum those and divide by the number of that weekday? My query below I currently have ```SUM''' instead of average but I'm not sure how to take the average I needs since it's hinging on the number of each individual weekday.
SELECT
dayname(s.creationtimestamp) as day, -- weekdays
HOUR(s.creationtimestamp) as Hour, -- Hours
sum(case when legtype1 = 2 then 1 else 0 end) as total_calls, -- total inbound
sum(case when legtype1 = 2 and answered = 0 then 1 else 0 end)as total_missed
FROM session s
WHERE (s.creationtimestamp >= '2018-01-01' AND creationtimestamp < now())
and WEEKDAY(s.creationtimestamp) BETWEEN 0 AND 4 -- Monday through friday
AND HOUR(s.creationtimestamp) between 7 and 18 -- 7am to 6pm
GROUP BY dayname(s.creationtimestamp), HOUR(s.creationtimestamp)
order by dayofweek(s.creationtimestamp), hour asc;
To reiterate: The query works but I'm not sure if I'm aggregating correctly based on each weekday and hour block from the 1st of the year to now.
here's a fiddle:
http://sqlfiddle.com/#!9/7b6b72

I think all things are fine for avg you just a bit more, hope below query will help you
select day,Hour,Avg(total_calls) as avg_total_calls,
Avg(total_missed) as avg_total_missed from
(
SELECT
dayname(s.creationtimestamp) as day, -- weekdays
HOUR(s.creationtimestamp) as Hour, -- Hours
sum(case when legtype1 = 2 then 1 else 0 end) as total_calls, -- total inbound
sum(case when legtype1 = 2 and answered = 0 then 1 else 0 end)as total_missed
FROM session s
WHERE (s.creationtimestamp >= '2018-01-01' AND creationtimestamp < now())
and WEEKDAY(s.creationtimestamp) BETWEEN 0 AND 4 -- Monday through friday
AND HOUR(s.creationtimestamp) between 7 and 18 -- 7am to 6pm
GROUP BY dayname(s.creationtimestamp), HOUR(s.creationtimestamp)
) as T group by day,Hour
order by day,Hour asc

Related

Getting the list of all months and the number of products ordered for the month, and should have '0'for the month with no orders - mysql

I have a set of data that looks like this
Cust Id order date Ordered Product
1 Jan 2 1
1 Jan 5 2
1 March 14 1
1 September 9 1
1 December 12 2
2 Jan 5 1
2 Feb 13 2
3 March 12 2
3 April 5 3
3 June 10 2
and my output should look like this
Cust Id order Date Order product
1 Jan 31 3
1 feb 29 0
1 Mar31 1
1 Apr 30 0
1 May 31 0
1 June 30 0
1 July 31 0
1 Aug 31 0
1 Sept 30 1
1 oct 31 0
1 Nov 30 0
1 Dec 31 2
and I have got this far
1 January 31 3
1 March 31 1
1 September 30 1
1 December 31 2
and my code is
select customer_id,
date_format(last_day(order_date), '%M %d') as new_months,
sum(products_ordered) as total
from amazon_test
where customer_id =1
group by new_months, customer_id;
I currently stuck at the part where I need to have all the months and '0' as the output since no orders were made.
If you are running MySQL 8.0, one option is to use a recursive query to generate the months, and then bring the table with a left join:
with recursive months as (
select customer_id, date_format(min(order_date), '%Y-%m-01') order_date, max(order_date) max_order_date
from amazon_test
group by customer_id
union all
select customer_id, order_date + interval 1 month, max_order_date
from months
where order_date + interval 1 month < max_order_date
)
select
m.customer_id,
date_format(last_day(m.order_date), '%M %d') new_months,
coalesce(sum(t.products_ordered), 0) ordered_products
from months m
left join amazon_test t
on t.customer_id = m.customer_id
and t.order_date >= m.order_date
and t.order_date < m.order_date + interval 1 month
where m.customer_id = 1
group by m.customer_id, m.order_date
order by m.customer_id, m.order_date
I phrased the query so it actually operates on all customer_ids - if you remove the where clause in the outer query, you do get the results for all customers. If you really want the results for only one customer, you can optimize the query by pushing the where filter to the anchor of the recusive query.
Demo on DB Fiddle:
customer_id | new_months | ordered_products
----------: | :----------- | ---------------:
1 | January 31 | 3
1 | February 29 | 0
1 | March 31 | 1
1 | April 30 | 0
1 | May 31 | 0
1 | June 30 | 0
1 | July 31 | 0
1 | August 31 | 0
1 | September 30 | 1
1 | October 31 | 0
1 | November 30 | 0
1 | December 31 | 2

Select daily amount of specific values from same column [duplicate]

This question already has answers here:
need to return two sets of data with two different where clauses
(2 answers)
Closed 8 years ago.
I'm building a feedback tool, and I have a feedback table that the following structure:
ID | Satisfaction | Timestamp
--------------------------------------------
1 0 2014-01-01 00:00:00
2 5 2014-01-01 00:00:00
3 10 2014-01-02 00:00:00
4 5 2014-01-02 00:00:00
5 10 2014-01-03 00:00:00
6 0 2014-01-03 00:00:00
7 10 2014-01-03 00:00:00
8 5 2014-01-04 00:00:00
9 5 2014-01-04 00:00:00
How can I get a daily count of the number of each "satisfaction" value?
For example:
Date | 0's | 5's | 10's
--------------------------------------
2014-01-01 | 1 | 1 | 0
2014-01-02 | 0 | 1 | 1
2014-01-03 | 1 | 0 | 2
2014-01-04 | 0 | 2 | 0
I'd imagine it involves a GROUP BY timestamp, but I'm not sure how to select
The simplest way to pivot this data in MySQL:
select date(timestamp),
sum(satisfaction = 0) as zeroes,
sum(satisfaction = 5) as fives,
sum(satisfaction = 10) as tens
from feedback
group by date(timestamp);
Solved! I was able to grab the counts of the individual values using a combination of sum() and case statements.
SELECT
DATE(timestamp),
IFNULL(sum(case when satisfaction = 0 then 1 end), 0) as 'unhappy',
IFNULL(sum(case when satisfaction = 5 then 1 end), 0) as 'neutral',
IFNULL(sum(case when satisfaction = 10 then 1 end), 0) as 'happy'
FROM feedback
GROUP BY DATE(timestamp)

mysql complex query for monthly report

employee makes entry in the following table when starting new task
from home or office
[tablename=CHECK]
c_id c_sdate c_emp c_task
-------------------------------------------------
1 2013-05-01 01:01:00 1 26 //date 01 from home-----
2 2013-05-01 08:11:00 1 27 //date 01 from office--- Present
3 2013-05-02 03:41:00 1 28 //date 02 from home---
4 2013-05-02 09:12:00 1 29 //date 02 from office-
5 2013-05-02 22:32:00 1 30 //date 02 from home---Present
6 2013-05-03 01:43:00 1 31 //date 03 from home
7 2013-06-03 23:25:00 1 32 //date 03 from home----------Homework
8 2013-06-03 02:15:00 2 33 //other employee
an employe will be considered as present if there 1 or many records where time between 8am and 8pm
an employe will be considered as workedFromHome if there 1 or many records where time NOT between 8am and 8pm, and not present on that day
note: do not count a day as workedFromHome if there is any record time between 8am and 8pm (means workedFromHome is only counted if he is not resent on that day)
I want to display monthly report of a employee eg. c_emp=1 for month eg. 5
like this in 1 query
c_emp presentCount HW_Count
1 3 1
or separatly query 1
c_emp presentCount
1 3
and query 2
c_emp HW_Count
1 1
I have tried for counting present working fine
select count(distinct(date_format(c_sdate,'%e'))) as count
from ita_check
where date_format(c_sdate,'%m')=5
and c_emp=1
and date_format(c_sdate,'%H%i')>=800
and date_format(c_sdate,'%H%i')<=2000
and for counting fromHome giving wrong count
select count(distinct(date_format(c_sdate,'%e'))) as count
from ita_check
where date_format(c_sdate,'%m')=5
and c_eid=1
and c_id not in (
select c_id
from ita_check
where date_format(c_sdate,'%m')=5
and c_eid=1
and (date_format(c_sdate,'%H%i')<=800 or date_format(c_sdate,'%H%i')>=2000)
)
and date_format(c_sdate,'%H%i')<800
or date_format(c_sdate,'%H%i')>2000
in above query for counting Working
the sub query returns 1 and 2
while the outer eliminate c_id=2 but not c_id=1
Try this query
SELECT c_emp,
sum(if(cnt>=1,1,0)) as Office,
count(*)-sum(if(cnt>=1,1,0)) as WFH from (
select c_emp, Date(c_sdate),
sum(if(c_sdate BETWEEN Date(c_sdate) + interval 8 hour
AND Date(c_sdate) + interval 20 hour, 1, 0)) as cnt
from table1
group by c_emp, Date(c_sdate)) tmp
group by c_emp
SQL FIDDLE:
| C_EMP | OFFICE | WFH |
------------------------
| 1 | 2 | 2 |
| 2 | 0 | 1 |
For monthly report
SELECT c_emp, date_format(c_date, '%c %Y') as Mnth,
sum(if(cnt>=1,1,0)) as Office,
count(*)-sum(if(cnt>=1,1,0)) as WFH from (
select c_emp, Date(c_sdate) as c_date,
sum(if(c_sdate BETWEEN Date(c_sdate) + interval 8 hour
AND Date(c_sdate) + interval 20 hour, 1, 0)) as cnt
from table1
group by c_emp, Date(c_sdate)) tmp
group by c_emp,Mnth
SQL FIDDLE:
| C_EMP | MNTH | OFFICE | WFH |
---------------------------------
| 1 | 5 2013 | 2 | 1 |
| 1 | 6 2013 | 0 | 1 |
| 2 | 6 2013 | 0 | 1 |

date_add and repeat forever

I got an alert table for users, in which we have to send alerts to users in user defined intervals like 0 ( only once), 3 months, 6 months, 1 year
So I designed a table like this
id | user_id | alert_date | repeat_int
-----+--------------+-------------------------+-------------
12 | 747 | 2013-04-19 00:00:00 | 0
13 | 746 | 2013-03-19 00:00:00 | 1
14 | 745 | 2012-04-19 00:00:00 | 0
15 | 744 | 2013-04-19 00:00:00 | 0
16 | 743 | 2013-05-19 00:00:00 | 0
We are sending alert just a day before "alert_date"
With the following query I can fetch the data
SELECT al.id,
al.user_id,
al.alert_date,
al.repeat_int AS repunit
FROM alerts AS al
WHERE DATE_ADD(alert_date,INTERVAL repeat_int MONTH)=date_add(CURRENT_DATE,INTERVAL 1 DAY)
OR date(al.alert_date)=date_add(CURRENT_DATE,INTERVAL 1 DAY)
Its working file but my real problem is
The repeat will only works once, we need it repeat every interval
ie. If alert date is 2012-03-14 and repeat_int is 0 - Need to work only once
but if alert date is 2012-03-14 and repeat_int is 1 - Need to work in every 14th from 2012-03-14
and if the alert date is 2012-03-14 and repeat_int is 3 - Need to work in every three month's 14. ie alert on 2012-03-14, 2012-06-14, 2012-09-14 etc...
Is there any way to do that?
Update
The OP has changed his schema in response to comments, so the query is essentially:
SELECT *
FROM alerts
WHERE CURRENT_DATE + INTERVAL 1 DAY = COALESCE(next_alert_date, alert_date);
This handles "next_alert_date" being NULL on the very first run.
Original answer
For the original schema:
SELECT *
FROM alerts
JOIN (SELECT CURRENT_DATE + INTERVAL 1 DAY AS tomorrow) d
WHERE -- We want to alert if
-- 1. Tomorrow is the alert_date
tomorrow = alert_date
OR
--
-- 2. Tomorrow is "repeat_int" months removed from alert_date, falling on
-- the same day of the month or on the end of the month if the original
-- alert_date day of month is later in the month than is possible for us
-- now. E.g., 2013-01-31 repeated monthly is adjusted to 2013-02-28.
(
PERIOD_DIFF(DATE_FORMAT(tomorrow, '%Y%m'), DATE_FORMAT(alert_date, '%Y%m'))
MOD repeat_int = 0
AND
-- Make sure we are at the same day of the month
( (DAYOFMONTH(tomorrow) = DAYOFMONTH(alert_date)
OR
-- Or, if the day of the alert is beyond the last day of our month,
-- that we are at the end of our month.
(LAST_DAY(alert_date) > LAST_DAY(tomorrow)
AND
DAYOFMONTH(tomorrow) = LAST_DAY(tomorrow)) )
);

mysql conditional summation

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;