需要使用SQL从字符串变量使用函数来计算秒和

时间:2019-05-13 06:32:02

标签: sql-server postgresql sql-server-2008

在名为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

4 个答案:

答案 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?

相关问题