I have created a new column called DesiredTimeOfFileCreation of type time(7); this will indicate at what time the data is to be extracted to an export file.
Let's suppose it is set to 6:00:00. I then have a SQL agent job scheduled at 6:00 (probably every 30 minutes), but it might run at 6:00:05 or even 6:01. I want to select all rows where the DesiredTimeOfFileCreation is less than 30 minutes ago.
Does someone already have a user-defined TimeDiff function? Or is there an easy alternative that I'm missing?
As Martin mentioned above, I need to handle the midnight wrap-around.
This seems overly complicated. The code below seems to work if one time is one hour before midnight, and one is within an hour after. Would be nice to make it more generic. I think the only way to do that would be to make up a dummy date, which I may experiment with next.
The reason I'm passing a date in the unit test is that I will be passing a casted version of GetUTCDate() as a parm:
ALTER FUNCTION TimeDiffMinutes
(
#FirstTime time(7),
#SecondTime time(7)
)
RETURNS int
AS
BEGIN
/*
Unit Test:
select dbo.TimeDiffMinutes('13:31',cast ('2013-06-10 13:35' as time)), -- simple test
dbo.TimeDiffMinutes('23:55',cast ('2013-06-10 00:05' as time)) -- test midnight wrap-around
select dbo.TimeDiffMinutes('23:55',cast ('2013-06-10 00:05' as time)) -- test midnight wrap-around
*/
-- Declare the return variable here
DECLARE #resultMinutes int
DECLARE #Hour int
-- although we can compare two times, the problem is that if one time is 11:55 and the other is 00:05, we want to show 10 minutes difference.
-- We cannot add 24 hours to a time, because that would be an invalid value
Set #Hour = datePart(hour,#SecondTime)
if (#Hour <= 0)
begin
-- increase both times by an hour so we can compare them, 23:55 will wrap around to 01:55
Set #FirstTime = DateAdd(hour,+1,#FirstTime)
Set #SecondTime = DateAdd(hour,+1,#SecondTime)
end
SET #resultMinutes = DATEDIFF(Minute,#FirstTime,#SecondTime)
-- Return the result of the function
RETURN #resultMinutes
END
NOTE: This code shows that you cannot go past 24 hours in a time; it just wraps back around (with no error!):
declare #FirstTime time(7)
SET #FirstTime = '23:05'
print #FirstTime
Set #FirstTime = DATEADD(HOUR,1,#FirstTime)
print #FirstTime
Improved version, using an arbitrary date.
ALTER FUNCTION TimeDiffMinutes
(
#FirstTime time(7),
#SecondTime time(7)
)
RETURNS int
AS
BEGIN
/*
Unit Test:
select dbo.TimeDiffMinutes('13:31',cast ('2013-06-10 13:35' as time)), -- simple test
dbo.TimeDiffMinutes('23:55',cast ('2013-06-10 00:05' as time)) -- test midnight wrap-around
select dbo.TimeDiffMinutes('23:55',cast ('2013-06-10 00:05' as time)) -- test midnight wrap-around
*/
-- Declare the return variable here
DECLARE #resultMinutes int
DECLARE #Hour int
DECLARE #FirstDate datetime
DECLARE #SecondDate datetime
Set #FirstDate = CAST('2001-01-01 ' + Convert(varchar(12),#FirstTime) as DateTime)
Set #SecondDate = CAST('2001-01-01 ' + Convert(varchar(12),#SecondTime) as DateTime)
-- although we can compare two times, the problem is that if one time is 11:55 and the other is 00:05, we want to show 10 minutes difference.
-- We cannot add 24 hours to a time, because that would be an invalid value
Set #Hour = datePart(hour,#SecondDate)
if (#Hour <= 0)
begin
-- increase both times by an hour so we can compare them, 23:55 will wrap around to 01:55
Set #SecondDate = DateAdd(day,+1,#SecondDate)
end
SET #resultMinutes = DATEDIFF(Minute,#FirstDate,#SecondDate)
-- Return the result of the function
RETURN #resultMinutes
END
This is how I will use the function. We store the local time that an airport wants an extract file in a table. Then we will use SQL agent or BizTalk to poll every 30 minutes looking for work to do. AirportCode is a column in the table, and we have our own crazy function that converts for timezones.
select *,
dbo.TimeDiffMinutes(
DesiredFileCreationTimeLocal,
cast(dbo.LocationLocalTimeFromAirportCode(AirportCode,GETUTCDATE()) as time)
) as 'MinutesAgo'
from TransactionExtractDistribution
where dbo.TimeDiffMinutes(
DesiredFileCreationTimeLocal,
cast(dbo.LocationLocalTimeFromAirportCode(AirportCode,GETUTCDATE()) AS time)
) < 30
This will probably work for me:
WHERE DATEDIFF(Minute,DesiredFileCreationTimeLocal,cast(GETDATE() as time)) < 30
I had to research what happened if you pass a Time as a variable to the DateDiff function.
It seems to work, the only trick is then how to pass two times to it.
My real-world scenario is more complex, because we are dealing with different locations in different time zones, so there will some UTC conversions added to the above.
Related
I have a problem for which a 15 minute search on SO couldn't get an answer. I have a database with a table called bugs, which is used to store some info about bugs in a system we are developing. Columns of importance are open_date, close_date and status of severity of the bug introduced. I need to make an SQL statement to display counted number of opened bugs for each of dates from a range of dates which is entered. A bug is considered open on “2012-01-01” if it is created on or
before “2012-01-01” (i.e. the same day) and closed on or after “2012-01-02” (i.e. day after that date).
I have made an sql for finding unit functionality of the requirement:
SELECT COUNT(DATE(Open_Date)) BugsOpen
, DATE(Open_Date) Dated
FROM bugs
WHERE DATE(open_date) <= (STR_TO_DATE('21/10/17','%d/%m/%Y'))
AND DATE(close_date) >= (DATE_ADD(STR_TO_DATE('21/10/17','%d/%m/%Y'),INTERVAL 1 DAY))
OR close_date IS NULL);
Which is working but I need this in like a function call or stored procedure call, ex. showBugs('21/10/17 - 29/10/17'), to display for each of the dates in the range input in this function/procedure.
I have found a way to extract to and from dates.
SELECT SUBSTRING('21/10/17 - 29/10/17', -19, 8) as from_date,
SUBSTRING('21/10/17 - 29/10/17' FROM 12) as to_date;
But I am really having trouble writing a simple procedure. I have started writing some code:
DROP PROCEDURE IF EXISTS showBugs;
CREATE DEFINER = 'root'#'localhost' PROCEDURE showBugs(rangeOfDates TEXT)
BEGIN
SET #from_date = SUBSTRING(rangeOfDates, -19, 8);
SET #to_date = SUBSTRING(rangeOfDates FROM 12);
REPEAT SET #from_date
END REPEAT;
END;
call showBugs('21/10/17 - 29/10/17');
I am really in a rut here and would be grateful if someone is to help me either with stored procedure or function implementation.
I am writing an event scheduler in Mysql for generating tickets 3 hour prior to the user travel time. Mysl is installed in server where the server time is in UTC and 12 hour format with 5:30 hour difference with IST time. I have one user whose travel time is 7:30 in the morning, so i need to generate ticket for this user at 4:30 in the morning. I am getting current server time and converting to IST and comparing travel time and current converted time difference is 3 hours. But some how its failing and always ticket creating at 10 am in the morning. Below is my complete event writtent in Mysql,
DELIMITER $$
ALTER EVENT `Auto_Generated_Ticket` ON SCHEDULE EVERY 1 MINUTE STARTS CURRENT_TIMESTAMP ON COMPLETION PRESERVE ENABLE DO BEGIN
DECLARE UserId INT;
DECLARE v_finished INT DEFAULT 0;
DECLARE GetDate DATE DEFAULT DATE(NOW());
/*get all active user's who's tariff enddate is > today and available journeys > 0 and pass-status=4(delivered)*/
DECLARE ActiveUserId CURSOR FOR
SELECT UT.user_id FROM `um.user_trs.tariff` UT
INNER JOIN `um.user` U ON U.user_id=UT.user_id
INNER JOIN `um.user_ps.pass` UP ON UP.user_id=UT.user_id
INNER JOIN `ps.pass` P ON P.pass_id=UP.pass_id
WHERE UT.end_date >=DATE(NOW()) AND UT.available_journeys > 0 AND UT.current_balance>0 AND P.status_id=4
GROUP BY UT.user_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_finished=1;
SET #GetTime= DATE_FORMAT(NOW(),'%Y-%m-%d %h:%i:%s %p');
OPEN ActiveUserId;
get_userid:LOOP
FETCH ActiveUserId INTO UserId;
IF v_finished=1 THEN
LEAVE get_userid;
END IF;
SET #StartTime=(SELECT RS.start_time FROM `um.user_rm.route` UR
INNER JOIN `rm.route_schedule` RS ON RS.route_schedule_id=UR.route_schedule_id
WHERE UR.user_id=UserId
ORDER BY ABS( TIMEDIFF( RS.`start_time`, TIME(CONVERT_TZ(#GetTime,'+00:00','+05:30')) ) ) LIMIT 1);
SET #TimeDiff=(HOUR(TIMEDIFF(#StartTime,TIME(CONVERT_TZ(#GetTime,'+00:00','+05:30')))));
/*if time difference between current time and schedule start time is 3 hours then generate ticket for the user for that particular schedule*/
IF (#TimeDiff =3) THEN
/*IF (#TNumber IS NULL) THEN*/
IF NOT EXISTS(SELECT ticket_number FROM `um.user_ts.ticket` WHERE route_id=#RouteId AND route_schedule_id=#RoutScheduleId
AND user_id=UserId AND DATE(date_of_issue)=#ISTDATE) THEN
INSERT INTO `um.user_ts.ticket`
(
`user_id`,`route_id`,`route_schedule_id`,`ticket_number`,`date_of_issue`,`is_cancelled`,
`amount_charged`,`user_tariff_id`,`opening_balance`,`is_auto_generated`
)
VALUES
(
UserId,#RouteId,#RoutScheduleId,#TicketNumber,CONVERT_TZ(UTC_TIMESTAMP(),'+00:00','+05:30'),
FALSE,#PerJourneyCost,#UserTariffId,#TariffCurrentBalance,1
);
END IF;/*end of route and schedule check*/
END IF; /*end of time difference check*/
END LOOP get_userid;
CLOSE ActiveUserId;
END$$
DELIMITER ;
Please let me know if any other way to convert time or any mistake which i am not noticing in the above query.
Regards
Sangeetha
There is a dedicated mysql command to do so. CONVERT_TZ(dt,from_tz,to_tz)
See here https://dev.mysql.com/doc/refman/5.6/en/date-and-time-functions.html#function_convert-tz
From their docs:
CONVERT_TZ() converts a datetime value dt from the time zone given by
from_tz to the time zone given by to_tz and returns the resulting
value
Maybe the warning applies to you ?
Warning
Do not use a downloadable package if your system has a zoneinfo
database. Use the mysql_tzinfo_to_sql utility instead. Otherwise, you
may cause a difference in datetime handling between MySQL and other
applications on your system.
I would break it down to a minimalistic example and see if this works.
Here's how I would do it to test the basics:
Simple example:
SELECT CONVERT_TZ('2016-05-24 12:00:00','GMT','MET');
So you could build a query to determine the timediff like this:
HOUR(TIMEDIFF(
CONVERT_TZ('2016-05-24 12:00:00','GMT','MET'),
'2016-05-24 12:00:00'
))
I want the unique month between two date
like
#StartDate = '04-05-2013'
#EndDate = '26-12-2013'
I want the output like
5 6 7 8 9 10 11 12
I also want to use cursor with above output how to get this using sql server
Generally speaking, you may use a script like the following in order to obtain the values you are looking for. I can't understand whether you would need a single result set or not, so the script simply returns one result set for each month: feel free to modify it as you need, even by mixing the code with your (existing?) cursor-related script:
DECLARE #StartDate datetime = '2013-05-04'
DECLARE #EndDate datetime = '2013-12-26'
WHILE (#StartDate < #EndDate)
BEGIN
SELECT MONTH(#StartDate)
SET #StartDate = DATEADD(month, 1, #StartDate)
END
Also please note this is a starting point: for performance reasons, you may want to somehow consider reviewing the entire query.
If you only need months number then you can get the difference of months between two dates and increment it to get the result as shown below:
DECLARE #StartDateMonth int = DATEPART(mm, '2013-05-04')
DECLARE #EndDateMonth int = DATEPART(mm,'2013-12-26')
WHILE (#StartDateMonth <= #EndDateMonth )
BEGIN
SELECT #StartDateMonth
SET #StartDateMonth = #StartDateMonth + 1
END
Hello again SQL Server 2008 gurus.
I need to apply the following rules to the setting of a worker's start and end times for their work day (hourly employees) in a SELECT statement. I apologize in advance for my SQL ignorance.
The rule is to set their start time to a value stored in a table field for that worker, if they login on or before their start time (a time stored in the worker starttime column) and therefore get credit for starting at their start time.
If they log out within a 10 minute period before or anytime after their end time stored in a column for the worker, they get credit for their full day, another value stored in a column of the worker table, otherwise they are penalized some percentage of an hour, i.e. their log out time rounded to .25 of an hour less closest to the time they logged out. i.e. if they are set to log out at 4:30, and they log out at 4:18, their log out time is 4:15. If they log out at 4:20, and are set to log out at 4:30, their log out time is 4:30.
The first rule applies to all hourly employees where their workday hours is less than or equal to their expected workday value. The caveat is, for those where overtime is ok (a bit value set to 1). If overtime is permitted, the number of billable hours can exceed the full day value stored for them, and therefore the value of their logout - login time can exceed their fullday value.
The question is, can these rules be calculated in the SELECT statement and if so can I get some help with the code?
The columns containing the information are:
worker.startime (TIME)
worker.endtime (TIME)
worker.overtimeallowed (BIT)
worker.workdayhours (decimal (12,2))
worker.penaltyvalue (decimal (12,2))
If it requires a UDF or stored procedure (since I'm using the Telerik ReportViewer) I'm not sure it would be supported, but that's probably another question.
So far I've gotten some help with applying some CASE logic - calculating whether a worker get's credit for their 1/2 lunch. The code that was supplied works as promised. This, I believe may be an extension to that logic - so I'll provide the code I have here:
-- for testing purposes only.
DECLARE #StartDate AS DateTime
SET #StartDate = CAST('03/25/2012' AS DATE)
DECLARE #EndDate AS DateTime
SET #EndDate = CAST('04/10/2012' AS DATE)
SELECT
w.Firstname
,w.Lastname
,wf.Login
,wf.logout
,ROUND(CAST(DATEDIFF(MI, wf.Login, wf.Logout) AS DECIMAL)/60,2)
- CASE
WHEN DATEDIFF(hour, wf.Login, wf.Logout) < w.MinimumHours THEN
w.LunchDeduction
ELSE
0
END AS [Hours Credited]
FROM Workers AS w
JOIN Workflow AS wf
ON wf.LoggedInWorkerid = w.ID
JOIN Roles AS r
ON w.RoleID = r.RoleID
WHERE (r.Descript = 'Hourly')
AND wf.Login >= #StartDate AND wf.Logout <= #EndDate
ORDER BY w.Lastname, w.Firstname
Here is a sample select dealing with constraints you described. CTEs create tables for testing purposes. Main query shows the calculations. You have worked with datediffs and dateadds so there is no mistery. If you haven't use % before, it is modulo operator used to round time to 15 minutes.
;with worker (ID, overtime, startTime, endTime) as
(
select 1, 1, CAST ('08:30' as time), CAST ('16:30' as time)
union all
select 2, 0, CAST ('08:30' as time), CAST ('16:30' as time)
union all
select 3, 0, CAST ('08:30' as time), CAST ('16:30' as time)
),
-- Test table of workflows
wf (workerID, login, logout) as
(
select 1, CAST ('2012-03-11 08:20' as datetime), CAST ('2012-03-11 19:33' as datetime)
union all
select 2, CAST ('2012-03-11 08:50' as datetime), CAST ('2012-03-11 16:20' as datetime)
union all
select 3, CAST ('2012-03-11 08:22' as datetime), CAST ('2012-03-11 16:18' as datetime)
)
select wf.workerID,
wf.login,
wf.logout,
-- if starttime > login return startTime else login
case when DATEDIFF(MI, w.startTime, cast (wf.login as time)) < 0
then cast(CAST (wf.login AS date) AS datetime) + w.startTime
else wf.login
end roundedLogin,
case when w.overtime = 1 -- Round to 15 minutes whenever finished
OR
-- Round to 15 minutes if left ten or more minutes before endTime
DATEDIFF(MI, cast (wf.logout as time), dateadd (MI, -10, w.endTime)) > 0
then dateadd (MI, -(DATEPART (MI, wf.logout) % 15), wf.logout)
-- stop at endTime if overtime = 0 OR left job at apropriate time
else cast(CAST (wf.logout AS date) AS datetime) + w.endTime
end roundedLogout
from worker w
inner join wf
on w.ID = wf.workerID
There will be a problem with this approach. When you start to integrate mathematics into original query you will notice that you have to write expressions evaluating roundedLogin and roundedLogout again to calculate billable hours. You cannot reuse alias defined in the same scope, but you can create derived table or view or even calculated fields. View returning columns from workflows and all additional expressions would probably be the best.
Using this view in other queries would simplify things by encapsulating logic at one place.
Not a very good title, so my apologies.
For some reason, (I wasn't the person who did it, i digress) we have a table structure where the field type for a date is varchar. (odd).
We have some dates, such as:
1932-04-01 00:00:00 and 1929-07-04 00:00:00
I need to do a query which will convert these date strings into a unix time stamp, however, in mySQL if you convert a date which is before 1970 it will return 0.
Any ideas?
Thanks so much!
EDIT: Wrong date format. ooops.
Aha! We've found a solution!
The SQL to do it:
SELECT DATEDIFF( STR_TO_DATE('04-07-1988','%d-%m-%Y'),FROM_UNIXTIME(0))*24*3600 -> 583977600
SELECT DATEDIFF( STR_TO_DATE('04-07-1968','%d-%m-%Y'),FROM_UNIXTIME(0))*24*3600 -> -47174400
This could be useful for future reference.
You can test it here: http://www.onlineconversion.com/unix_time.htm
I've adapted the DATEDIFF workaround to also include time not just days. I've wrapped it up into a stored function, but you can just extract the SELECT part out if you don't want to use functions.
DELIMITER |
CREATE FUNCTION SIGNED_UNIX_TIMESTAMP (d DATETIME)
RETURNS BIGINT
DETERMINISTIC
BEGIN
DECLARE tz VARCHAR(100);
DECLARE ts BIGINT;
SET tz = ##time_zone;
SET time_zone = '+00:00';
SELECT DATEDIFF(d, FROM_UNIXTIME(0)) * 86400 +
TIME_TO_SEC(
TIMEDIFF(
d,
DATE_ADD(MAKEDATE(YEAR(d), DAYOFYEAR(d)), INTERVAL 0 HOUR)
)
) INTO ts;
SET time_zone = tz;
return ts;
END|
DELIMITER ;
-- SELECT UNIX_TIMESTAMP('1900-01-02 03:45:00');
-- will return 0
-- SELECT SIGNED_UNIX_TIMESTAMP('1900-01-02 03:45:00');
-- will return -2208888900
convert these date strings into a
unix time stamp
Traditional Unix timestamps are an unsigned integer count of seconds since 1-Jan-1970 therefore can't represent any date before that.
At best you will have mixed results depending on the system you are using to represent the timestamp.
From wikipedia
There was originally some controversy
over whether the Unix time_t should
be signed or unsigned. If unsigned,
its range in the future would be
doubled, postponing the 32-bit
overflow (by 68 years). However, it
would then be incapable of
representing times prior to 1970.
Dennis Ritchie, when asked about this
issue, said that he hadn't thought
very deeply about it, but was of the
opinion that the ability to represent
all times within his lifetime would be
nice. (Ritchie's birth, in 1941, is
around Unix time −893 400 000.) The
consensus is for time_t to be signed,
and this is the usual practice. The
software development platform for
version 6 of the QNX operating system
has an unsigned 32-bit time_t, though
older releases used a signed type.
It appears that MySQL treats timestamps as an unsigned integer, meaning that times before the Epoc will all resolve to 0.
This being the case, you always have the option to implement your own unsigned timestamp type and use that for your calculations.
If its feasible for your problem, you could shift all your mysql times by, say 100 years, and then work with those adjusted timestamps or re calculate the negative timestamp value.
As some have said, make sure your system is using 64bits to represent the timestamp otherwise you'll hit the year 2038 problem.
I have not tried the above solutions but this might in case you are not able to retrieve the date value from the MySQL database in the form of timestamp, then this operation can also be tried
SELECT TIMESTAMPDIFF(second,FROM_UNIXTIME(0),'1960-01-01 00:00:00');
To get the max. Range +/- wise use this query on your birthday field, in my case "yyyy-mm-dd" but you can change it to your needs
select name, (#bday:=STR_TO_DATE(birthday,"%Y-%m-%d")),if(year(#bday)<1970,UNIX_TIMESTAMP(adddate(#bday, interval 68 year))-2145916800,UNIX_TIMESTAMP(#bday)) from people
I feel like we're making this much too difficult...
Use my functions below so you can convert anything to and from unix timestamps, much like you do in a browser.
Call functions like this:
select to_unix_time('1776-07-04 10:02:00'),
from_unix_time(-6106024680000);
By compiling these:
delimiter $$
create function to_unix_time (
p_datetime datetime
) returns bigint
deterministic
begin
declare v_ret bigint;
select round(timestampdiff(
microsecond,
'1970-01-01 00:00:00',
p_datetime
) / 1000, 0)
into v_ret;
return v_ret;
end$$
create function from_unix_time (
p_time bigint
) returns datetime(6)
deterministic
begin
declare v_ret datetime(6);
select '1970-01-01 00:00:00' +
interval (p_time * 1000) microsecond
into v_ret;
return v_ret;
end$$
Use Date instead of timestamps. Date will solve your Problems.
check this link