当 where 子句与 table 值函数一起使用时会发生错误?

Error occurs when a where clause is used with a table valued function?

我有以下 table 值的 UDF:

CREATE FUNCTION [dbo].[funParseRestParams](
    @Params NVARCHAR(MAX),
    @Delim VARCHAR(100))
    RETURNS TABLE
    AS

        RETURN (
            SELECT 
                LTRIM(RTRIM(SUBSTRING([Argument], 1, CHARINDEX('=', [Argument], 1) - 1))) Parameter,
                LTRIM(RTRIM(SUBSTRING([Argument], CHARINDEX('=', [Argument], 1) + 1, LEN([Argument]) - CHARINDEX('=', [Argument], 1)))) Value
            FROM (          
                SELECT 
                    LTRIM(RTRIM(SUBSTRING(@Params, [Number], CHARINDEX(@Delim, @Params + @Delim, [Number]) - [Number]))) [Argument]
                FROM (
                    SELECT 
                        ROW_NUMBER() OVER (ORDER BY name) [Number]
                    FROM sys.all_objects) AS x
                WHERE Number <= LEN(@Params) AND SUBSTRING(@Delim + @Params, [Number], LEN(@Delim)) = @Delim) AS y);

GO

我正在 运行 使用来自 ReportServer.dbo.ExecutionLogStorage 的参数字符串在游标中进行此操作。参数看起来像这样:"Some%20Param%20Name=Hello&Some%20Other%20Param=NoLuck"

当我 运行 游标中的函数是这样的:

SELECT * FROM Reports.dbo.funParseRestParams(@Param, '&')

一切都很好,但是当我添加一个 where 子句时:

    SELECT * FROM Reports.dbo.funParseRestParams(@Param, '&') R 
    WHERE R.[Parameter] = 'AccessType'

我每次都收到以下错误消息:

Msg 537, Level 16, State 3, Line 13
Invalid length parameter passed to the LEFT or SUBSTRING function.

那么为什么我的 where 子句会终止函数?

编辑:添加一些额外的东西。您可以将下面的代码粘贴到 SQL 服务器以复制此错误。该错误仅在使用 "first" 列(where 子句中的参数列)时发生。在 where 子句中使用 "second" 值列不会导致错误。您可以删除下面的评论以查看实际效果:

DECLARE @Params NVARCHAR(MAX)
DECLARE @Delim VARCHAR(1)

SET @Params = 'Greeting=Hello&Name=George&Dessert=Jello'
SET @Delim = '&'

SELECT * FROM (
    SELECT 
        LTRIM(RTRIM(SUBSTRING([Argument], 1, CHARINDEX('=', [Argument], 1) - 1))) Parameter,
        LTRIM(RTRIM(SUBSTRING([Argument], CHARINDEX('=', [Argument], 1) + 1, LEN([Argument]) - CHARINDEX('=', [Argument], 1)))) Value
    FROM (          
        SELECT 
            LTRIM(RTRIM(SUBSTRING(@Params, [Number], CHARINDEX(@Delim, @Params + @Delim, [Number]) - [Number]))) [Argument]
        FROM (
            SELECT 
                ROW_NUMBER() OVER (ORDER BY name) [Number]
            FROM sys.all_objects) AS x
        WHERE [Number] <= LEN(@Params) AND SUBSTRING(@Delim + @Params, [Number], LEN(@Delim)) = @Delim) AS y
) R 
--WHERE R.Parameter = 'AccessType'
--WHERE R.Parameter = 'Greeting'
--WHERE R.Value = 'Bananas'
--WHERE R.Value = 'Jello'

您不妨考虑以下内容:

CREATE FUNCTION dbo.SplitStrings_Moden
(
   @List NVARCHAR(MAX),
   @Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING AS
RETURN
  WITH E1(N)        AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 
                         UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 
                         UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1),
       E2(N)        AS (SELECT 1 FROM E1 a, E1 b),
       E4(N)        AS (SELECT 1 FROM E2 a, E2 b),
       E42(N)       AS (SELECT 1 FROM E4 a, E2 b),
       cteTally(N)  AS (SELECT 0 UNION ALL SELECT TOP (DATALENGTH(ISNULL(@List,1))) 
                         ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E42),
       cteStart(N1) AS (SELECT t.N+1 FROM cteTally t
                         WHERE (SUBSTRING(@List,t.N,1) = @Delimiter OR t.N = 0))
  SELECT Item = SUBSTRING(@List, s.N1, ISNULL(NULLIF(CHARINDEX(@Delimiter,@List,s.N1),0)-s.N1,8000))
    FROM cteStart s;

此代码归功于 Jeff Moden here。该站点还有其他方法。可能值得采用经过充分审查的解决方案(您也必须自己审查),而不是试图重新发明轮子并且不得不花费过多的时间来调试它。

我访问了 MSDN 以搜索答案,我得到了一个。我仍在查看细节,但出于某种原因,以下作品在原始作品中没有。第一个子查询的第二行被改变:

DECLARE @Params NVARCHAR(MAX)
DECLARE @Delim VARCHAR(1)

SET @Params = 'Greeting=Hello&Name=George&Dessert=Jello'
SET @Delim = '&'

SELECT * FROM (
    SELECT 
        LTRIM(RTRIM(SUBSTRING([Argument], 0, CHARINDEX('=', [Argument], 1) ))) Parameter,
        LTRIM(RTRIM(SUBSTRING([Argument], CHARINDEX('=', [Argument], 1) + 1, LEN([Argument]) - CHARINDEX('=', [Argument], 1)))) Value
    FROM (          
        SELECT 
            LTRIM(RTRIM(SUBSTRING(@Params, [Number], CHARINDEX(@Delim, @Params + @Delim, [Number]) - [Number]))) [Argument]
        FROM (
            SELECT 
                ROW_NUMBER() OVER (ORDER BY name) [Number]
            FROM sys.all_objects) AS x
        WHERE [Number] <= LEN(@Params) AND SUBSTRING(@Delim + @Params, [Number], LEN(@Delim)) = @Delim) AS y
) R 

--WHERE R.Parameter = 'AccessType'
WHERE R.Parameter = 'Greeting'
--WHERE R.Value = 'Bananas'
--WHERE R.Value = 'Jello'