MY SQL does not pull data past midnight UTC - mysql

I am really having issue showing the items. I have two different times(they are in UTC).
FOR Eg: startTime - 15:00:00(in UTC) and cutOffTime - 03:00:00(in UTC and supposed to be up to 3am UTC next morning). My local timezone is EST. The issue I am having is after 8pm(EST) when next day start at UTC. My items does not shows up. Here is my sql query. I am pulling startTime and cutOffTime from table2. MY server is in UTC as well.
SELECT a.id, a.column1, a.catID, b.startTime, b.cutOffTime, IFNULL(b.`column2`, 'everyday') as 'availableDays'
FROM table1 a
RIGHT JOIN table2 b on a.catID = b.catID
WHERE id = 012
and CASE WHEN b.startTime IS NOT NULL THEN CONCAT(CURDATE(),' ', b.startTime) < NOW() ELSE 1 END
and CASE WHEN b.cutOffTime IS NOT NULL THEN If((b.startTime is not NULL AND b.cutOffTime < b.startTime), DATE_ADD(CONCAT(CURDATE(),' ', b.cutOffTime), INTERVAL 24 HOUR), CONCAT(CURDATE(),' ', b.cutOffTime)) > NOW() ELSE 1 END
HAVING availableDays in ('everyday', 'wednesday')
Order by a.column1 ASC;
My table2 is like this:
catID Cat startTime cutOffTime
1. Test 15:00:00 03:00:00
2. Test2 null null
3. Test3 15:00:00 null
Suppose it is 8:30pm EST(which is 00:30:00 UTC) then I am expecting to get all the results. Now, It is only returning me 2 and 3.

Related

Combining data from multiple rows into one

First question on here, so i will try my best to be clear.
I have 2 tables:
"TABLE1" which contains a record for each stock code and a list of attributes.
In TABLE 1 there is just one record for each stock_code
"TABLE2" which contains a log of changes to attributes of products, over time.
"TABLE2" contains the following fields:.
stock_code
stock_attribute
old_value
new_value
change_date
change_time
TABLE 2 has multiple entries ofr each stock_code.
Every time a stock item is change, another entry is made in Table2, with the attribute that has changed, the change date, time, old value and new value.
I want to create a query which will result in a table that has one record for each stock_code (from TABLE 1), and a column for each week over past year, with the value in each field being the last recorded "new_val" for that week (From TABLE 2)
I have tried
SELECT a.`stcode`, b.`week1`, b.`week2`, b.`week3`, b.`week4` etc. etc.
from (SELECT stcode, )as a
LEFT JOIN (SELECT stcode,
(CASE WHEN chngdate BETWEEN DATE_SUB(CURDATE(),INTERVAL 363 DAY) AND DATE_SUB(CURDATE(),INTERVAL 357 DAY) THEN newval END)week1,
(CASE WHEN chngdate BETWEEN DATE_SUB(CURDATE(),INTERVAL 356 DAY) AND DATE_SUB(CURDATE(),INTERVAL 350 DAY) THEN newval END)week2,
(CASE WHEN chngdate BETWEEN DATE_SUB(CURDATE(),INTERVAL 349 DAY) AND DATE_SUB(CURDATE(),INTERVAL 343 DAY) THEN newval END)week3,
(CASE WHEN chngdate BETWEEN DATE_SUB(CURDATE(),INTERVAL 342 DAY) AND DATE_SUB(CURDATE(),INTERVAL 336 DAY) THEN newval END)week4,
(etc
etc
etc
FROM (SELECT * from TABLE 2 ORDER BY "chngdate" DESC, "chngtime" DESC )as sub) as b ON b.stcode = s.stcode
ORDER BY stcode ASC
The trouble is with this, i get multiple lines for a stock_code which has mutliple entries....
for example, for stock_code abc123 the result i get is
STCODE WEEK1 WEEK2 WEEK3 WEEK4 WEEK5 WEEK6
abc123 null null 4 null null null
abc123 2 null null null null null
abc123 null null null null 3 null
what i WANT is this:
STCODE WEEK1 WEEK2 WEEK3 WEEK4 WEEK5 WEEK6
abc123 2 null 4 null 3 null
I have also tried the following, but teh query took so long, it never finished (there were 52 derived tables!)
SELECT a.`stcode`, w1.`new_value`, w2.`new_value`, w3.`new_value`, w4.`new_value` etc. etc.
from (SELECT stcode, )as a
LEFT JOIN (SELECT stcode,
LEFT JOIN (SELECT stcode, depot, fieldname, chngdate, chngtime, newval from STDepotAmendmentsLog WHERE chngdate BETWEEN DATE_SUB(CURDATE(),INTERVAL 363 DAY) AND DATE_SUB(CURDATE(),INTERVAL 357 DAY) ORDER BY "chngdate" DESC, "chngtime" DESC)as w1 on s.stcode = w1.stcode
etc for week 2, 3, 4 etc etc
You could do the following:
Find the greatest date for each "week"
Find the rows corresponding to those dates
Use conditional aggregation to convert rows into columns
Here is a rough outline of the code. It assumes that e.g. if today is 2020-03-03 then week 52 is from 2020-02-26 to 2020-03-03. Adjust if necessary:
SELECT t.stock_code
, MAX(CASE WHEN weeknum = 51 THEN new_value END) AS week01
, MAX(CASE WHEN weeknum = 50 THEN new_value END) AS week02
, MAX(CASE WHEN weeknum = 1 THEN new_value END) AS week51
, MAX(CASE WHEN weeknum = 0 THEN new_value END) AS week52
FROM table2 AS t
JOIN (
SELECT stock_code
, DATEDIFF(CURRENT_DATE, change_date) div 7 AS weeknum -- count multiples of 7
, MAX(change_date) AS greatest_date
GROUP BY stock_code, weeknum
FROM table2
) AS a ON t.stock_code = a.stock_code AND t.change_date = a.greatest_date
GROUP BY t.stock_code

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.

mysql LAST_DAY() only reads 1 subquery result, how to process all results? using joins?

I have an insurance policies table like this:
+-------------------------------------------------------------+
| id | cancellation_val | cancellation_interval | expire_date |
+-------------------------------------------------------------+
| 1 | 30 | day | 2019-06-09 |
| 2 | 2 | month | 2019-12-01 |
+-------------------------------------------------------------+
I need to get the ids of the policies that are going to expire based on cancellation, from today and within 4 months, calculating the last day of the month, like this pseudo-code:
'today' <= LAST_DAY( expire_date - cancellation_val/interval ) < 'today + 4 months'
Being not a pro I think I should use JOINs but I don't know how, after days of trying the only thing I achieved was this:
SELECT LAST_DAY(
DATE_FORMAT(
STR_TO_DATE(
(SELECT CASE cancellation_interval
WHEN "day" THEN date_sub(expire_date, INTERVAL cancellation_val DAY)
WHEN "month" THEN date_sub(data_scadenzaexpire_date, INTERVAL cancellation_val MONTH)
END
AS newDate
FROM insurance WHERE id=2
), '%Y-%m-%d'
), '%Y-%m-%d'
)
)
This is working but I don't need the "WHERE id=2" clause (because I need to process ALL rows of the table), and if I remove it I got error "subquery returns more than 1 row".
So how I can proceed? And using the result to stay between 'today' AND 'today + 4 months' ?
I think with some kind of JOIN I could do it in a easier way but I don't know how.
Thank you all
The problem is the structure of the query, not the LAST_DAY function.
We want to return the id values of rows that meet some condition. So the query would be of the form:
SELECT t.id
, ...
FROM insurance t
WHERE ...
HAVING ...
Introducing another SELECT keyword basically introduces a subquery. There are restrictions on subqueries... in the SELECT list, a subquery can return a single column and (at most) a single row.
So let's ditch that extra SELECT keyword.
We can derive the newdate as an expression of the SELECT list, and then we can reference that derived column in the HAVING clause. The spec said we wanted to return the id value, so we include that in the SELECT list. We don't have to return any other columns, but for testing/debugging, it can be useful to return the values that were used to derive the newdate column.
Something like this:
SELECT t.id
, LAST_DAY(
CASE t.cancellation_interval
WHEN 'day' THEN t.expire_date - INTERVAL t.cancellation_val DAY
WHEN 'month' THEN t.expire_date - INTERVAL t.cancellation_val MONTH
ELSE t.expire_date
END
) AS newdate
, t.expire_date
, t.cancellation_interval
, t.cancellation_val
FROM insurance t
HAVING newdate >= DATE(NOW())
AND newdate <= DATE(NOW()) + INTERVAL 4 MONTH
ORDER
BY newdate ASC
We don't have to include the newdate in the SELECT list; we could just replace occurrences of newdate in the HAVING clause with the expression.
We could also use an inline view to "hide" the derivation of the newdate column
SELECT v.id
, v.newdate
FROM ( SELECT t.id
, LAST_DAY(
CASE t.cancellation_interval
WHEN 'day' THEN t.expire_date - INTERVAL t.cancellation_val DAY
WHEN 'month' THEN t.expire_date - INTERVAL t.cancellation_val MONTH
ELSE t.expire_date
END
) AS newdate
FROM insurance t
) v
WHERE v.newdate >= DATE(NOW())
AND v.newdate <= DATE(NOW()) + INTERVAL 4 MONTH
ORDER
BY v.newdate ASC
check this query: remove the HAVING Line to see all rows
SELECT
IF(cancellation_interval = 'day',
i.expire_date - INTERVAL i.`cancellation_val` DAY,
i.expire_date - INTERVAL i.`cancellation_val` MONTH
) as cancellation_day,
i.*
FROM `insurance` i
HAVING cancellation_day < NOW() + INTERVAL 4 MONTH;
SAMPLES
MariaDB [test]> SELECT IF(cancellation_interval = 'day', i.expire_date - INTERVAL i.`cancellation_val` DAY, i.expire_date - INTERVAL i.`cancellation_val` MONTH ) as cancellation_day, i.* FROM `insurance` i HAVING cancellation_day < NOW() + INTERVAL 4 MONTH;
+------------------+----+------------------+-----------------------+-------------+
| cancellation_day | id | cancellation_val | cancellation_interval | expire_date |
+------------------+----+------------------+-----------------------+-------------+
| 2019-05-10 | 1 | 30 | day | 2019-06-09 |
+------------------+----+------------------+-----------------------+-------------+
1 row in set (0.001 sec)
When you use a SELECT query as an expression, it can only return one row.
If you want to process all the rows, you need to call LAST_DAY() inside the query, not on the result.
SELECT *
FROM insurance
WHERE CURDATE() <= LAST_DAY(
expire_date - IF(cancellation_interval = 'day',
INTERVAL cancellation_val DAY,
INTERVAL cancellation_val MONTH))
AND LAST_DAY(expire_date - IF(cancellation_interval = 'day',
INTERVAL cancellation_val DAY,
INTERVAL cancellation_val MONTH)) < CURDATE + INTERVAL 4 MONTH

Mysql query problems (total amount of time)

I’m stuck..
I have the indicators on my heater connected to a raspberry pi. The pi then put the changed state (on and off) with the time into a mysql database.
Before I’ve used PHP to present the data but I’m now trying out Grafana. And I cant get the mysql query to present what I want.
I need to know how much time the heater has been activated (0/1) in the specified time range (typically the last 24h). And I need to take into account that the time range may start with ”0”, meaning that the heater has been on the time before that. And the same if it ends with ”1”.
And maybe also the percent of the day where it’s been activated.
Can someone please help me?
I'm looking for a result like this
+-------+--------------+--------------+
| value | secondsOfDay | PercentOfDay |
+-------+--------------+--------------+
| 0 | 28800 | 33.3 |
| 1 | 57600 | 66.6 |
+-------+--------------+--------------+
I've prepared:
http://sqlfiddle.com/#!9/556364a
Thanks!
To get the required data, you can use the following query:
NOTE: Depending on CURRDATE() the examples will fail. You can ofc. replace CURDATE() with a fixed value like 2018-11-27;
Explanation:
The query is joining the table with itself, taking into account, that on/off follows each other (L.id = R.id -1)
The query is selecting Any result, where "on" or "off" is today.
IF on was yesterday, the on-time is "corrected" to 00:00:00 of today: case when L.Time < CURDATE() then CURDATE() else L.Time end as onTime
IF the last entry is on, the off-time is "corrected" to 00:00:00 of tomorrow: COALESCE(R.time, CURDATE() + Interval 1 day) (note: You maybe want to use NOW() instead of CURDATE() + Interval 1 day, to have the current amount of seconds until "now")
The Same two methods are used to calculate the running seconds.
Query:
SELECT
L.playtime_id AS LID,
R.playtime_id AS RID,
case when L.Time < CURDATE() then CURDATE() else L.Time end as onTime,
COALESCE(R.time, CURDATE() + Interval 1 day) AS offTime,
(
UNIX_TIMESTAMP(COALESCE(R.time, CURDATE() + Interval 1 day)) -
UNIX_TIMESTAMP(case when L.Time < CURDATE() then CURDATE() else L.Time end)
) as RunningSeconds
FROM item0005 as L
LEFT JOIN item0005 AS R
ON L.playtime_id = R.playtime_id -1
WHERE
L.`value` = 1 AND
(
DATE(L.Time) = CURDATE() OR
DATE (R.Time) = CURDATE()
)
;
Result Example:
LID RID onTime offTime RunningSeconds
2 3 2018-11-27T09:00:00Z 2018-11-27T11:26:24Z 8784
4 5 2018-11-27T11:26:27Z 2018-11-27T11:28:29Z 122
6 7 2018-11-27T11:29:39Z 2018-11-27T11:39:55Z 616
8 (null) 2018-11-27T11:50:55Z 2018-11-28T00:00:00Z 43745
Example assuming 00:00:00 of tomorrow: http://sqlfiddle.com/#!9/20bc14/1
Example using NOW() to count up seconds if the last state is on: http://sqlfiddle.com/#!9/3c6227/1
If you just need the Aggregations out of this, you can use another Surrounding Select and calculate the Percentage by knowing a day has 86400 seconds:
SELECT
SUM(RunningSeconds) AS RunningSeconds,
SUM(RunningSeconds) / 86400 * 100 AS PercentageRunning
FROM (
...
) as temp;
http://sqlfiddle.com/#!9/20bc14/5
try this
SELECT VALUE, SUM(TIME_TO_SEC(TIMEDIFF(T2,T1))), 100*SUM(TIME_TO_SEC(TIMEDIFF(T2,T1)))/86400 FROM
(SELECT A.VALUE AS VALUE, COALESCE(B.TIME, TIMESTAMP(CURDATE()-2)) AS T1, A.TIME AS T2
FROM ITEM0005 A
LEFT JOIN ITEM0005 B
ON A.PLAYTIME_ID-1= B.PLAYTIME_ID
WHERE A.VALUE='0'
UNION
SELECT '1' AS VALUE, A.TIME AS T1, COALESCE(B.TIME, TIMESTAMP(CURDATE()-1)) AS T2
FROM ITEM0005 A
LEFT JOIN ITEM0005 B
ON A.PLAYTIME_ID= B.PLAYTIME_ID-1
WHERE A.VALUE='0') C
GROUP BY VALUE;

Compute based on the value of time in mysql

I'm trying to compute a column based on the value of time, i'm not exactly sure what's better to use if I should use an If condition or Case?
Here are my columns
| Time_In | Time_Out | Val1 | Val2 |
| 2014-07-19 04:00:04 | 2014-07-19 08:00:00 | 5 | 15 |
What i'm trying to do is if the time is if the time is between '08:00:00' to '17:00:00' (business hours) it will automatically multiply Val1 and Val2
If the time is '18:00:00' to '23:00:00' (after business hours)
The only step I'm able to get right now is the getting the time in the timestamp cause that's what i'll be comparing
`SELECT date_format(STR_TO_DATE(Time_In, '%Y-%m-%d %H:%i:%s'), '%H:%i:%s')
Output: 04:00:04
`SELECT date_format(STR_TO_DATE(Time_Out, '%Y-%m-%d %H:%i:%s'), '%H:%i:%s')
Output: 08:00:00
How should I construct this?
Thank You for the help
I tried it using the if condition first and this is working :)
SELECT
IF(`Time_In` >= CAST('13:00:01' AS time) AND `Time_Out` <= CAST('15:00:00' AS time),(Val11 * Val2),'false')
FROM table WHERE ID = '5'
And got the output 75 :D
As you assumed you can do that with a CASE construct.
convert your string to a DATETIME value with STR_TO_DATE
extract the TIME part with the TIME function
So you could do it, here with to example values of
'2014-07-19 12:00:00'
'2014-07-19 18:00:00'
with this statement (you would need only one CASE construct of course):
SELECT
CASE
WHEN Time(STR_TO_DATE('2014-07-19 12:00:00', '%Y-%m-%d %H:%i:%s')) BETWEEN '04:00:00' AND '17:00:00'
THEN Val1 * Val2
ELSE NULL
END result,
CASE
WHEN Time(STR_TO_DATE('2014-07-19 18:00:00', '%Y-%m-%d %H:%i:%s')) BETWEEN '04:00:00' AND '17:00:00'
THEN Val1 * Val2
ELSE NULL
END result2
FROM
example;
DEMO