【发布时间】:2012-09-10 21:54:05
【问题描述】:
我有一个拆分字符串的函数(为了清楚起见,粘贴在末尾)。 此功能在单独使用时按预期工作。 示例:
SELECT value
FROM dbo.mg_fn_Split('2#1','#')
返回
-- value --
-- 2 --
-- 1 --
-----------
但是当在“WHERE IN”子句中使用时,如本例所示(稍后将详细介绍 tableA):
SELECT * FROM TableA WHERE TableA.id IN
(
SELECT value
FROM dbo.mg_fn_Split('2#1','#')
)
我收到错误:“传递给 LEFT 或 SUBSTRING 函数的长度参数无效。”
这里以TableA 为例。使用不同的表(假设它们有 id 列)有时会返回正确的结果,而在其他表上我会得到错误。
我假设它与执行顺序有关,但我仍然看不出什么会“破坏”函数。
我正在寻找“正在发生的事情”的解释,而不是“改用这个”。我知道我可以使用连接来获取结果。
函数定义:
-- Description: Returns a table containing the results of a string-split operation.
-- Params:
-- DelimitedList: The string to split
-- Delimiter: The delimiter char, defaults to ','
-- Columns:
-- Position - The char index of the item
-- Value - The actual item
-- =============================================
CREATE Function [dbo].[mg_fn_Split]
(
@DelimitedList nvarchar(max)
, @Delimiter nvarchar(2) = ','
)
RETURNS TABLE
AS
RETURN
(
With CorrectedList As
(
Select Case When Left(@DelimitedList, Len(@Delimiter)) <> @Delimiter Then @Delimiter Else '' End
+ @DelimitedList
+ Case When Right(@DelimitedList, Len(@Delimiter)) <> @Delimiter Then @Delimiter Else '' End
As List
, Len(@Delimiter) As DelimiterLen
)
, Numbers As
(
Select TOP( Coalesce(DataLength(@DelimitedList)/2,0) ) Row_Number() Over ( Order By c1.object_id ) As Value
From sys.columns As c1
Cross Join sys.columns As c2
)
Select CharIndex(@Delimiter, CL.list, N.Value) + CL.DelimiterLen As Position
, Substring (
CL.List
, CharIndex(@Delimiter, CL.list, N.Value) + CL.DelimiterLen
, CharIndex(@Delimiter, CL.list, N.Value + 1)
- ( CharIndex(@Delimiter, CL.list, N.Value) + CL.DelimiterLen )
) As Value
From CorrectedList As CL
Cross Join Numbers As N
Where N.Value <= DataLength(CL.List) / 2
And Substring(CL.List, N.Value, CL.DelimiterLen) = @Delimiter
)
编辑:我设置了一个小提琴来展示这个: http://sqlfiddle.com/#!3/9f9ff/3
【问题讨论】:
-
内联 UDF 会扩展到查询中,因此可能某些连接操作或过滤器会以您未预料到的不同顺序进行评估。
-
不使用join的原因是什么?恕我直言,如果可能,最好将字符串解析排除在 sql 命令之外。
-
我同意 Martin 的评论,我相信您的函数的 WHERE 子句正在被推送到您的 Select * from TableA。如果您在函数中注释掉以下 And Substring(CL.List, N.Value, CL.DelimiterLen) = @Delimiter 您将遇到由您的 Substring 值之一评估为 -1 引起的相同问题。
-
同意马丁的评论。您可以轻松解决问题。在主查询中,在 SELECT 子句中,在
SUBSTRING的第三个参数中,将NULLIF()应用于被减数:NULLIF(CharIndex(@Delimiter, CL.list, N.Value + 1), 0) - ...。
标签: sql sql-server-2008 user-defined-functions