简体   繁体   English

SQL Server 2008 R2在强制转换varchar-> time时失败

[英]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. varchartime失败,这取决于顶层语句的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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM