order by with group by sub query not working - mysql

I have a simple stats query in mysql.
I had it working using php but was keen to keep it in the database so I found another question on it here. Thanks to that I have it working in mysql now.
I need to get a running total count of records submitted per day.
I have the running total working but no matter what I change it simply will not order by day.
It will order by the running total column but not the day.
Any input is appreciated.
SET #runtot:=0;
SELECT
q1.`Day` AS 'Days of month',
q1.`Record count` AS 'Record count',
(#runtot := #runtot + q1.`Record count`) AS 'rt'
FROM
(SELECT
FROM_UNIXTIME(tr.`tr_last_update`, '%e') AS 'Day',
COUNT(tr.tr_id_pk) AS 'Record Count'
FROM records_table AS tr
GROUP BY FROM_UNIXTIME(tr.`tr_last_update`,'%e')
) AS q1
ORDER BY 'Days of month';
Days of month Record count rt
10 13 13
11 2 15
7 255 270
8 173 443
9 166 609

It's being treated as a string. Try casting to an int:
SELECT
q1.`Day` AS 'Days of month',
...
ORDER BY CAST(q1.`Day` AS signed integer)

Unless I'm much mistaken, isn't your whole query the same as:
SELECT DAYOFMONTH(FROM_UNIXTIME(tr_last_update)) AS `Days of month`,
COUNT(tr_id_pk) AS `Record Count`,
#runtot := #runtot + COUNT(tr_id_pk) AS rt
FROM records_table, (SELECT #runtot:=0) AS z
GROUP BY `Days of month`
ORDER BY `Days of month`

Related

MySQL - how do I force AVG function to include 0's

set #cumulativeSum := 0;
SELECT
z.hour+1 as time,
(#cumulativeSum := #cumulativeSum + z.enquiries) as target
FROM
( SELECT
y.hour as hour,
IFNULL(ROUND(AVG(y.enquiries), 0), 0) as enquiries
FROM
( SELECT DAY(o.date_created),
HOUR(o.date_created) as hour,
COUNT(DISTINCT o.phone) as enquiries
FROM orders o
WHERE phone IS NOT NULL
AND name NOT LIKE 'test%'
AND o.email NOT LIKE 'jawor%'
AND o.email NOT LIKE 'test%'
AND o.email NOT LIKE '%moneyshake%'
AND o.date_created < CURRENT_DATE()
AND o.date_created >= DATE(NOW()) + INTERVAL -7 DAY
GROUP BY HOUR(o.date_created), DAY(o.date_created) ) y
GROUP BY hour ) z
This query is meant to give an average number of enquiries by hour for the last 7 days. I've done it this way to exclude duplicates of o.phone only within each hour of each day, rather than across all days or all hours.
It outputs:
time
target
1
1
2
2
3
3
5
4
etc..
etc..
I want it to include a row for 4am, even if the value for target doesn't change (because the AVG for 4am is 0)
Please let me know if more info is needed!
Credit to Akina's comment for solving

Amount of time for giving the date

I'm trying to get a report in MySQL for consecutive days based on activity recorded. I have the start date&time and end date&time of a given status. My goal is to receive a report in a form:
Status|Date|Sum of activity
The problem that I've encountered is that some activities start i.e. 2019-12-12 18:21:12 and ends the next day 2019-12-13 03:21:12. Is there a way to let's say split the result for one date until 23:59:59 and add the rest of time to the following day? So far I have a code below, but it just sums the timestampdiff.
USE db;
SELECT
table1.status,
left(table1.start_time, 7) ' Date',
sec_to_time(
sum(
timestampdiff(
second,
table1.start_time,
(
case when table1.end_time is null then now() else table1.end_time end
)
)
)
) 'Sum of activity'
FROM
table1
GROUP by 1,2
Update : Let me clarify a bit my question. I have some activities that take for example 36 hours, starting on 2019-12-20 and ending on 2019-12-22. I need a composed monthly report with each day in the month selected from start_time, so for the example described above (36h over 3 days) I would like to get:
Activity1|2019-12-20|3h
Activity1|2019-12-21|24h
Activity1|2019-12-22|9h
Update2: Thank you for the 2nd update,but the proposed code works only for the first record in the dataset (for more records the time is not summed up) and doesn't take into account the activity type. I will provide more data maybe it will help:
Activity start_time end_time
1048 2019-12-27 06:42:51 2019-12-27 07:11:42
1048 2019-12-29 07:07:11 2019-12-29 07:08:59
1048 2019-12-29 07:09:19 2019-12-29 07:21:10
2066 2019-12-25 07:08:00 2019-12-25 19:01:17
2066 2019-12-25 19:01:17 2019-12-26 06:55:15
2066 2019-12-26 06:55:15 2019-12-26 18:20:51
You can use date() function :
select status, date(start_time) as date, count(*) as "Sum of activities"
from table1
group by status, date(start_time);
Demo
Update (depending on your comment): Try to use
select status, date(start_time) as date,
sec_to_time(sum(timestampdiff(second,
start_time,
(case
when end_time is null then
now()
else
end_time
end))))
as "Sum of activities"
from table1
group by status, date(start_time);
Update2 : To accomplish the last mentioned duty, need to generate rows firstly :
select date1,
extract( hour from
sec_to_time(
sum(case
when date1 = date(start_time) then
timestampdiff(second,start_time,date2)
when date1 = date(end_time) then
timestampdiff(second,date1,end_time)
else
timestampdiff(second,date1,date2)
end
)) ) as "Time Difference as hour"
from
(
select #cr := #cr + 1 as rn,
date_sub(date(end_time), interval date(end_time)-date(start_time) - #cr + 1 day) as date1,
date_sub(date(end_time), interval date(end_time)-date(start_time) - #cr day) as date2,
start_time, end_time
from information_schema.tables c1
cross join ( select #cr := 0 ) r
cross join table1 t
where #cr < date(end_time)- date(start_time)+1
) q
group by date1;
Demo 2
removing extract( hour from ) part you can get the whole difference upto second precision.

Find number of "active" rows each month for multiple months in one query

I have a mySQL database with each row containing an activate and a deactivate date. This refers to the period of time when the object the row represents was active.
activate deactivate id
2015-03-01 2015-05-10 1
2013-02-04 2014-08-23 2
I want to find the number of rows that were active at any time during each month. Ex.
Jan: 4
Feb: 2
Mar: 1
etc...
I figured out how to do this for a single month, but I'm struggling with how to do it for all 12 months in a year in a single query. The reason I would like it in a single query is for performance, as information is used immediately and caching wouldn't make sense in this scenario. Here's the code I have for a month at a time. It checks if the activate date comes before the end of the month in question and that the deactivate date was not before the beginning of the period in question.
SELECT * from tblName WHERE activate <= DATE_SUB(NOW(), INTERVAL 1 MONTH)
AND deactivate >= DATE_SUB(NOW(), INTERVAL 2 MONTH)
If anybody has any idea how to change this and do grouping such that I can do this for an indefinite number of months I'd appreciate it. I'm at a loss as to how to group.
If you have a table of months that you care about, you can do:
select m.*,
(select count(*)
from table t
where t.activate_date <= m.month_end and
t.deactivate_date >= m.month_start
) as Actives
from months m;
If you don't have such a table handy, you can create one on the fly:
select m.*,
(select count(*)
from table t
where t.activate_date <= m.month_end and
t.deactivate_date >= m.month_start
) as Actives
from (select date('2015-01-01') as month_start, date('2015-01-31') as month_end union all
select date('2015-02-01') as month_start, date('2015-02-28') as month_end union all
select date('2015-03-01') as month_start, date('2015-03-31') as month_end union all
select date('2015-04-01') as month_start, date('2015-04-30') as month_end
) m;
EDIT:
A potentially faster way is to calculate a cumulative sum of activations and deactivations and then take the maximum per month:
select year(date), month(date), max(cumes)
from (select d, (#s := #s + inc) as cumes
from (select activate_date as d, 1 as inc from table t union all
select deactivate_date, -1 as inc from table t
) t cross join
(select #s := 0) param
order by d
) s
group by year(date), month(date);

interval by 4 using sql - Mysql

I've a table and i want that data is interval by 4 or, when i'm using modulo the record is not that i expected, PFB `
SELECT (DATE_FORMAT(subscribed_from, '%Y-%m')) AS date_ FROM subscription
WHERE operator = 'tim'
AND DATE_FORMAT(subscribed_from, '%Y-%m-%d') BETWEEN '2013-01-01' AND '2014-12-31'
GROUP BY (DATE_FORMAT(subscribed_from, '%Y-%m'));
it will show record like this
2013-01
2013-02
2013-03
2013-04
2013-05
2013-06
2013-07
2013-08
2013-09
i want take only data interval by 4, this below is record that i expected.
2013-01
2013-05
2013-09
2014-02
and also for interval by 2, this below record is that i expected
2013-01
2013-03
2013-05
2013-07
2013-09
if i using modulo % 2 it will start from 2013-01 and jump by 2, but the problem if the where range i want to start from 2013-02, 02 it self not showing on the result. so if the where clause the month start from 2 it will given the interval such as 2,4,6,8,10,12
SELECT date_, SUM(the_metric_you_want_to_aggregate)
FROM (
SELECT 4*FLOOR(
(DATE_FORMAT(subscribed_from, '%Y%m') - 201301)
/4) AS date_,
the_metric_you_want_to_aggregate
FROM subscription
WHERE operator = 'tim'
AND subscribed_from BETWEEN 20130101000000 AND 201412315959
) AS ilv
GROUP BY date_
(where 201301 is the year/month start of the range you are selecting by - assuming that is the reference for the 4-month aggregation)
Note that enclosing column references in functions (...DATE_FORMAT(subscribed_from, '%Y-%m-%d') BETWEEN...) prevents the use of indexes.
You have to use variables. Here is sample for interval by 4.
SET #row_number:=0;
SELECT date_ from (
SELECT (DATE_FORMAT(subscribed_from, '%Y-%m')) AS date_,#row_number:=#row_number+1 FROM subscription
WHERE operator = 'tim' AND DATE_FORMAT(subscribed_from, '%Y-%m-%d') BETWEEN '2013-01-01' AND '2014-12-31'
GROUP BY (DATE_FORMAT(subscribed_from, '%Y-%m'))
) as tbl where #row_number % 4=0;
let says i'm using this method to generate the intevals, but i want the start number is from my input, let says it start from 4 and if the condition put %4 should be the output is 4, 8 ,12 ....
enter code here
SET #row:=0;
SELECT *
FROM (
SELECT
#row := #row +1 AS rownum
FROM (
SELECT #row) r, subscription
) ranked
WHERE rownum %4 = 1

MYSQL Query to find changes in count

I have a great SQL query (provided by the very helpful FoxyGen) which returns a specific subset set of information from large set of metrics.
SELECT SUM(metric_activeSessions),
MAX(metric_timeStamp) as max_time,
MIN(metric_timeStamp) as min_time
FROM metrics_tbl
WHERE metric_timeStamp > NOW() - INTERVAL 60 MINUTE
GROUP BY unix_timestamp(metric_timeStamp) div 300
I'm trying to figure out if I can append something to this existing query that will return only adjacent results which have a major negative difference in value.
For example, if the results are:
1. 2334 # 12:01
2. 2134 # 12:05
In this scenario, result 2 has a changed by a count -100.
Thanks in advance for any guidance.
Mitch
You can calculate the difference using variables in MySQL:
SELECT s, max_time, min_time,
(case when (#x := #sprev) is null then NULL
when (#sprev := s) is null the NULL
else #x
end) as sprev
FROM (SELECT SUM(metric_activeSessions) as s, MAX(metric_timeStamp) as max_time,
MIN(metric_timeStamp) as min_time
FROM metrics_tbl
WHERE metric_timeStamp > NOW() - INTERVAL 60 MINUTE
GROUP BY unix_timestamp(metric_timeStamp) div 300
) t CROSS JOIN
(SELECT #sprev := NULL) vars
order by max_time;
You can then test for whatever difference you like using a having clause:
having s - sprev >= 100
or whatever.