Cast works on one field but not other - sql-server-2008

I have two fields in my table, Cycle and Idle, both are nvarchar(50) and both contain time. I am trying to get a total for each field based on a particular program number using TSQL. Below is a small example of the data.
Id ProgramNo Cycle Idle
209702 3998_BOTH_OPPS.MPF 00:02:41 00:00:25
209703 472_7580_OPP1.MPF 00:02:08 00:01:44
209704 3998_BOTH_OPPS.MPF 00:00:27 00:00:11
209705 3998_BOTH_OPPS.MPF 00:00:00 00:00:00
209706 3998_BOTH_OPPS.MPF 00:00:01 00:01:40
209707 9491_OPP1.MPF 00:00:00 00:00:00
209708 9491_OPP1.MPF 00:00:01 00:00:04
209709 9491_OPP1.MPF 00:01:05 00:00:19
So for example, get the total Cycle time and Idle time for ProgramNo 3998_BOTH_OPPS.MPF and 9491_OPP1.MPF
This is my query...
SELECT
ProgramNo,
cast(dateadd(MILLISECOND, sum(datediff(MILLISECOND, 0, cast(Cycle AS DATETIME))), 0) AS TIME) AS CycleTime,
cast(dateadd(MILLISECOND, sum(datediff(MILLISECOND, 0, cast(Idle AS DATETIME))), 0) AS TIME) AS IdleTime
FROM Cycle
GROUP BY ProgramNo
It works just fine for CycleTime but I get an error for IdleTime:
"Conversion failed when converting date and/or time from character string."
Any suggestions? Thank you in advance.

You need to find values that don't match the HH:MM:SS format.
Here is one simplistic method:
select idle_time
from cycle
where idle_time not like '[0-9][0-9]:[0-9][0-9]:[0-9][0-9]'
If that doesn't work, then look at the components:
where (idle_time like '[0-9][0-9]:[0-9][0-9]:[0-9][0-9]' and
(not (left(idle_time, 2) between '00' and '23') or
not substring(idle_time, 4, 2) between '00' and '59') or
not right(idle_time, 2) between '00' and '59')
)
SQL Server 2012+ makes this much easier with try_convert().

Based on your comment above, you can fix your data like so:
cast(dateadd(MILLISECOND, sum(datediff(MILLISECOND, 0, cast(right(Idle,8) AS DATETIME))), 0) AS TIME) as IdleTime
We are just looking at the right most 8 characters... so if there isn't a #. before your time it will still work
select right('3.18:15:05',8)
select right('18:15:05',8)

Related

SQL Query - Find out how many times a row changes from 0 to another value

I am using MySQL 8 and need to create a stored procedure
I have a single table that has a DATE field and a value field which can be 0 or any other number. This value field represents the daily amount of rain for that day.
The table stores data between today and 10 years.
I need to find out how many periods of rain there will be in the next 10 years.
So, for example, if my table contains the following data:
Date - Value
2018-06-09 - 0
2018-06-10 - 50
2018-06-11 - 0
2018-06-12 - 15
2018-06-13 - 17
2018-06-14 - 0
2018-06-15 - 0
2018-06-16 - 12
2018-06-17 - 123
2018-06-18 - 17
Then the SP should return 3, because there were 3 periods of rain.
Any help in getting me closer to the answer will be appreciated!
You don't need to have a stored procedure for this.
A solution with MySQL's 8.0 LEAD function this supports dates with gaps.
The complete table needs to be scanned but i don't think that a huge problem with ~3560 records.
Query
SELECT
SUM(filter_match = 1) AS number
FROM (
SELECT
((t.value = 0) AND (LEAD(t.value) OVER (ORDER BY t.date ASC) != 0)) AS filter_match
FROM
t
) t
see demo https://www.db-fiddle.com/f/sev4NqgLsFPgtNgwzruwy/2
By the way, would you mind expanding your answer to understand how
LEAD and SUM work together?
LEAD(t.value) OVER (ORDER BY t.date ASC) simply means get the next value from the next record ordered by date.
this demo shows it nicely https://www.db-fiddle.com/f/sev4NqgLsFPgtNgwzruwy/6
SUM(filter_match = 1) is a conditional sum. in this case the alias filter_match needs to be true.
see what filter_match is demo https://www.db-fiddle.com/f/sev4NqgLsFPgtNgwzruwy/8
In MySQL aggregate functions can have a SQL expression something like 1 = 1 (which is always true or 1) or 1 = 0 (which is always false or 0).
The conditional sum only sums up when the condition is true.
see demo https://www.db-fiddle.com/f/sev4NqgLsFPgtNgwzruwy/7
Use MySQL join:
SELECT COUNT(*) Number_of_Periods
FROM yourTable A JOIN yourTable B
ON DATE(A.`DATE`)=DATE(B.`DATE` - INTERVAL 1 DAY)
AND A.`VALUE`=0 AND B.`VALUE`>0;
See Demo on DB Fiddle.

Splitting a Time into Twelve Equal Parts

I am trying to get value of 12 equal parts of the night length.
This is what my table looks like:
sunrise_time sunset_time Day_Length Night_length
2014-01-01 06:02:41.000 2014-01-01 20:44:05.000 14:41:24.0000000 09:18:36.0000000
This is my query, but getting day_length instead of night_light:
select (convert(varchar(10),dateadd(ss,abs(datediff(ss,sunrise_time,sunset_time))/12,0),8)) as nighthour
from table1
Expected output: 00:46:33
Actual output: 01:13:27
What's wrong with my query?
Switch your start date and end date around in the datediff to avoid issues. Here's a small change to your code.
select convert(varchar(10), dateadd(ss, datediff(ss, 0, night_length) / 12, 0), 8)
from table1

mysql query to fetch the quarter month from table

Table name: activity
Field name: ProcessYM
I have mysql data like below.
ProcessYM
==========
201312
201311
201310
201309
201308
201307
201306
201305
201304
201303
201302
201301
201212
201211
201210
201209
201208
201207
201206
I want to fetch the result like below. I mean, the mysql query to fetch the every quarter of the year like 201312, 201309, 201306, 201303, 201212, 201209.. and so on.
Actual Output I expect
=======================
ProcessYM
201312
201309
201306
201303
201212
201209
201206
I have tried the below query, but it does not produce the expected result.
SELECT distinct `ActProcessYM` from `activity` where `ActProcessYM`%3=0 order by ActProcessYM desc
Output of above query
=====================
201312
201309
201306
201303
201210
201207
It is much appreciated for your smart reply.
You need to modulo of the month part only. Your query is implicitly casting your ProcessYM as an INT.
For example:
SELECT DISTINCT ProcessYM
FROM activity
WHERE RIGHT(ProcessYM,2)%3=0
ORDER BY ProcessYM DESC
fiddle
you should retrieve the last two digits from field value and do the logic as you are doing.
SELECT distinct `ActProcessYM` from `activity` where substring(ActProcessYM,5,2)%3=0 order by ActProcessYM desc
Here's a not-so-quick-and-dirty way of handing this date processing. I believe you're looking for a MySQL formula like this:
yyyymm = TRUNC_QUARTER(yyyymm)
That is, you are looking for a function that converts any yyyymm month notation into a notation that shows the month that ends the quarter in question.
Let's start with an expression that converts any particular DATETIME expression to the DATE of the beginning of the quarter.
DATE(CONCAT(YEAR(value),'-', 1 + 3*(QUARTER(value)-1),'-01'))
This takes a timestamp (e.g. '2011-04-20 11:15:01') and turns it into the date of the starting of the quarter. (e.g. '2011-04-01')
Having things in this date form is helpful because you can use them for date arithmetic. For example, you can get the last day of that quarter like this.
DATE(CONCAT(YEAR(value),'-', 1 + 3*(QUARTER(value)-1),'-01'))
+ INTERVAL 1 QUARTER - INTERVAL 1 DAY
Here's a writeup on all this: http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/
I've found it helpful to try to stick to the date datatype when processing time series data.
You need to separate out the month value before doing the modulo 3 (% 3). Doing a modulo 100 first will do it:
(ProcessYM % 100) % 3) = 0
or
mod(mod(ProcessYM,100),3) = 0
Try this,
SELECT distinct `ProcessYM` from `activity` where SUBSTRING(`ProcessYM`,5,2)%3=0 order by ProcessYM desc

MySQL time between returning false and it should be true

SELECT *
FROM tablename
WHERE
MAKETIME(3,0,0) BETWEEN MAKETIME(23,0,0) AND MAKETIME(5,0,0)
is returning nothing And 3:00 is between 23:00 AND 5:00 time. Why is that can anyone explain me how to solve this problem?
It's unclear what you're actually trying to do here, because even if 3 were between 5 and 23 your query would simply return every record in the table.
SELECT MAKETIME(3,0,0) BETWEEN MAKETIME(5,0,0) AND MAKETIME(23,0,0)
Returns 0, because 3 is not between 5 and 23.
SELECT MAKETIME(5,0,0) BETWEEN MAKETIME(3,0,0) AND MAKETIME(23,0,0)
Returns 1, because 5 is between 3 and 23.
Demo: SQL Fiddle
Presumably you're trying to wrap into the previous day, in which case you can directly compare datetime values, but it's unclear given your question what fields/datatypes you're actually working with.
Update:
Based on your comment, I think you want 2 comparisons. 3 is not between 5 and 23, because time doesn't wrap across days. But if you only care about the time portion you can handle it like this:
SELECT *
FROM tablename
WHERE YourTime BETWEEN MAKETIME(23,0,0) AND MAKETIME(23,59,59)
OR YourTime BETWEEN MAKETIME(0,0,0) AND MAKETIME(5,0,0)
Remember that BETWEEN is inclusive, so if 5am is your cutoff time you may want it to be MAKETIME(4,59,59) so it includes 4:59 but not 5:00
Function MAKETIME returns a time value calculated from the hour, minute, and second arguments:
mysql> SELECT MAKETIME(3,0,0),MAKETIME(23,0,0),MAKETIME(5,0,0)
-> '03:00:00', '23:00:00', '05:00:00'
and, of course, 3 is not BETWEEN 23 AND 5 and it will return false. But yes, 3AM actually is between 11PM and 5AM, so how could you solve this?
Let's consider 23 as your START_TIME, and 5 as your END_TIME.
Since START_TIME has to happen before END_TIME, if this is not the case (23>5) that means that the interval rolls over the next day.
I would try with a query like this:
SELECT *
FROM yourtable
WHERE
(MAKETIME(START_TIME,0,0)<=MAKETIME(END_TIME,0,0) AND MAKETIME(3,0,0) BETWEEN MAKETIME(START_TIME,0,0) AND MAKETIME(END_TIME,0,0))
OR
(MAKETIME(START_TIME,0,0)>MAKETIME(END_TIME,0,0) AND NOT (MAKETIME(3,0,0) BETWEEN MAKETIME(START_TIME,0,0) AND MAKETIME(END_TIME,0,0)))

MySQL verify 10 minute increments in timestamps

I'm working with a logfile that records data every 10 minutes. I'm trying to come up with a query that can verify that data was in fact saved every 10 minutes.
Here are some sample timestamps:
2008-01-01 00:00:00
2008-01-01 00:10:00
2008-01-01 00:20:00
2008-01-01 00:30:00
Any ideas on this? I'd give some SQL if I thought it could be improved to be correct but I don't have anything worth posting.
One trick is to make a virtual table to which you can attempt to join the the data from your logfile. In my example I've used the postgres generate_series function to generate a series of second values to append to an initial timestamp (I assume there is a similar function in MySQL?).
The trick is to use the virtual table to which to do a left join to the actual data, to find where there is a missing value in the logging table (i.e., where logger.timestamp will be NULL).
Something along these lines will show you where there is a missing timestamp if any.
SELECT
y.c
, logger.timestamp
FROM
(SELECT
a + cast(b || ' sec' as interval) as c
FROM
(SELECT
cast('2011-10-31 10:00:00' as timestamp) as a
,t.b from generate_series(0,100,10) as t(b)
)x
) y
LEFT JOIN (
SELECT timestamp from log
) logger ON y.c = logger.timestamp
WHERE
logger.timestamp IS NULL;