在名为Adventurous的表中有一个名为duration的列,该列的值如下。在“ H”的后缀是小时,在“ M”的后缀是分钟,在“ S”的后缀是秒。我们如何选择小时,分钟和秒,然后将其全部转换为秒,即以秒为形式的所有小时,分和秒的总和。
Duration
--------
PT10M13S
PT13M22S
PT1H2M18S
PT11S
我尝试使用如下所示的子字符串和charindex并尝试创建一个函数,但出现错误:
Declare @Duration varchar(30) ='PT16H13M42S', @Dur varchar(10)
Declare @hours int
declare @mins int
declare @secs int
declare @len int
select @len = len(substring (@Duration, 3, len(@Duration))), @Dur=substring (@Duration, 3, len(@Duration))
select @hours = charindex('H', @Dur)
select substring(@Dur, 1, @hours-1)
select @Duration=substring (@Dur, @hours+1, len(@Dur))
select @mins = charindex('M', @Duration)
select substring(@Duration, 1, @mins-1)
select @Dur=substring (@Duration, @mins+1, len(@Duration))
select @secs= charindex('S', @Dur)
select substring(@Dur, 1, @Secs-1)
select @len, @Dur, @Duration
示例PT1H2M18S = 1 * 3600 + 2 * 60 + 18 = 3738
答案 0 :(得分:2)
尝试一下:
Declare @t table (duration varchar(50))
insert into @t values ('PT1H2M18S')
select
convert(int,substring(duration,CHARINDEX('PT',duration)+2,(CHARINDEX('H',duration)-CHARINDEX('PT',duration))-2))*3600 +
convert(int,substring(duration,CHARINDEX('H',duration)+1,(CHARINDEX('M',duration)-CHARINDEX('H',duration))-1))*60 +
convert(int,substring(duration,CHARINDEX('M',duration)+1,(CHARINDEX('S',duration)-CHARINDEX('M',duration))-1))
from @t
答案 1 :(得分:2)
另一种可能的方法是将输入的Duration
文本转换为有效的T-SQL
表达式('PT1H2M18S'
将转换为'1*3600+2*60+18*1+0'
)。之后,请考虑以下两个选项:
生成并执行动态语句,该语句将计算每个表达式或
定义一个函数进行计算
输入:
CREATE TABLE #Data (
Duration varchar(50)
)
INSERT INTO #Data
(Duration)
VALUES
('PT10M13S'),
('PT13M22S'),
('PT1H2M18S'),
('PT100H'),
('PT11S')
动态声明:
DECLARE @stm nvarchar(max)
SET @stm = N''
SELECT @stm = @stm +
CONCAT(
'UNION ALL SELECT ''',
Duration,
''' AS [Duration], ',
REPLACE(REPLACE(REPLACE(REPLACE(Duration, 'H', '*3600+'), 'M', '*60+'), 'S', '*1+'), 'PT', ''),
'0 AS [Seconds] '
)
FROM #Data
SET @stm = STUFF(@stm, 1, 10, N'')
EXEC (@stm)
用户定义的功能:
CREATE FUNCTION [udfCalculateHMS] (@expression varchar(100))
RETURNS int
AS
BEGIN
DECLARE @result int
DECLARE @s varchar(100)
--
SET @result = 0
WHILE (CHARINDEX('+', @expression) > 0) BEGIN
SET @s = SUBSTRING(@expression, 1, CHARINDEX('+', @expression) - 1)
SET @expression = STUFF(@expression, 1, CHARINDEX('+', @expression), '')
SET @result = @result +
CONVERT(int, SUBSTRING(@s, 1, CHARINDEX('*', @s) - 1)) *
CONVERT(int, STUFF(@s, 1, CHARINDEX('*', @s), ''))
END
-- Return value
RETURN @result
END
SELECT
Duration,
dbo.udfCalculateHMS(CONCAT(REPLACE(REPLACE(REPLACE(REPLACE(Duration, 'H', '*3600+'), 'M', '*60+'), 'S', '*1+'), 'PT', ''), '0')) AS Seconds
FROM #Data
输出:
Duration Seconds
PT10M13S 613
PT13M22S 802
PT1H2M18S 3738
PT100H 360000
PT11S 11
答案 2 :(得分:0)
这是我要在字符串中移动以拉出正确的整数值的方式。要偏移的字符数可能会有所不同,具体取决于您是否可以每小时,每分钟和每秒钟更改字符数。但是原理应该可以帮助您前进。
Declare @Duration varchar(30) ='PT16H13M42S'
select * from
(values(substring(@Duration,CHARINDEX('PT',@duration)+2,(CHARINDEX('H',@Duration)-CHARINDEX('PT',@Duration))-2),
substring(@Duration,CHARINDEX('H',@duration)+1,(CHARINDEX('M',@Duration)-CHARINDEX('H',@Duration))-1),
substring(@Duration,CHARINDEX('M',@duration)+1,(CHARINDEX('S',@Duration)-CHARINDEX('M',@Duration))-1))) duration ([Hours], [Minutes], [Seconds]);
答案 3 :(得分:0)
使用Tally Table和最可靠的ISNUMERIC SQL函数引发答案
这对于小型数据集应该很好。我还假设您有有效的数字,即小时部分不是> 24,分钟部分或秒部分不是> 60
create table #t(duration nvarchar(max));
insert into #t values
('PT10M13S')
,('PT13M22S')
,('PT1H2M18S')
,('PT11S')
select
totalseconds= sum(m.factor* case when ISNUMERIC(substring(duration, r-2,2))=1 then substring(duration, r-2,2) else substring(duration, r-1,1) end ),
duration from #t
cross join
(
select r=row_number() over (order by (select NULL))-1
from sys.objects s1 cross join sys.objects s2
)t
join
(values('S',1),('M',60),('H',3600)) m(part,factor)
on r<=len(duration) and substring(duration, r,1) =m.part
group by duration
drop table #t
PS:请参见此SO链接,该链接表明标量UDF比ISNUMERIC更快 Fastest way to check if a character is a digit?