我有一个包含以下列的表(提供了一些示例条目):
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.
主要有两个问题:
这种现有的字符串格式是否可以实现这种转换?
如果是这样,你能引导我朝着正确的方向前进,这样我才能做到这一点吗?
谢谢!
答案 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字段,它们是年龄,有您需要出生日期的日期时间,并且它们以某种方式将此年龄添加到它