DynamicSQL Split函数用于添加逗号分隔字符串的条件不起作用

时间:2017-11-16 16:31:08

标签: sql-server tsql split dynamic-sql

我正在尝试使用split函数为查询添加一系列值。我也想把这些价值“或”在一起,但首先要做的事情。我正在按照我在一个使用split函数的更大查询中找到的一个例子,所以我做了一个小查询来试图弄清楚它是如何工作的。到目前为止,我得到的只是错误*“名称'选择*来自Country As sp WHERE(sp.CountryID in(SELECT [Value] FROM dbo.Split('2,22,',','))) '不是一个有效的标识符。“*我知道DynamicSQL,我不太确定这个分割函数应该如何工作。

DECLARE @Countries varchar(MAX);
DECLARE @FiltersOn bit;
DECLARE @Country int;
DECLARE @Query  varchar(MAX);

Set @FiltersOn = 0;
Set @Query = 'Select * from Country As sp ';
Set @Countries ='2,22,'



        IF ( @Countries IS NOT NULL )
            BEGIN
                IF ( @FiltersOn = 1 )
                    BEGIN
                        SET @Query = @Query + ' AND '
                    END
                ELSE
                    BEGIN
                        SET @Query = @Query + ' WHERE '
                        SET @FiltersOn = 1
                    END

                SET @Query = @Query
                    + '(sp.CountryID in (SELECT [Value] FROM dbo.Split('''
                    + @Countries + ''', '','')))'
            END

            EXEC @Query

这是国家/地区表的定义:

CREATE TABLE [dbo].[Country](
    [CountryID] [int] IDENTITY(1,1) NOT NULL,
    [AgentID] [int] NULL,
    [Name] [varchar](50) NULL,
    [CountryLookupID] [int] NOT NULL

这是拆分功能代码:

CREATE FUNCTION [dbo].[Split]
(
    @String varchar(8000),
    @Delimiter varchar(10)
)
RETURNS @ValueTable table ([Value] varchar(255))

BEGIN
    DECLARE @NextString varchar(4000)
    DECLARE @Pos int
    DECLARE @NextPos int
    DECLARE @DelimiterCheck varchar(1)

    -- initialise
    SET @NextString = ''
    SET @DelimiterCheck = RIGHT(@String, 1)

    -- add trailing delimiter
    IF (@DelimiterCheck <> @Delimiter)
        SET @String = @String + @Delimiter

    -- find position of first delimiter
    SET @Pos = CHARINDEX(@Delimiter, @String)
    SET @NextPos = 1

    -- loop while there is a delimiter in the string
    WHILE (@Pos <> 0)
    BEGIN
        SET @NextString = SUBSTRING(@String, 1, @Pos - 1)

        INSERT INTO @ValueTable ([Value]) VALUES (@NextString)

        SET @String = SUBSTRING(@String, @Pos + 1, LEN(@String))

        SET @NextPos = @Pos
        SET @Pos = CHARINDEX(@Delimiter, @String)
    END

    RETURN
END

2 个答案:

答案 0 :(得分:0)

您没有正确撰写EXEC声明。

使用EXEC (@Query)代替EXEC @Query

答案 1 :(得分:0)

这不是你问题的直接答案,但我可以推荐阅读这些内容,因为我发现这些解决方案在系统之前会产生问题。

我建议不要使用WHILE循环的解决方案。虽然循环不断产生性能问题,因为SQL Server没有像C#引擎那样优化循环的能力。

不确定为什么要使用动态SQL。如果你想确保没有SQL注入,请使用sp_executesql,因为它检查有害代码,但我会避免使用动态sql,除非我们不知道我们的底层数据的结构(因此需要动态sql )。

刚刚编写了一个函数,用于使用cte进行字符串拆分以保持性能。

    CREATE SCHEMA Util;

    GO

    CREATE FUNCTION Util.String_Split (
        @Text varchar(MAX),
        @SplitChar char
    )
    RETURNS TABLE
    AS
    RETURN(
        WITH cte AS
        (
            SELECT
                1 AS [RowNumber],
                X.Text,
                X.RemainingText
            FROM
                (
                    SELECT
                        SUBSTRING(@Text, 1, CHARINDEX(';', @Text) - 1) AS [Text],
                        SUBSTRING(@Text, CHARINDEX(';', @Text) + 1, LEN(@Text) - CHARINDEX(';', @Text)) AS [RemainingText]
                ) X
            UNION ALL
            SELECT
                cte.RowNumber + 1,
                X.Text,
                X.RemainingText
            FROM
                cte
                CROSS APPLY (
                    SELECT
                        SUBSTRING(cte.RemainingText, 1, ISNULL(NULLIF(CHARINDEX(';', cte.RemainingText) - 1, -1), LEN(cte.RemainingText)))  AS [Text],
                        CASE
                            WHEN CHARINDEX(';', cte.RemainingText) = 0 THEN
                                ''
                            ELSE
                                SUBSTRING(cte.RemainingText, CHARINDEX(';', cte.RemainingText) + 1, LEN(cte.RemainingText))
                        END AS [RemainingText]
                ) X
            WHERE
                X.Text  ''
        )
        SELECT
            cte.Text
        FROM
            cte
        WHERE
            cte.Text IS NOT NULL
    );

然后,您可以使用此调用调用不使用动态部分进行查询:

    SELECT
        *
    FROM
        Country sp
    WHERE
        @Countries IS NULL
        OR
        sp.CountryId IN (
             SELECT * FROM Util.String_Split(@Countries, ',')
        )