Sql server 2008 SQL server-转换失败错误,但实际上不是

Sql server 2008 SQL server-转换失败错误,但实际上不是,sql-server-2008,split,type-conversion,Sql Server 2008,Split,Type Conversion,我有一个用户定义的函数,可以将整数列表拆分为一个值表。我用它来解析输入,为给定的一组类型或状态选择一组记录 这项工作: select * from RequestStatus where RequestStatusUID in (select [value] from dbo.SplitIDs('1,2,3', ',')) 这并不是: select * from Requests where RequestStatusUID in (select [value] from dbo.SplitI

我有一个用户定义的函数,可以将整数列表拆分为一个值表。我用它来解析输入,为给定的一组类型或状态选择一组记录

这项工作:

select * from RequestStatus
where RequestStatusUID in (select [value] from dbo.SplitIDs('1,2,3', ','))
这并不是:

select * from Requests
where RequestStatusUID in (select [value] from dbo.SplitIDs('1,2,3', ','))
Requests查询返回错误“将varchar值“1,2,3”转换为int数据类型时转换失败。”两个表上的RequestStatusUID都是int列。这两个计划在我看来都是一样的。在不相关的查询中,函数的工作方式完全相同。据我所知,只有Requests表有问题

CREATE TABLE [dbo].[Requests]  ( 
[RequestUID]        int IDENTITY(1,1) NOT NULL,
[UserUID]           int NOT NULL,
[LocationUID]       int NOT NULL,
[DateOpened]        date NULL,
[DateClosed]        date NULL,
[RequestStatusUID]  int NOT NULL,
[DiscussionUID]     int NULL,
[RequestTypeUID]    int NOT NULL,
[RequestNo]         varchar(16) NOT NULL,
[LastUpdateUID]     int NOT NULL,
[LastUpdated]       date NOT NULL,
CONSTRAINT [PK_Requests] PRIMARY KEY NONCLUSTERED([RequestUID])
如果我使用另一个返回varchars的函数,并将RequestStatusUID列转换为varchar,则它也可以工作:

select * from Requests
where cast(RequestStatusUID as varchar(4)) in (select [value] from dbo.Split('1,2,3', ','))
作为参考,我使用的SplitIDs函数(的修改版本)。拆分函数与末尾没有强制转换为int的函数相同:

ALTER FUNCTION [dbo].[SplitIDs] ( @str VARCHAR(MAX), @delim char(1)=',' )
RETURNS TABLE
AS
RETURN
(
    with cte as (
        select 0 a, 1 b
        union all
        select b, cast(charindex(@delim, @str, b) + 1 as int)
        from cte
        where b > a
    )
    select cast(substring(@str,a,
    case when b > 1 then b-a-1 else len(@str) - a + 1 end) as int) [value]      
    from cte where a >0
)

我可以使用convert to strings解决方案,但我真的很想知道为什么会失败。

我想您会发现这种语法的性能要好得多:

SELECT r.* FROM dbo.Requests AS r
INNER JOIN dbo.SplitIDs('1,2,3', ',') AS s
ON r.RequestStatusUID = s.value;
由于您的函数选择,谓词仍然有一堆隐式转换,但是连接消除了昂贵的表假脱机。如果使用适当的列列表(仅限于所需的实际列),而不是使用
SELECT*
,您可能会看到性能甚至稍好一些

您的
IN()
查询,带有一个昂贵的表假脱机():

My
JOIN
版本,其中成本将转移到您正在进行的扫描():

以下是运行时指标(当然是基于少量行)-():

转换错误似乎源于函数。所以我用我自己的(下面)。即使在添加了我们最初不知道的外键之后。我不确定原始函数到底有什么问题,但它创建的所有隐式转换似乎都会在某个时候给优化器带来问题。因此,我建议改为:

CREATE FUNCTION dbo.SplitInts
(
   @List       VARCHAR(MAX),
   @Delimiter  VARCHAR(255) = ','
)
RETURNS TABLE
WITH SCHEMABINDING
AS
   RETURN 
   (  
      SELECT [value] = y.i.value('(./text())[1]', 'int')
      FROM 
      ( 
        SELECT x = CONVERT(XML, '<i>' 
          + REPLACE(@List, @Delimiter, '</i><i>') 
          + '</i>').query('.')
      ) AS a CROSS APPLY x.nodes('i') AS y(i)
   );
GO

另外,在(1,2,3)中的RequestStatusUID正常工作的请求中选择*。只是我需要有一个变量,而不是字符串文字。我无法重现,即使我在函数末尾去掉显式转换。你能在sqlfiddle.com上设置一个完整的复制程序吗?您是否也尝试过任何其他返回INT的TVF,或者您是否与此特定函数结过婚?由于观察到的性能问题,我倾向于远离TVFs中的递归CTE方法。在添加外键约束之前,我无法在sqlfiddle.com上重现它。。。看见信息缺失。什么外键约束。到状态表。但是,在我再次移除它之后,问题仍然存在。
DECLARE @param VARCHAR(MAX) = NULL;-- also try = '1,2,3';

SELECT r.*
FROM dbo.Requests AS r
LEFT OUTER JOIN dbo.SplitInts(@param, default) AS s
ON r.RequestStatusUID = s.value
WHERE (r.RequestStatusUID = s.value OR @param IS NULL);