[英]SQL Server 2008 R2 fails on cast varchar->time
I encountered a weird error in our SQL Server 2008 R2 server. 我在SQL Server 2008 R2服务器中遇到一个奇怪的错误。 The cast of a
varchar
to time
fails depending on what other columns are used in SELECT
clause of the top level statement. 将
varchar
为time
失败,这取决于顶层语句的SELECT
子句中使用了哪些其他列。 Code to reproduce the issue 重现问题的代码
CREATE FUNCTION [dbo].[explode]
(
@haystack varchar(max),
@separator varchar(8000)
)
RETURNS
@ret TABLE
(
orderCol int identity(1,1),
value varchar(max)
)
AS
BEGIN
declare @index bigint
set @index = charindex(@separator,@haystack)
while @index > 0
begin
insert into @ret (value) values (substring(@haystack,1,@index-1))
set @haystack = substring(@haystack,@index+len(@separator),len(@haystack)-@index-len(@separator)+1)
set @index = charindex(@separator,@haystack)
end
insert into @ret (value) select @haystack
RETURN
END
And the query: 和查询:
declare @s varchar(1000) = 'a,2015-10-08,1451,1,2,3,4,5,6,7,8,9,10,11;a,2015-10-08,1721,12,13,14,15,16,17,18,19,20,21,22'
declare @units varchar(1000) = 'l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11'
set @units = '@label,@date,@hour,'+@units
;with cte as (select b.value,c.value as unit, a.orderCol as ri from dbo.explode(@s,';') a
cross apply dbo.explode(value,',') b
inner join dbo.explode(@units,',') c
on b.orderCol = c.orderCol),
topCte as (
select c4.unit as unit
,convert(varchar,(case
when len(c3.value) <= 3 then '0' + substring(c3.value,1,1) + ':' + substring(c3.value,2,2)
else (substring(c3.value,1,2) + ':' + substring(c3.value,3,2))
end+':00'),108) as [time]
,c1.value as label
,c2.value as [Date]
,c4.value
from cte c1
inner join cte c2
on c1.ri = c2.ri and c1.unit = '@label' and c2.unit = '@date'
inner join cte c3
on c1.ri = c3.ri and c3.unit = '@hour'
inner join cte c4
on c1.ri = c4.ri and c4.unit not in ('@label','@date','@hour')
)
select unit, label, [date], value, cast([time] as time)
from topCte
This will fail with: 这将失败:
Msg 241, Level 16, State 1, Line 5
消息241,级别16,状态1,第5行
Conversion failed when converting date and/or time from character string.从字符串转换日期和/或时间时转换失败。
However when I change the last two lines into any of these, it works correctly: 但是,当我将最后两行更改为以下任意一行时,它可以正常工作:
select unit, label, [date], value, [time]
from topCte
select unit, label, [date], cast([time] as time)
from topCte
I would like to stress that I'm fully aware that this code is sub optimal and I know how to rework this so to avoid the error by rewriting the code still fullfilling business requirement. 我想强调一点,我完全意识到此代码是次优的,并且我知道如何对其进行重新编写,以便通过重写仍可满足业务需求的代码来避免错误。 However this error shouldn't occur in this way and I'm very curious what is triggering it.
但是,不应以这种方式发生此错误,我很好奇是什么触发了它。
I believe there is something wrong in how SQL Server runs the upper query. 我相信SQL Server如何运行上层查询有问题。 I would store the result into a Temporary Table and then cast the result as desired
我将结果存储到临时表中,然后根据需要转换结果
create table #table_name
(
unit varchar(3),
label varchar(10),
[date] varchar(10),
value varchar(10),
[time] varchar(10),
)
declare @s varchar(1000) = 'a,2015-10-08,1451,1,2,3,4,5,6,7,8,9,10,11;a,2015-10-08,1721,12,13,14,15,16,17,18,19,20,21,22'
declare @units varchar(1000) = 'l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11'
set @units = '@label,@date,@hour,'+@units
;with cte as (select b.value,c.value as unit, a.orderCol as ri from dbo.explode(@s,';') a
cross apply dbo.explode(value,',') b
inner join dbo.explode(@units,',') c
on b.orderCol = c.orderCol),
topCte as (
select c4.unit as unit
,(case
when len(c3.value) <= 3 then '0' + substring(c3.value,1,1) + ':' + substring(c3.value,2,2)
else (substring(c3.value,1,2) + ':' + substring(c3.value,3,2))
end+':00') as [time]
,c1.value as label
,c2.value as [Date]
,c4.value
from cte c1
inner join cte c2
on c1.ri = c2.ri and c1.unit = '@label' and c2.unit = '@date'
inner join cte c3
on c1.ri = c3.ri and c3.unit = '@hour'
inner join cte c4
on c1.ri = c4.ri and c4.unit not in ('@label','@date','@hour')
)
insert into #table_name(unit, label, [date], value, [time])
select unit, label, [date], value, [time]
from topCte
select unit, label, [date], value, cast([time] as time)
from #table_name
The problem is your case expression for the [time] column. 问题是您的[time]列的大小写表达式。 If you remove the cast in the select statement you will see values like 14:51:00 which is invalid for time.
如果您在select语句中删除了强制类型转换,您将看到类似于14:51:00的值,该值在时间上无效。 You are initially converting to a varchar but you don't specify the size which is another issue.
您最初正在转换为varchar,但未指定大小,这是另一个问题。 There really is no need to convert this to a varchar because it is already a varchar value.
确实不需要将其转换为varchar,因为它已经是varchar值。
Here is a working version of your code. 这是您的代码的有效版本。
declare @s varchar(1000) = 'a,2015-10-08,1451,1,2,3,4,5,6,7,8,9,10,11;a,2015-10-08,1721,12,13,14,15,16,17,18,19,20,21,22'
declare @units varchar(1000) = 'l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11'
set @units = '@label,@date,@hour,'+@units
;with cte as (select b.value,c.value as unit, a.orderCol as ri from dbo.explode(@s,';') a
cross apply dbo.explode(value,',') b
inner join dbo.explode(@units,',') c
on b.orderCol = c.orderCol),
topCte as (
select c4.unit as unit
,
case
when len(c3.value) <= 3 then '0' + substring(c3.value,1,1) + ':' + substring(c3.value,2,2)
else (substring(c3.value,1,2) + ':' + substring(c3.value,3,2))
end
as [time]
,c1.value as label
,c2.value as [Date]
,c4.value
from cte c1
inner join cte c2
on c1.ri = c2.ri and c1.unit = '@label' and c2.unit = '@date'
inner join cte c3
on c1.ri = c3.ri and c3.unit = '@hour'
inner join cte c4
on c1.ri = c4.ri and c4.unit not in ('@label','@date','@hour')
)
select unit, label, [date], value, cast([time] as time)
from topCte
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.