表值函数的子查询无法正常工作

时间:2015-01-22 11:21:58

标签: sql-server tsql

DECLARE @temp AS TABLE (id INT, NAME VARCHAR(20) )

DECLARE @str VARCHAR(20) = '1,2'

INSERT INTO @temp (id, NAME)
VALUES (1, ''), (2, ''), (2, '')

SELECT * 
FROM @temp a
WHERE id IN ((SELECT String FROM dbo.FN_SplitStrings(@str,',')))

运行此

时出现以下错误
  

将varchar值“1,2”转换为数据类型时转换失败   中间体

代码:

CREATE function [dbo].[FN_SplitStrings] 
( 
   @StringToSplit varchar(8000), 
   @Separator varchar(128)
) 
RETURN TABLE 
AS 
   RETURN 
     with indices as 
     ( 
        select 
            0 S, 1 E 
        union all 
        select 
            E, charindex(@Separator, @StringToSplit, E) + len(@Separator) 
        from 
            indices 
        where E > S
     ) 
     select 
         substring(@StringToSplit,S, case when E > len(@Separator) 
 then e-s-len(@Separator) else len(@StringToSplit) - s + 1 end) String ,
         S StartIndex 
     from 
         indices 
     where 
         S > 0 

2 个答案:

答案 0 :(得分:0)

试试这个。这种拆分可以在没有功能的情况下使用

DECLARE @temp AS TABLE
(
   id INT,
   NAME VARCHAR(20)
)

DECLARE @str VARCHAR(20)='1,2'
INSERT INTO @temp
        ( id, NAME )
VALUES  ( 1, '' ),
         ( 2, ''),
         ( 2, '')

SELECT * FROM @temp a
 WHERE id IN 
 (
        SELECT LTRIM(RTRIM(Split.a.value('.', 'VARCHAR(100)'))) 'KeyWords' 
        FROM  
        (
             -- To change ',' to any other delimeter, just change ',' before '</M><M>' to your desired one
             SELECT CAST ('<M>' + REPLACE(@str, ',', '</M><M>') + '</M>' AS XML) AS Data                
        ) AS A 
        CROSS APPLY Data.nodes ('/M') AS Split(a)
)

编辑:

你在拆分功能方面遇到了一些问题。

<强>功能

ALTER FUNCTION dbo.FN_SplitStrings(@StringToSplit varchar(8000),@Separator char(1))
RETURNS table
AS
RETURN (
    WITH splitter_cte AS (
      SELECT CHARINDEX(@Separator, @StringToSplit) as pos, 0 as lastPos
      UNION ALL
      SELECT CHARINDEX(@Separator, @StringToSplit, pos + 1), pos
      FROM splitter_cte
      WHERE pos > 0
    )
    SELECT SUBSTRING(@StringToSplit, lastPos + 1,
                     case when pos = 0 then 80000
                     else pos - lastPos -1 end) as String
    FROM splitter_cte
  )

<强>查询

DECLARE @temp AS TABLE (id INT, NAME VARCHAR(20) )

DECLARE @str VARCHAR(20) = '1,2'

INSERT INTO @temp (id, NAME)
VALUES (1, ''), (2, ''), (2, '')

SELECT * 
FROM @temp a
WHERE id IN ((SELECT String FROM dbo.FN_SplitStrings(@str,',')))

答案 1 :(得分:0)

原因是数据类型的混合以及您的函数是内联表值函数的事实,这意味着它在查询优化发生之前嵌入到查询中。

如果您从函数中删除where S > 0并使用1,2执行该函数,则该函数的结果为:

String
------
1,2
1
2

注意值为1,2的第一行。

当优化器使用您的查询完成其工作时,在评估函数的where子句之前完成与列id的比较。在该比较中,您对int进行了隐式转换,1,2无法转换为int

要解决此问题,您可以确保split函数的String列始终为int(并且可能更改过程中列的名称)。

 select 
     cast(substring(@StringToSplit,S, case when E > len(@Separator) 
                                   then e-s-len(@Separator) 
                                   else len(@StringToSplit) - s + 1 
                                 end) as int) String ,