TSQL - 将日期字符串转换为datetime

时间:2013-10-16 21:06:59

标签: sql tsql

我有一个包含以下列的表(提供了一些示例条目):

COLUMN NAME: pt_age_old

20 Years 8 Months 3 Weeks
1 Year 3 Months 2 Weeks
58 Years 7 Months
1 Year
7 Years 11 Months 2 Weeks
26 Years 6 Months
56 Years 6 Months
48 Years 6 Months 4 Weeks
29 Years 11 Months
4 Years 3 Months
61 Years 8 Months 4 Weeks

我试图把它投到日期时间,当然这没用。与转换相同。 继续收到以下消息:

Msg 241, Level 16, State 1, Line 2
Conversion failed when converting date and/or time from character string.

主要有两个问题:

这种现有的字符串格式是否可以实现这种转换?

如果是这样,你能引导我朝着正确的方向前进,这样我才能做到这一点吗?

谢谢!

4 个答案:

答案 0 :(得分:2)

这可以使用下面的自定义代码完成 - 我假设您使用的值是人的年龄,并且您正在尝试计算他们的出生日期鉴于他们今天的年龄。

您可以在此处查看以下代码:http://sqlfiddle.com/#!3/c757c/2

create function dbo.AgeToDOB(@age nvarchar(32))
returns datetime
as
begin

    declare @pointer int = 0
    , @pointerPrev int = 1
    , @y nvarchar(6)
    , @m nvarchar(6)
    , @w nvarchar(6)
    , @d nvarchar(6)
    , @result datetime = getutcdate() --set this to the date we're working to/from

    --convert various ways of expressing units to a standard
    set @age = REPLACE(@age,'Years','Y')
    set @age = REPLACE(@age,'Year','Y')
    set @age = REPLACE(@age,'Months','M')
    set @age = REPLACE(@age,'Month','M')
    set @age = REPLACE(@age,'Weeks','W')
    set @age = REPLACE(@age,'Week','W')
    set @age = REPLACE(@age,'Days','D')
    set @age = REPLACE(@age,'Day','D')

    set @pointer = isnull(nullif(CHARINDEX('Y',@age),0),@pointer)
    set @y = case when @pointer > @pointerprev then SUBSTRING(@age,@pointerprev,@pointer - @pointerprev) else null end

    set @pointerPrev = @pointer + 1
    set @pointer = isnull(nullif(CHARINDEX('M',@age),0),@pointer)
    set @m = case when @pointer > @pointerprev then SUBSTRING(@age,@pointerprev,@pointer - @pointerprev) else null end

    set @pointerPrev = @pointer + 1
    set @pointer = isnull(nullif(CHARINDEX('W',@age),0),@pointer)
    set @w = case when @pointer > @pointerprev then SUBSTRING(@age,@pointerprev,@pointer - @pointerprev) else null end

    set @pointerPrev = @pointer + 1
    set @pointer = isnull(nullif(CHARINDEX('D',@age),0),@pointer)
    set @d = case when @pointer > @pointerprev then SUBSTRING(@age,@pointerprev,@pointer - @pointerprev) else null end

    set @result =  dateadd(YEAR, 0 - isnull(cast(@y as int),0), @result)
    set @result =  dateadd(MONTH, 0 - isnull(cast(@m as int),0), @result)
    set @result =  dateadd(Week, 0 - isnull(cast(@w as int),0), @result)
    set @result =  dateadd(Day, 0 - isnull(cast(@d as int),0), @result)

    return @result

end

go

select dbo.AgeToDOB( '20 Years 8 Months 3 Weeks')

注意:这里有很多优化空间;我已经把它简单地留在上面,以帮助清楚它是什么。

答案 1 :(得分:1)

从技术上讲,可以将相对时间标签转换为日期时间,但您需要一个参考日期(截至“2013-10-16”的20年8个月3周)。参考日期很可能是今天(使用GETDATE()或CURRENT_TIMESTAMP),但有可能是另一个日期。您必须解析标签,将其转换为持续时间,然后将持续时间应用于参考时间。这本来就很慢。

至少有两种可能的方法,编写一个FUNCTION来解析和转换相对时间标签或使用.NET扩展函数进行转换。您需要识别所有可能的标签,否则转换将失败。请记住,参考日期很重要,因为2个月不是固定的天数(1月 - 2月= 58或59天)。

以下是该功能的示例:

-- Test data
DECLARE @test varchar(50)
      , @ref_date datetime

SET @test = '20 Years 8 Months 3 Weeks'
SET @ref_date = '2013-10-16' -- or use GETDATE() / CURRENT_TIMESTAMP

-- Logic in function
DECLARE @pos int
      , @ii int
      , @val int
      , @label varchar(50)
      , @result datetime

SET @pos = 0
SET @result = @ref_date

WHILE (@pos <= LEN(@test))
BEGIN
  -- Parse the value first
  SET @ii = NULLIF(CHARINDEX(' ', @test, @pos), 0)
  SET @label = RTRIM(LTRIM(SUBSTRING(@test, @pos, @ii - @pos)))
  SET @val = CAST(@label AS int)
  SET @pos = @ii + 1

  -- Parse the label next
  SET @ii = NULLIF(CHARINDEX(' ', @test, @pos), 0)
  IF (@ii IS NULL) SET @ii = LEN(@test) + 1
  SET @label = RTRIM(LTRIM(SUBSTRING(@test, @pos, @ii - @pos)))
  SET @pos = @ii + 1

  -- Apply the value and offset
  IF (@label = 'Days') OR (@label = 'Day')
    SET @result = DATEADD(dd, -@val, @result)
  ELSE IF (@label = 'Weeks') OR (@label = 'Week')
    SET @result = DATEADD(ww, -@val, @result)
  ELSE IF (@label = 'Months') OR (@label = 'Month')
    SET @result = DATEADD(mm, -@val, @result)
  ELSE IF (@label = 'Years') OR (@label = 'Year')
    SET @result = DATEADD(yy, -@val, @result)

END

SELECT @result
-- So if this works correctly,
-- 20 Years 8 Months 3 Weeks from 2013-10-16 == 1993-01-26

答案 2 :(得分:1)

创建一个将其拆分的功能:

create function f_tst
(
@t varchar(200)
) returns date
begin
declare @a date = current_timestamp
;with split1 as
(
  select 1 start, charindex(' ', @t + ' ', 4)+1 stop
  where @t like '% %'
  union all
  select stop, charindex(' ', @t + ' ', stop + 4)+1 
  from split1
  where charindex(' ', @t, stop) > 0
), split2 as
(
  select cast(left(x.sub, charindex(' ', x.sub)) as int) number, 
  substring(x.sub, charindex(' ', x.sub) + 1, 1) unit 
  from split1
  cross apply (select substring(@t, start, stop - start) sub) x
)
select @a = case unit when 'W' then dateadd(ww, -number, @a)
                       when 'Y' then dateadd(yy, -number, @a)
                       when 'M' then dateadd(mm, -number, @a)
            end
from split2
return @a
end

测试功能:

select dbo.f_tst(age)
from (values('20 Years 8 Months 3 Weeks'),('1 Month') ) x(age)

结果:

1993-01-27
2013-09-17

答案 3 :(得分:0)

不,日期时间类型显示实际日期,例如YYYY-MM-DD HH:MM,您的字符串不是DATE字段,它们是年龄,有您需要出生日期的日期时间,并且它们以某种方式将此年龄添加到它