【问题标题】:SQL Server 2008 R2 fails on cast varchar->timeSQL Server 2008 R2 在强制转换 varchar->time 时失败
【发布时间】:2015-11-26 01:26:23
【问题描述】:

我在我们的 SQL Server 2008 R2 服务器中遇到了一个奇怪的错误。 varchartime 的转换失败,具体取决于顶级语句的 SELECT 子句中使用的其他列。重现问题的代码

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

还有查询:

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

这将失败:

消息 241,第 16 级,状态 1,第 5 行
从字符串转换日期和/或时间时转换失败。

但是,当我将最后两行更改为其中任何一行时,它都能正常工作:

select unit, label, [date], value, [time] 
from topCte
select unit, label, [date], cast([time] as time)
from topCte

我想强调的是,我完全意识到这段代码是次优的,并且我知道如何重新编写它,以便通过重写仍然满足业务需求的代码来避免错误。但是这个错误不应该以这种方式发生,我很好奇是什么触发了它。

【问题讨论】:

  • time 的值是多少?
  • 你说你知道它不是最理想的并且你知道如何解决它。该功能需要完全重写。它看起来像是一种基于循环的分离器。我会先解决这个问题。 sqlperformance.com/2012/07/t-sql-queries/split-strings
  • @roryap - 时间由@s 的两个子字符串生成,因此作为 [time] 输入的值是 '14:51:00' 和 '17:21:00'
  • @SeanLange 我说我知道这不是最有效的,我知道如何避免这个问题,但是根据我对 SQL Server 工作原理的理解,这个问题不应该发生所以我想知道是否这是一个错误,发生这种情况的根本原因是什么。我说我知道如何修改查询——而不是如何修复它。这是2个不同的东西。该函数不需要完全重写,因为对于我正在处理的数据量来说,它已经足够高效了。我添加了它,因此代码是完全可重现的。至于您的替代方案 - 感谢您提供链接,但所提供的改进可以忽略不计。
  • 如果这是您唯一需要拆分器的时候,则过早优化。多年来,我一直在使用实际的 Moden 版本,而不是那里发布的版本。由于 sql server central 的社区,它随着时间的推移而发展和改进。正如我所说,我强烈推荐它,但这是你的电话。真正的问题是当其他人看到您的拆分器并将其用于大型表格上的非常长的字符串时。表演顿时如火龙一般,让人心惊肉跳。去过那里,做到了。

标签: sql sql-server tsql sql-server-2008-r2


【解决方案1】:

我认为 SQL Server 运行上层查询的方式有问题。 我会将结果存储到临时表中,然后根据需要转换结果

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

【讨论】:

  • 是的 - 我为解决这个问题做了很多快速而肮脏的工作。它只是让我感到困惑 SQL Server 如何遇到错误。不是倒数第二个评论 - 如果我只是从最终选择中删除 [value],它会起作用。
  • 好的,很好。我的建议是重写查询并为您的“a”、“b”、“c”、“cte”、“topCte”使用临时表。您将拥有更多控制权,代码将更加“可读”
【解决方案2】:

问题是 [time] 列的大小写表达式。如果您在 select 语句中删除强制转换,您将看到像 14:51:00 这样对时间无效的值。您最初转换为 varchar 但未指定大小,这是另一个问题。确实没有必要将其转换为 varchar,因为它已经是 varchar 值。

这是您的代码的工作版本。

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

【讨论】:

  • 实际上它也给了我错误:“从字符串转换日期和/或时间时转换失败。”。此外 - 当您不输入要转换的长度或字符类型时,它默认为 30 - 这是 SQL Server 的规范。
  • 还有为什么 14:51:00 对时间无效?这有效:选择演员('14:51:00' as time)
  • 是的,默认长度已指定,但默认长度也会根据使用情况而变化。为什么要在明确时依赖默认值只是几个击键。是的,你是对的,它对时间有效。奇怪的是,这段代码使用您的函数和提供的示例数据在我的系统上运行时出现问题。
  • 你是什么意思 - 默认长度也会根据使用情况而变化?可以使用默认值。当我将值 - 最终 - 转换为时间时,我不可能有一个长度超过 30 个字符的字符串可以转换为时间。我并不反对明确地说没有人应该使用明确的值,但这是与这里无关的细节之一。
  • 至于为什么这段代码会抛出错误,你有更多的数据导致这个失败。它在我的系统上运行得很好。
猜你喜欢
  • 2013-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-07
  • 1970-01-01
  • 1970-01-01
  • 2013-06-05
相关资源
最近更新 更多