This question already has an answer here:
I want to calculate time diff between starttime=23:30 and endtime=00:15 the time diff is coming -23.25
(1 answer)
Closed 5 years ago.
This is My First Condition. suppose CheckIn Time is Greater Than Check Out Time so How Can I calculate Time Duration
StaffName | attendDate| staffid| firmId| Shiftname | AttendId| CheckIn| CheckOut | Total Duration
---------------------------------------------------------------------------------------------
Kiran A Veri | 2017-03-28 |5146 |1 | Night | 34584 |18:00:00| 03:00:00 | 09:00:00
This is my second condition. If check in time is less than checkout time so how to manage both condition in this Query?
StaffName | attendDate| staffid| firmId| Shiftname | AttendId| CheckIn| CheckOut | Total Duration
---------------------------------------------------------------------------------------------
Ritesh B Patel | 2017-03-28 |5146 |1 | General | 34584 |10:06:06| 19:35:46 | 09:29:00
For the first part:
Use a CASE WHEN CheckIn > CheckOut in conjunction with DATEDIFF.
For the second part:
Build a datetime or time field and convert it using Style=108 (hh.mm.ss)
CREATE TABLE TEST(ID int, CheckIn time, CheckOut time);
INSERT INTO TEST VALUES(1, '03:00:00', '10:00:00'),(2, '10:26:13', '03:12:15'),(3, '18:00:00', '03:00:00');
-- Below SQL-Server 2012
SELECT CASE WHEN CheckIn > CheckOut
THEN CONVERT(VARCHAR(20), 86400 - DATEADD(SECOND, DATEDIFF(SECOND, CheckOut, CheckIn), '00:00:00'), 108)
ELSE CONVERT(VARCHAR(20),DATEADD(SECOND, DATEDIFF(SECOND, CheckIn, CheckOut), '00:00:00'), 108)
END Elapsed_Time
FROM TEST
-- SQL-Server 2012 or above
SELECT CASE WHEN CheckIn > CheckOut
THEN FORMAT(86400 - DATEADD(SECOND, DATEDIFF(SECOND, CheckOut, CheckIn), '00:00:00'), 'hh\:mm\:ss')
ELSE FORMAT(DATEADD(SECOND, DATEDIFF(SECOND, CheckIn, CheckOut), '00:00:00'), 'hh\:mm\:ss')
END Elapsed_Time
FROM TEST
GO
| Elapsed_Time |
| :----------- |
| 07:00:00 |
| 16:46:02 |
| 09:00:00 |
| Elapsed_Time |
| :----------- |
| 07:00:00 |
| 04:46:02 |
| 09:00:00 |
dbfiddle here
Related
I am aware that Snowflake is very similar to Oracle. I am doing a job that requires me to move some SQL Server code over into snowflake , however I can't seem to get these date functions converted over. In SSMS this SELECT DATEADD(YEAR,DATEDIFF(YEAR,0,'1912-01-01'),0) statement would return 1912-01-01 00:00:000. since the 0 indicates "The beginning of time" what would be Snowflakes equivalent to this 0 be and better yet how would you convert this to say Oracle code?
Thank you ahead of time!
Sharing few examples from Snowflake -
select date_trunc(year,'1912-01-05'::date);
+-------------------------------------+
| DATE_TRUNC(YEAR,'1912-01-05'::DATE) |
|-------------------------------------|
| 1912-01-01 |
+-------------------------------------+
select date_trunc(year,'1912-10-05'::date);
+-------------------------------------+
| DATE_TRUNC(YEAR,'1912-10-05'::DATE) |
|-------------------------------------|
| 1912-01-01 |
+-------------------------------------+
select date_trunc(year,'1912-12-01 10:00:000'::timestamp);
+----------------------------------------------------+
| DATE_TRUNC(YEAR,'1912-12-01 10:00:000'::TIMESTAMP) |
|----------------------------------------------------|
| 1912-01-01 00:00:00.000 |
+----------------------------------------------------+
I am familiar with the method you are talking about. It was a workaround to make up for the fact that SQL Server did not have a function to truncate date/time.
In Snowflake, just use DATE_TRUNC( <date_or_time_part>, <date_or_time_expr> )
https://docs.snowflake.com/en/sql-reference/functions/date_trunc.html
select to_date('2015-05-08T23:39:20.123-07:00') as "DATE1",
date_trunc('YEAR', "DATE1") as "TRUNCATED TO YEAR",
date_trunc('MONTH', "DATE1") as "TRUNCATED TO MONTH",
date_trunc('DAY', "DATE1") as "TRUNCATED TO DAY";
+------------+-------------------+--------------------+------------------+
| DATE1 | TRUNCATED TO YEAR | TRUNCATED TO MONTH | TRUNCATED TO DAY |
|------------+-------------------+--------------------+------------------|
| 2015-05-08 | 2015-01-01 | 2015-05-01 | 2015-05-08 |
+------------+-------------------+--------------------+------------------+
select to_timestamp('2015-05-08T23:39:20.123-07:00') as "TIMESTAMP1",
date_trunc('HOUR', "TIMESTAMP1") as "TRUNCATED TO HOUR",
date_trunc('MINUTE', "TIMESTAMP1") as "TRUNCATED TO MINUTE",
date_trunc('SECOND', "TIMESTAMP1") as "TRUNCATED TO SECOND";
+-------------------------+-------------------------+-------------------------+-------------------------+
| TIMESTAMP1 | TRUNCATED TO HOUR | TRUNCATED TO MINUTE | TRUNCATED TO SECOND |
|-------------------------+-------------------------+-------------------------+-------------------------|
| 2015-05-08 23:39:20.123 | 2015-05-08 23:00:00.000 | 2015-05-08 23:39:00.000 | 2015-05-08 23:39:20.000 |
+-------------------------+-------------------------+-------------------------+-------------------------+
Snowflake and Oracle both have a date truncate function
Snowflake:
DATE_TRUNC has many supported periods it can truncate to:
SELECT '1921-12-23'::date as d
,DATE_TRUNC('year', d) as d_trunc_to_year;
gives:
D
D_TRUNC_TO_YEAR
1921-12-23
1921-01-01
Oracle:
Oracle has TRUNC
Which there example looks to solve a rather similar way:
SELECT TRUNC(TO_DATE('27-OCT-92','DD-MON-YY'), 'YEAR')
"New Year" FROM DUAL;
New Year
---------
01-JAN-92
How to calculate working hours between two dates in snowflake without creating tables?
i have tried function like (datediff) and timestamp but i could not reach the solution
i would like to get something like that
+---------------------+---------------------+---------------+
| create_Task | Solved_Task | BusinessHours |
+---------------------+---------------------+---------------+
| 2012-03-05 09:00:00 | 2012-03-05 15:00:00 | 6.000000 |
| 2012-03-05 10:00:00 | 2012-03-06 10:00:00 | 8.000000 |
| 2012-03-05 11:00:00 | 2012-03-06 10:00:00 | 7.000000 |
| 2012-03-05 10:00:00 | 2012-03-06 15:00:00 | 13.000000 |
| 2012-03-09 16:00:00 | 2012-03-12 10:00:00 | 2.000000 |
| 2012-03-06 16:00:00 | 2012-03-15 10:00:00 | 50.000000 |
| 2012-03-09 16:00:00 | 2012-03-19 10:00:00 | 42.000000 |
+---------------------+---------------------+---------------+
and i would like to specify the working hours so then i can calculate the business hours
One way to do this is by creating a working hours table. Then you can run a fairly simple query:
select
t.id
, sum(datediff(‘second’,
-- calculate the max of the two start time
(case when t.start <=
w.working_day_start_timestamp
then w.working_day_start_timestamp
else t.start
end),
-- calculate the min of the two end times
(case when t.end >=
w.working_day_end_timestamp
then w.working_day_end_timestamp
else t.end
end)
)) / 3600 -- convert to hourly
as working_hour_diff
from
working_days_times w,
cross join time_intervals t
where -- select all intersecting intervals
(
t.start <= w.working_day_end_timestamp
and
t.end >= w.working_day_start_timestamp
)
and -- select only working days
w.is_working_day
group by
t.id
If you need a function, this article describes the implementation of a Javascript UDF in Snowflake:
https://medium.com/dandy-engineering-blog/how-to-calculate-the-number-of-working-hours-between-two-timestamps-in-sql-b5696de66e51
I am using SQL and I need to get this result but I dont know how. What I need is to get which interval does a time exist. Please refer to the example below
----------------------
| Time | Interval|
----------------------|
| 9:03 | 9:00 |
| 9:02 | 8:30 |
| 9:32 | 9:00 |
| 9:33 | 9:30 |
-----------------------
Sample interval matrix
-----------------------------
| Interval | Time |
-----------------------------|
| 9:00 | 9:02 - 9:31:59 |
| 9:30 | 9:32 - 10:01:59|
| 10:00 | 10:02- 10:31:59|
----------------------- ------
So basically 9:03 - 9:32 exist in 9:00 oclock interval and 9:33 - 10:02 exist in 9:30 interval
I have this query
SELECT CONVERT(time(7),DATEADD(MINUTE,(DATEPART(MINUTE, '10:32:00') /
30)*30,(DATEADD(HOUR, DATEDIFF(HOUR, 0, '10:32:00'), 0))))
but this is the farthest I can come up, the result should be 10:00 interval. You can point me in the right direction or give me some tips
I think this will give you what you need:
declare #testtable table (thedate time )
insert into #testtable values ( '09:03'), ('09:02'), ('09:32'), ('09:33')
select
thedate,
case when datepart(minute,thedate) between 0 and 2 then dateadd(minute, -30,cast(cast(datepart(hour,thedate) as varchar)+':00' as time))
when datepart(minute,thedate) between 3 and 32 then cast(cast(datepart(hour,thedate) as varchar)+':00' as time)
else dateadd(minute, +30,cast(cast(datepart(hour,thedate) as varchar)+':00' as time))
end
from #testtable
which produces:
09:03:00.0000000 09:00:00.0000000
09:02:00.0000000 08:30:00.0000000
09:32:00.0000000 09:00:00.0000000
09:33:00.0000000 09:30:00.0000000
The following solution uses DATEDIFF with MINUTE against an offset of 00:02 to correct the difference first, then truncates the time to 30 min intervals by operating the minutes as integers and doing / 30 * 30 and finally convert back the result to TIME.
DECLARE #Times TABLE (ClockTime TIME)
INSERT INTO #Times (ClockTime)
VALUES
('08:57'),
('08:58'),
('08:59'),
('09:00'),
('09:01'),
('09:02'),
('09:03'),
('09:05'),
('09:31'),
('09:31:59'),
('09:32'),
('10:32'),
('23:02'),
('23:03'),
('23:31:15')
SELECT
T.ClockTime,
MinutesMinus2 = DATEDIFF(MINUTE, '00:02', T.ClockTime),
MinutesMinus2On30Intervals = DATEDIFF(MINUTE, '00:02', T.ClockTime) / 30 * 30,
MinutesMinus2On30IntervalsAsTime = CONVERT(
TIME,
DATEADD(
MINUTE,
DATEDIFF(MINUTE, '00:02', T.ClockTime) / 30 * 30, -- MinutesMinus2On30Intervals
0))
FROM
#Times AS T
Results:
+------------------+---------------+----------------------------+----------------------------------+
| ClockTime | MinutesMinus2 | MinutesMinus2On30Intervals | MinutesMinus2On30IntervalsAsTime |
+------------------+---------------+----------------------------+----------------------------------+
| 08:57:00.0000000 | 535 | 510 | 08:30:00.0000000 |
| 08:58:00.0000000 | 536 | 510 | 08:30:00.0000000 |
| 08:59:00.0000000 | 537 | 510 | 08:30:00.0000000 |
| 09:00:00.0000000 | 538 | 510 | 08:30:00.0000000 |
| 09:01:00.0000000 | 539 | 510 | 08:30:00.0000000 |
| 09:02:00.0000000 | 540 | 540 | 09:00:00.0000000 |
| 09:03:00.0000000 | 541 | 540 | 09:00:00.0000000 |
| 09:05:00.0000000 | 543 | 540 | 09:00:00.0000000 |
| 09:31:00.0000000 | 569 | 540 | 09:00:00.0000000 |
| 09:31:59.0000000 | 569 | 540 | 09:00:00.0000000 |
| 09:32:00.0000000 | 570 | 570 | 09:30:00.0000000 |
| 10:32:00.0000000 | 630 | 630 | 10:30:00.0000000 |
| 23:02:00.0000000 | 1380 | 1380 | 23:00:00.0000000 |
| 23:03:00.0000000 | 1381 | 1380 | 23:00:00.0000000 |
| 23:31:15.0000000 | 1409 | 1380 | 23:00:00.0000000 |
+------------------+---------------+----------------------------+----------------------------------+
You could also parameterize the offset with a TIME variable in case your your time bias changes:
DECLARE #TimeOffset TIME = '00:02'
SELECT
Interval = CONVERT(
TIME,
DATEADD(
MINUTE,
DATEDIFF(MINUTE, #TimeOffset, T.ClockTime) / 30 * 30,
0))
FROM
#Times AS T
Note that i have change the interval time range string to start and end time. If you data is in string, do consider converting to 2 separate time data type. Else you will need to perform some string parsing in the query
Once the data is in the right format, the query is simply a INNER JOIN
declare #sample table
(
[Time] time(0)
)
insert into #sample
values ('09:03'),
('09:02'),
('09:32'),
('09:33')
declare #interval table
(
[Interval] time(0),
[Time_Start] time(0),
[Time_End] time(0)
)
insert into #interval
values ('09:00', '09:02', '09:31:59'),
('09:30', '09:32', '10:01:59'),
('10:00', '10:02', '10:31:59')
select s.Time, i.Interval
from #sample s
inner join #interval i on s.Time between i.Time_Start and i.Time_End
I give the answer to others for the idea but I think this is more clean and easy to understand
DECLARE #date time = '07-04-2019 10:32:00'
SELECT CONVERT(time(7),DATEADD(MINUTE,(DATEPART(MINUTE,
CASE WHEN DATEPART(MINUTE, #date) = 31 THEN DATEADD(minute,-2,#date) WHEN DATEPART(MINUTE, #date) = 1 THEN DATEADD(minute,-2,#date) ELSE #date END
) / 30)*30,(DATEADD(HOUR, DATEDIFF(HOUR, 0, CASE WHEN DATEPART(MINUTE,#date) = 1 THEN DATEADD(HOUR,-1,#date) ELSE #date END), 0))))
I am currently making a SQL Query to access data in a table called "Alarms". This table is set up as in the Following Format:
AlarmNumber | Time | AlarmState
-------------|-------|-----------
1046 | 10:30 | 0
1045 | 10:25 | 1
1044 | 10:24 | 0
1046 | 10:24 | 1
1046 | 10:23 | 0
1046 | 10:22 | 1
What I would like to achieve is to sort the alarms Into the Following Format
The Goal is to display the Alarm Start Time, Alarm Stop Time and Alarm Active Time (Alarm End Time - Alarm Start Time)
AlarmNumber | AlarmStartTime | AlarmEndTime | AlarmActiveTime
-------------|-----------------|--------------|----------------
1046 | 10:24 | 10:30 | 00:02
1045 | 10:24 | - | 10:24 + Current Time
1044 | Shift Start Time| 10:30 |10:30 - Shift Start Time
1046 | 10:22 | 10:23 | 00:01
My current code is the following (Note: _Global_Vars is a table with Timezones):
SELECT
TODATETIMEOFFSET([ALARM_START_TIME],0) AT TIME ZONE (SELECT g.LocalTimeZone FROM _Global_Vars as g) AS [ALARM_START_TIME],
TODATETIMEOFFSET(ALARM_FINISH_TIME,0) AT TIME ZONE (SELECT g.LocalTimeZone FROM _Global_Vars as g) AS [ALARM_FINISH_TIME],
DATEDIFF(SS, [ALARM_START_TIME], [ALARM_FINISH_TIME]),
sub.AlarmNumber
FROM
(
SELECT
(a.[Time]) AS AlarmTime,
(a.[AlarmNumber]+1) as AlarmNumber,
(CASE WHEN a.[AlarmState] = 1 THEN a.[Time] END) [ALARM_START_TIME],
(CASE WHEN a.[AlarmState] = 0 THEN a.[Time] END) [ALARM_FINISH_TIME]
FROM [Alarms] as a
WHERE (a.[Time] > DATEADD(mi, - 60.0 * 12, GETUTCDATE()))
)`
The issue at the moment is that if I use MAX in front of the CASE and GROUP BY AlarmNumber, it combines all of the values for AlarmNumber into a single row where I would like it to have multiple instances of Alarmnumber if the Alarm occurs multiple times
I am a novice regarding writing SQL Queries so any help would be great.
I will post just part of the solution (containing just first three colmns) since you not clearly specified the goal content for the last column.
SELECT t.alarmNumber,
isnull(MAX(CASE WHEN t.alarmstate = 1 THEN CAST(t.[time] as varchar(20)) END), 'Shift Start Time') AlarmStartTime,
isnull(MAX(CASE WHEN t.alarmstate = 0 THEN CAST(t.[time] as varchar(20)) END), 'Current Time') AlarmEndTime
FROM
(
SELECT *, row_number() over (partition by alarmnumber, alarmstate order by [time]) al_group
FROM Alarms
) t
GROUP BY t.alarmNumber, t.al_group
demo
I got 1 table which is dbo.Invoice. My current query now is able to select "SalesRef" that does not have invoice for "Mvt_Type" = '122'. However, I need to extend my query with PostDate field.
My problem is current query still display an SalesRef that does not have invoice for "Mvt_Type" = '122' with Postdate today( 8/8/2017). My expected result is it can only be display if no invoice was made more than 2 days after the Postdate. So, it suppose to display on 11/8/2017 or more.
Table dbo.Invoice
| PO_NUMBER | TYPE | MVT_TYPE | QUANTITY | SALESREF | DEBIT | POSTDATE |
|----------- |------ |---------- |---------- |---------- |------- |------------ |
| 10001001 | GR | 101 | 1000.00 | 5001 | S | 2017-01-08 |
| 10001001 | GR | 101 | 2000.00 | 5002 | S | 2017-02-08 |
| 10001001 | GR | 122 | 1000.00 | 5001 | H | 2017-01-08 |
| 10001001 | INV | 000 | 1000.00 | 5001 | S | 2017-01-08 |
| 10001001 | INV | 000 | 2000.00 | 5002 | S | 2017-02-08 |
| 10001001 | GR | 122 | 1500.00 | 5002 | H | 2017-02-08 |
| 10001001 | INV | 000 | 1000.00 | 5001 | H | 2017-01-08 |
Below is my current query :
SELECT *
FROM dbo.INVOICE i
WHERE MVT_TYPE = '122' AND SALESREF IS NOT NULL AND POSTDATE > CONVERT(VARCHAR(10), dateadd(day,2,getdate()),101)
AND NOT EXISTS (SELECT 1
FROM dbo.INVOICE
WHERE DEBIT = 'H' AND MVT_TYPE = '000' AND SALESREF = i.SALESREF )
Expected Result is same like below. But this time need to add PostDate.
| PO_NUMBER | TYPE | MVT_TYPE | QUANTITY | SALESREF | DEBIT | POSTDATE |
|----------- |------ |---------- |---------- |---------- |------- |------------ |
| 10001001 | GR | 122 | 1500.00 | 5002 | H | 2017-02-08 |
If PostDate is DATE or DATETIME, instead of casting you could use DATEDIFF function to get the days between two dates and do the INT comparison:
WHERE DATEDIFF(DAY, PostDate, GETDATE())>2
If PostDate is varchar, stored in the format shown in the OP:
SET LANGUAGE british
SELECT ....
WHERE DATEDIFF(DAY, CAST(PostDate as datetime), GETDATE())>2
EDIT: Apparently DATEDIFF will work if PostDate is VARCHAR data type as well
DECLARE #PostDate VARCHAR(50)
SET #PostDate='08-01-2017'
SELECT DATEDIFF(DAY, #PostDate, GETDATE()) -- GETDATE() is 08-08-2017
-- Returns 7
Having said this, it is a good practice to keep Dates and Times as proper data types. In your case, you could change the data type to DATE, if possible. Will speed up lookups
EDIT 2: Please note, SQL Server works with ISO 8601 Date Format, which is YYYY-MM-DD, but the dates in OP's example, even though as per OP refer to dates in August 2017, are given incorrectly (referring to Jan and Feb 2017) and are stored as varchar. For correct results, these need to be either converted to DATE/DATETIME data type, or reformatted with the correct ISO format.
EDIT 3: Showing an example of casting OP's date format into proper, ISO format before calling DATEDIFF:
SET LANGUAGE british
DECLARE #PostDate VARCHAR(50)
SET #PostDate='2017-01-08'
SELECT DATEDIFF(DAY, CAST(#PostDate AS DATETIME), GETDATE()) -- GETDATE() is 08-08-2017
-- Returns 7
And the WHERE clause would be as follows:
-- In the begining of the select statement
SET LANGUAGE british
SELECT *
FROM ...
WHERE DATEDIFF(DAY, CAST(PostDate as datetime), GETDATE())>2
Is the POSTDATE - date column? If no then you are comparing strings and the result is as expected as '2017-01-08' > '08/10/2017' ('2' > '0'). Most probably you just need to cast the POSTDATE. See the example:
select
case
when '2017-01-08' > CONVERT(VARCHAR(10), dateadd(day,2,getdate()),101) THEN 1
ELSE 0
end without_cast,
case
when CAST('2017-01-08' AS DATE) > CONVERT(VARCHAR(10), dateadd(day,2,getdate()),101) THEN 1
ELSE 0
end with_cast
So what you need is:
SELECT *
FROM dbo.INVOICE i
WHERE MVT_TYPE = '122' AND SALESREF IS NOT NULL AND CAST(POSTDATE AS DATE) > CONVERT(VARCHAR(10), dateadd(day,2,getdate()),101)
AND NOT EXISTS (SELECT 1
FROM dbo.INVOICE
WHERE DEBIT = 'H' AND MVT_TYPE = '000' AND SALESREF = i.SALESREF )
Your problem is that you store a date as a varchar.
To compare 2 dates correctly you should compare their DATE rappresentation, not strings.
So I suggest you to convert your varchar to date, i.e. instead of
CAST(POSTDATE AS DATE) > CONVERT(VARCHAR(10), dateadd(day,2,getdate()),101)
you should use DATEFROMPARTS ( left(POSTDATE, 4), right(POSTDATE, 2), substring(POSTDATE,6,2)) > dateadd(day,2,cast(getdate() as date));.
DATEFROMPARTS function is available starting with SQL Server 2012, let me know if you are on the earlier version and I'll rewrite my code