Add missing ZERO on HH:MM:SS in SQL column - sql-server

This is the format I need from a T-SQL Column HH:MM:SS
Example:
SELECT CONVERT(varchar,GETDATE(),108) -- 11:06:03
I have these values: (Select duration from MyTable)
2:13:00
11:02
0:43
That needs to be this:
02:13:00
00:11:02
00:00:43
How can this be done in T-SQL?

Use Convert, then cast
transformation. The peculiarity here is hidden in omitted hours string, while sql server assumes seconds omitted.
Select
CONVERT(time(0),
case when len(duration) > 5 then duration else '00:' + duration end, 108)
from MyTable
Examples:
SELECT CONVERT(time(0),
case when len('2:13:00') > 5 then '2:13:00' else '00:' + '2:13:00' end ,108)
SELECT CONVERT(time(0),
case when len('11:02') > 5 then '11:02' else '00:' + '11:02' end ,108)
SELECT CONVERT(time(0),
case when len('0:43') > 5 then '0:43' else '00:' + '0:43' end ,108)
--------------
02:13:00
00:11:02
00:00:43
Update1: optimized the answer to time(0) due #Mikael Eriksson tip
Update2: modified solution concerning example2 requirement

This did it for me at the end.....
SELECT RIGHT('000' + CASE WHEN LEN(Duration) > 5
THEN LEFT(duration, CHARINDEX(':', duration) - 1)
ELSE 0 -- Hours
END, 3) + ':' +
RIGHT('00' + CASE WHEN LEN(Duration) > 5
THEN SUBSTRING(duration
,CHARINDEX(':',duration) + 1
,CHARINDEX(':',duration
,CHARINDEX(':',duration) + 1) - 1- CHARINDEX(':',duration))
ELSE LEFT(duration,CHARINDEX(':',duration) - 1)END, 2) + ':' + -- Minutes
RIGHT('00'+ RIGHT(duration, 2), 2) -- Seconds
NewDuration
FROM MyTable

Related

Summing Times as Durations

I am trying to sum a time field stored in my database as an nvarchar like 'hh:mm', in minutes, however, the following does not work (likely because time cannot handle anything over '23:59:59'):
SELECT
SUM(DATEDIFF(MINUTE, '00:00:00', MyTimeField)) AS TotalMinutes
FROM MyTable
Here's my SQL fiddle on this. Any help appreciated
24:00 is not valid, and if you try 00:00, you have already entered the next day - but because you are not considering a full date when using datediff, that cannot happen. Because if you mention 00:00, then that points at the beginning of the current day rather than the next.
Consider storing time as datetime rather than varchar - and store the clean up times as full time stamp rather than just stating the hour.
Here's the solution:
CREATE TABLE MyTable
([MyTimeField] nvarchar(5))
;
INSERT INTO MyTable
([MyTimeField])
VALUES
('00:15'),
('00:30'),
('01:45'),
('23:15'),
('23:59'),
('24:00')
;
WITH IntMinutes AS
(
SELECT
CASE
WHEN RIGHT('0000' + MyTimeField, 5) < '24:00' THEN DATEDIFF(MINUTE, '00:00:00', MyTimeField)
ELSE
DATEDIFF(MINUTE, '00:00:00', '00:' + RIGHT(MyTimeField, 2)) +
DATEDIFF(MINUTE, '00:00:00', CAST((LEFT(MyTimeField, 2) - 24) AS VARCHAR(2)) + ':00') +
DATEDIFF(MINUTE, '00:00:00', '23:59') + 1
END AS Minutes
FROM MyTable
)
SELECT SUM(Minutes) AS TotalMinutes
FROM IntMinutes

Convert number of minutes to hh:mm

I have a column in a table that stores the number of minutes as a numeric(18,4) field named [course_access_minutes].
The stored values come from a blackboard database and look like this:
0.0500
0.0667
0.3667
up to
314.0833
625.8167
How do I convert these to time hh:mm, I've had a good look at the database documentation and all I can find is
course_access_minutes numeric(18,4) This is the number of minutes that the user accesses this course in total during this login session.
Can I assume that I can make a direct conversion from minutes into hours? I think I will take any values below 1 as 0 minutes. What is the best way to do this in SQL? Thanks in advance for your help.
Try this
SELECT CONVERT(varchar, DATEADD(s, 625.8167 * 60, 0), 108)
If the duration is longer than 24 hours you can use this
SELECT CONVERT(varchar, CAST(1877.4501 * 60 AS int) / 3600)
+ RIGHT(CONVERT(varchar, DATEADD(s, 1877.4501 * 60, 0), 108), 6)
You could use FLOOR like this
DECLARE #SampleData AS TABLE
(
Minutes numeric(18,4)
)
INSERT INTO #SampleData
VALUES
( 0.0500),
( 1.0500),
( 30.0500),
( 80.0500),
( 314.0833),
( 625.8167)
SELECT CONCAT(floor(sd.Minutes/60),':', CASE WHEN sd.Minutes - floor(sd.Minutes/60)*60 < 1 THEN '0'
ELSE FLOOR(sd.Minutes - floor(sd.Minutes/60)*60 )
END) AS hours
FROM #SampleData sd
Returns
hours
0:0
0:1
0:30
1:20
5:14
10:25
WITH _Samples AS (
SELECT CONVERT(numeric(18, 4), 0.0500) [course_access_minutes]
UNION ALL SELECT 0.0667
UNION ALL SELECT 0.3667
UNION ALL SELECT 314.0833
UNION ALL SELECT 625.8167
)
SELECT
S.course_access_minutes,
-- split out the number
FLOOR(S.course_access_minutes / 60) [hours],
FLOOR(S.course_access_minutes % 60) [minutes],
FLOOR((S.course_access_minutes - FLOOR(S.course_access_minutes)) * 60) [seconds],
-- to a string
CONVERT(varchar(10), FLOOR(S.course_access_minutes / 60))
+ ':' + RIGHT('00' + CONVERT(varchar(10), FLOOR(S.course_access_minutes % 60)), 2)
+ ':' + RIGHT('00' + CONVERT(varchar(10), FLOOR((S.course_access_minutes - FLOOR(S.course_access_minutes)) * 60)), 2) [time_string],
-- You could consider converting to the time data type if the values will never exceed the limit
-- time supports 00:00:00.0000000 through 23:59:59.9999999
-- 0 through 1439.9833333 ... 23 * 60 = 1380 + 59 = 1439 + (59 / 60) = 1439.9833333
-- (see: https://learn.microsoft.com/en-us/sql/t-sql/data-types/time-transact-sql)
CONVERT(time,
CONVERT(varchar(10), FLOOR(S.course_access_minutes / 60))
+ ':' + RIGHT('00' + CONVERT(varchar(10), FLOOR(S.course_access_minutes % 60)), 2)
+ ':' + RIGHT('00' + CONVERT(varchar(10), FLOOR((S.course_access_minutes - FLOOR(S.course_access_minutes)) * 60)), 2)
) [time]
FROM
_Samples S
(It wouldn't be difficult to further this idea and split out the fractional seconds as well.)
Which yields:
course_access_minutes hours minutes seconds time_string time
---------------------- ------ -------- -------- ------------ ----------------
0.0500 0 0 3 0:00:03 00:00:03.0000000
0.0667 0 0 4 0:00:04 00:00:04.0000000
0.3667 0 0 22 0:00:22 00:00:22.0000000
314.0833 5 14 4 5:14:04 05:14:04.0000000
625.8167 10 25 49 10:25:49 10:25:49.0000000
Note that this is going to be like Greg's answer, but I wanted to explain and simplify it.
You have minutes, so dividing them by 60 and flooring it (removing the decimal) gives the hours (without the minutes).
If you take the total minutes again, and remove (mod it by) the floored hours - which requires conversion to minutes by multiplying by 60 - you are left with the remaining minutes by essentially just finding out what is left after taking away that many groups of sixties:
SELECT FLOOR(course_access_minutes / 60) as Hours,
(FLOOR(course_access_minutes) % 60) as Minutes
FROM MyTable
If you want the decimal to appear for the amount of minute fractions (you want the seconds to appear, in decimal form), remove FLOOR.
If you want seconds in real numbers, keep FLOOR and use what Greg had: FLOOR((S.course_access_minutes - FLOOR(S.course_access_minutes)) * 60) for seconds. Be careful with the parenthesis, though, because you can end up accidentally flooring your decimaled minutes and get 0, and then 0*60 is zero:
FLOOR(
(
course_access_minutes -
FLOOR(course_access_minutes)
) * 60
) as Seconds

SQL Parse Duration String to Seconds

I am using SQL Server 12.0.4213.0 and am new to advanced SQL queries.
I have a nvarchar column named duration containing timespans in one of two formats: 39 Seconds if the duration is under 1 minute, else the format is like 1 Day 18 Hours 2 Minutes. Note that each Day/Hour/Minute/Second can be singular or plural. Also in the first format, if the day is 0, the Day will be omitted (same with Hour).
I need to convert the duration to an integer representing total seconds. 1 Minute 30 Seconds should return 90 and 39 Seconds should return 39.
I think this could be completed with a nested CASE statement. The dataset is very large so I want to be sure I am approaching this efficiently. Any help would be appreciated.
One method I have working in PowerShell is to split the duration column on the space character, loop through the resultant array incrementing counter by 2, take array[counter] and multiply it by the corresponding seconds value determined by array[counter+1], add the product to the total seconds sum.
This is a little complicated, but not terrible. The way you can solve this is by removing the Day(s), Hours(s), Minute(s), Second(s) labels from the string, and replacing these with some delimiting string/character.
Then you can split the resulting values using a CTE.
Then, you'll create the timespan in seconds from each part based on it's location in the delimited string.
Finally, sum over the resulting time-span-part-seconds.
EDIT: Now defined as a function
Like so:
CREATE FUNCTION fn_getDurationSeconds(#duration varchar(100)) returns int
as
BEGIN
DECLARE #durationSeconds INT = 0;
with split (duration, part, remainder) as
(
SELECT
duration,
left(duration, CHARINDEX(':', duration)-1),
right(duration, LEN(duration) - CHARINDEX(':', duration))
FROM
(
SELECT
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(
CASE WHEN #duration LIKE '% Day' or #duration like '% Days' THEN #Duration + ' 0 Hours 0 Minutes 0 Seconds'
WHEN #duration LIKE '% Hour' or #duration like '% Hours' THEN #Duration + ' 0 Minutes 0 Seconds'
WHEN #duration LIKE '% Minute'or #duration like '% Minutes' THEN #Duration + ' 0 Seconds'
ELSE #Duration
END, ' Day', ':'),
' Hour', ':'),
' Minute', ':'),
' Second', ':00'),
's', ''),
' ', '') duration
) t
union all
select
duration,
left(remainder, CHARINDEX(':', remainder) - 1),
right(remainder, LEN(remainder) - CHARINDEX(':', remainder))
from split
where CHARINDEX(':', remainder) > 0
)
SELECT #durationSeconds = sum(seconds)
FROM
(
select
duration,
CASE row_number() over(partition by duration order by LEN(remainder) )
WHEN 4 THEN 24 * 60 * 60
WHEN 3 THEN 60 * 60
WHEN 2 THEN 60
WHEN 1 THEN 1
END * CAST(part AS INT) Seconds
from split
) t
group by duration
order by duration
RETURN #durationSeconds
END
Another option is to use substring and patindex and a couple of common table expressions to get the desired result:
First, create and populate a sample table: (Please save us this step in your future questions)
DECLARE #T AS TABLE
(
Duration varchar(40),
NumberOfSeconds int
)
INSERT INTO #T (Duration) VALUES
('39 Seconds'),
('2 Minutes 4 Seconds'),
('1 Hours 7 Minutes 17 Seconds'),
('3 Days 9 Hour 8 Minutes 25 Seconds')
Then, use one cte to hold the patindex results for each word.
This cte is only there to save us the need to write the patindex over and over again.
Add another cte to this one, that will use substring to extract the values for each time part, using the first cte:
;WITH cte1 as
(
SELECT *,
PATINDEX('%Day%', Duration) As DaysIndex,
PATINDEX('%Hour%', Duration) As HoursIndex,
PATINDEX('%Minute%', Duration) As MinutesIndex,
PATINDEX('%Second%', Duration) As SecondIndex
FROM #T
), cte2 as
(
SELECT *,
CASE WHEN DaysIndex > 0 THEN
CAST(SUBSTRING(Duration, 0, DaysIndex) As int) * 60 * 60 * 24
ELSE
0
END As D,
CASE WHEN HoursIndex > 0 THEN
CAST(
SUBSTRING(Duration,
CASE WHEN DaysIndex > 0 THEN DaysIndex + 4
ELSE 0
END,
CASE WHEN DaysIndex > 0 THEN HoursIndex - DaysIndex - 4
ELSE HoursIndex
END
) As int) * 60 * 60
ELSE
0
END As H,
CASE WHEN MinutesIndex > 0 THEN
CAST(
SUBSTRING(Duration,
CASE WHEN HoursIndex > 0 THEN HoursIndex + 5
ELSE 0
END,
CASE WHEN HoursIndex > 0 THEN MinutesIndex - HoursIndex - 5
ELSE MinutesIndex
END
) As int) * 60
ELSE
0
END As M,
CASE WHEN SecondIndex > 0 THEN
CAST(
SUBSTRING(Duration,
CASE WHEN MinutesIndex > 0 THEN MinutesIndex + 7
ELSE 0
END,
CASE WHEN MinutesIndex > 0 THEN SecondIndex - MinutesIndex - 7
ELSE SecondIndex
END
) As int)
ELSE
0
END As S
FROM cte1
)
Now, update the NumberOfSeconds column:
UPDATE cte2
SET NumberOfSeconds = D + H + M + S
Verify the update results:
SELECT *
FROM #T
Results:
Duration NumberOfSeconds
---------------------------------------- ---------------
39 Seconds 39
2 Minutes 4 Seconds 124
1 Hours 7 Minutes 17 Seconds 4037
3 Days 9 Hour 8 Minutes 25 Seconds 292105

SQL Server : selecting 'WEEKDAY' part by passing date in 'ddd' format

How I can retrieve 'WEEKDAY' part by passing 'ddd' format in SQL Server?
For example in if I pass 'tue' and the response will be 3, beacuse
1 - Sunday
2 - Monday
3 - Tuesday
... like that.
I get the 'WEEKDAY' of current date by executing following query.
select DATEPART(WEEKDAY, GETDATE())
This will return the day number for the given short day name honouring the current DATEFIRST setting.
--Example day name
DECLARE #Day CHAR(3) = 'Tue'
SELECT
DATEPART(WEEKDAY,DATEADD(DAY, Number, GETDATE())) DayNumber
FROM
master..spt_values N
WHERE
N.type = 'P' AND N.number BETWEEN 1 AND 7
AND DATENAME(WEEKDAY,DATEADD(DAY, Number, GETDATE())) LIKE #Day+'%'
You can try similar to this
SELECT *
FROM (
SELECT 1 PK ,'Sunday' Value UNION
SELECT 2,'Monday' UNION
SELECT 3,'Tuesday' UNION
...
SELECT 7,'Saturday'
) T WHERE T.[Value] LIKE '%tue%'
Try this:
SELECT datepart(weekday,getdate()), datename(dw, getdate())
This will return the weekday and name of that day considering ISO_WEEK rule
More detail here
this will give you the weekday no that is not dependant on ##datefirst or langauge setting
select [weekday] = case #weekday_name
when 'Sun' then (7 - ##datefirst + 0) % 7 + 1
when 'Mon' then (7 - ##datefirst + 1) % 7 + 1
when 'Tue' then (7 - ##datefirst + 2) % 7 + 1
when 'Wed' then (7 - ##datefirst + 3) % 7 + 1
when 'Thu' then (7 - ##datefirst + 4) % 7 + 1
when 'Fri' then (7 - ##datefirst + 5) % 7 + 1
when 'Sat' then (7 - ##datefirst + 6) % 7 + 1
end

SQL Adding integer strings with zero values

I am trying to add strings which are integers. I have 201404 as input and I need it to be converted to 201503 so the only way to do this is to increase the year (2014) by 1 and decrease the month 02 by 1.
I have tried the below but the leading zero in the month does not seem to preserve:
DECLARE #YearMonth INT = 201404
, #left INT = 0
, #right INT = 0
SET #YearMonth = CAST(#YearMonth AS VARCHAR(6))
SET #left = CAST(LEFT(#YearMonth, 4) + 1 AS VARCHAR(MAX))
SET #right = RIGHT(#YearMonth, 2) - 1
SET #right = CAST(#right AS VARCHAR(2))
SET #right = RIGHT(('0' + CAST(#right AS VARCHAR(2))), 2)
PRINT #left
PRINT RIGHT('0' + LTRIM(RTRIM(#right)), 6)
Dealing with integer YYYYMM format can be difficult when adding and subtracting months. One method is to convert to a number of months, and then convert back to the format. So, this converts the value to a number of months
select (#YearMonth / 100) * 12 + (#YearMonth % 100)
Then we can add a number, such as 11 and convert back to the integer format:
select (( (#YearMonth / 100) * 12 + (#YearMonth % 100) + 11) / 12) * 100 +
( (#YearMonth / 100) * 12 + (#YearMonth % 100) + 11) % 12)
) as yyyymm
Another method that might be simpler is to use date arithmetic:
select dateadd(11, month, cast(#YearMonth as varchar(255)) + '01')
This returns a date. You can convert it back to the number as:
select (year(dateadd(11, month, cast(#YearMonth as varchar(255)) + '01')) * 100 +
month(dateadd(11, month, cast(#YearMonth as varchar(255)) + '01'))
) as yyyymm
Use REPLICATE
replicate('0', 2 - len(#right)) + #right
Just ran this:
DECLARE #YearMonth INT = 201404;
SELECT CONVERT(VARCHAR(6), DATEPART(YEAR, T.Data) + 1) + RIGHT(100 + DATEPART(MONTH, T.Data) -1, 2)
FROM (VALUES (CONVERT(VARCHAR(8), #YearMonth) + '01')) AS T(Data);
Result:
201503
It's going to pick month number and add 100 to it and then pick 2 right chars from it, so for instance you got 4, it becomes 104 and then RIGHT function picks last 2 characters, which are 04.
Checked with other params, seems fine:
DECLARE #YearMonth INT = 201411;
SELECT CONVERT(VARCHAR(6), DATEPART(YEAR, T.Data) + 1) + RIGHT(100 + DATEPART(MONTH, T.Data) -1, 2)
FROM (VALUES (CONVERT(VARCHAR(8), #YearMonth) + '01')) AS T(Data);
Result:
201510
I would convert implicitly to date, add 11 months and then format back as a string. The integer conversion would be implicit as well.
select format(dateadd(month, 11, str(#YearMonth) + '01'), 'yyyyMM')

Resources