0

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

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.

主要的2个问题是:

这种现有的字符串格式甚至可以进行这种类型的转换吗?

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

谢谢!

4

4 回答 4

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')

注意:这里有很大的优化空间;我在上面留下了简单的内容,以帮助清楚发生了什么。

于 2013-10-16T21:43:56.397 回答
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
于 2013-10-16T21:22:53.717 回答
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
于 2013-10-17T09:01:16.203 回答
0

不,日期时间类型表示实际日期,例如 YYYY-MM-DD HH:MM,您的字符串不是 DATE 字段,它们是年龄,要确定您需要出生日期的日期时间,它们会以某种方式将这个年龄添加到其中

于 2013-10-16T21:08:44.700 回答