2013-10-16 113 views
0

我有下面几列(以提供的示例项)的表: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 

我试图将它转换为datetime,当然这没有奏效。与转换一样。 不断收到以下消息:

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

2个主要问题是:

是这种类型的转换的甚至有可能与这个现有的字符串格式?

如果是这样,你能引导我走向正确的方向,所以我可以做到这一点?

谢谢!

+1

,你会'61年8月4 Weeks'期待什么如同'datetime'?甚至不清楚一年或一个月有多少天。什么是“零时间”?是否应该从这个'datetime'中减去或添加时间? –

+1

没有。这是不可能的。那些不是日期。他们是持续时间或时间段。你需要知道“基准时间”(aka epoch)是什么,所以你可以这样做,那些日期是相对的。例如jan 1 1980 + 20 years + 8 months + 3 weeks =你想要保存在db中的实际日期。 –

+0

有一些调整是可能的 - 但为了将上述内容转换成日期,你需要表达它应该如何成为一个约会 - 也就是说,它是20年,8个月和3周以来/直到什么时候? – JohnLBevan

回答

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

这似乎很有用!我会试试这个。 谢谢你的帮助约翰! – George

0

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

1

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

至少有两种方法可以做到这一点,编写一个函数来解析和转换相对时间标签或使用.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, [email protected], @result) 
    ELSE IF (@label = 'Weeks') OR (@label = 'Week') 
    SET @result = DATEADD(ww, [email protected], @result) 
    ELSE IF (@label = 'Months') OR (@label = 'Month') 
    SET @result = DATEADD(mm, [email protected], @result) 
    ELSE IF (@label = 'Years') OR (@label = 'Year') 
    SET @result = DATEADD(yy, [email protected], @result) 

END 

SELECT @result 
-- So if this works correctly, 
-- 20 Years 8 Months 3 Weeks from 2013-10-16 == 1993-01-26 
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 
相关问题