在动态SQL中使用带有sp_executesql和参数的NOT IN

时间:2016-05-12 16:52:22

标签: sql-server sql-server-2008 tsql

说我有这张桌子:

create table CodeTable (
    CodeType int, 
    CodeVal char(2), 
    CodeDesc varchar(50))

insert CodeTable values 
    (1, 'AB', 'Desc for AB'), 
    (1, 'CD', 'Desc for CD'), 
    (1, 'DE', 'Desc for DE'),
    (2, 'FG', 'Desc for FG'),
    (2, 'HI', 'Desc for HI')

我希望存储过程从表中获取值,但是如果需要则排除指定的代码:

CREATE PROCEDURE [dbo].[GetCodes]
    @CodeType int,
    @NotInList varchar(100) = ''
AS
BEGIN
    SET NOCOUNT ON;

    declare @sql nvarchar(1000) = '
        select CodeType, CodeVal, CodeDesc
        from CodeTable
        where CodeType = @CodeType'

    if isnull(@NotInList, '') <> ''
        set @sql += ' and CodeVal not in (@NotInList)'

    exec sp_executesql @sql, N'@CodeType int, @NotInList varchar(100)', @CodeType, @NotInList
END

这些工作符合要求:

exec [dbo].[GetCodes] 1, ''
exec [dbo].[GetCodes] 1, 'AB'

但这些给了我所有代码:

exec [dbo].[GetCodes] 1, 'AB,CD'
exec [dbo].[GetCodes] 1, '''AB'',''CD'''

我错过了什么?我知道我可以使用sp_executesql而不使用params来构建完整的SQL字符串,但我想知道我是否可以使用params。谢谢!

3 个答案:

答案 0 :(得分:1)

传递给sp_executesql的参数被视为一个单独的值,而不是列表。所以当你传入AB,CD&#39;您的查询变为

      select CodeType, CodeVal, CodeDesc  from CodeTable where CodeType = 1 and CodeVal not in ( 'AB,CD' ) 

有关解决方法选项,请参阅以下问题的答案

sp_executesql with 'IN' statement

答案 1 :(得分:1)

试试这个......这段聪明的代码消除了使用函数分割逗号分隔值的痛苦。但它的聪明和快速。

变量@NotInList也是可选的,如果你传递一个值,它将被添加到实际的select子句中,否则它将被忽略。

CREATE PROCEDURE [dbo].[GetCodes]
    @CodeType int,
    @NotInList varchar(100) = NULL
AS
BEGIN
    SET NOCOUNT ON;

    declare @xml xml,@SQL NVARCHAR(MAX);

    set @xml = N'<root><r>' + replace(@NotInList,',','</r><r>') + '</r></root>'

 SET @SQL = N'select CodeType, CodeVal, CodeDesc
              from CodeTable
              where CodeType = @CodeType '
          + CASE WHEN @NotInList IS NOT NULL THEN 
          N' AND CodeVal NOT IN (
                                select r.value(''.'',''varchar(max)'') as item
                                from @xml.nodes(''//root/r'') as records(r)
                                )' ELSE N'' END 

    exec sp_executesql @sql
                    , N'@CodeType int, @xml XML'
                    , @CodeType
                    , @Xml
END

答案 2 :(得分:0)

这是我在dynamic项目列表中执行这些操作的一种方式。

我有一个表函数,它将采用一个连接的字符串,然后像我这样将它拆分成一个表。

FUNCTION [dbo].[SplitString] (@Value NVARCHAR(MAX), @SplitCharacter NVARCHAR(1), @AddWildCard BIT = 1)
RETURNS @Results TABLE 
(
    String NVARCHAR(MAX)    
    , Sequence INT 
)

AS 

BEGIN 

    DECLARE @Splitter NVARCHAR(MAX)
    DECLARE @SplitPosition INT
    DECLARE @SequenceNumber INT 
    SET @SequenceNumber = 1
--  PRINT 'ENTERING LOOP SPLITTER'
    WHILE   LEN(@Value) > 0
    BEGIN       
--      PRINT 'GETTING FIRST POSITION OF SPLITTER CHARACTER'
        SET @SplitPosition =  CHARINDEX(@SplitCharacter,@Value) 
        IF  @SplitPosition = 0 
        BEGIN 
            SET @SplitPosition = LEN(@Value) 
        END 

        SET @Splitter = REPLACE(SUBSTRING(LTRIM(@Value),1,@SplitPosition),@SplitCharacter,'')

        INSERT INTO  @Results 
        SELECT  
            CASE 
                WHEN @AddWildCard = 1 THEN 
                    '%' + RTRIM(LTRIM(@Splitter)) +  '%' 
                ELSE    
                    RTRIM(LTRIM(@Splitter)) 
            END 
        , @SequenceNumber



        SET 
            @Value =    CASE 
                            WHEN @SplitPosition > 0 THEN  
                                SUBSTRING(@Value,(@SplitPosition + 1 ),LEN(@Value)) 
                            ELSE 
                                '' 
                        END 
        SET 
            @SequenceNumber = @SequenceNumber + 1
    END 

--  PRINT 'RETURNING VALUE'

    RETURN 

END 

所以,例如,如果我们有第一个场景:

exec [dbo].[GetCodes] 1, 'AB,CD'

我们将在where子句

中调用splitString函数

然后使用上面代码的一些mod:

DECLARE @CodeType int
DECLARE @NotInList NVARCHAR(max) 
SET @CodeType = 1
SET @NotInList = 'AB,CD'

declare @CodeTable table  (
    CodeType int, 
    CodeVal char(2), 
    CodeDesc varchar(50))

insert @CodeTable values 
    (1, 'AB', 'Desc for AB'), 
    (1, 'CD', 'Desc for CD'), 
    (1, 'DE', 'Desc for DE'),
    (2, 'FG', 'Desc for FG'),
    (2, 'HI', 'Desc for HI')

  select CodeType, CodeVal, CodeDesc
        from @CodeTable
        where 
            CodeType = @CodeType
            AND 
                (CASE WHEN LEN(ISNULL(@NotInList,0)) > 0 THEN 
                    CASE WHEN CodeVal NOT IN (SELECT string FROM dbo.SplitString(@NotInList, ',',0)) THEN 1 ELSE 0 END 
                ELSE 
                    1 END ) = 1

这样做意味着您不必运行sp_executesql