2016-05-12 65 views
2

說我有這個表:動態SQL中使用不符合sp_executesql的和參數

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來做到這一點,沒有參數,並建立完整的SQL字符串,但我想知道我是否可以用params做到這一點。謝謝!

+1

以備將來使用,你可能要考慮使用「表值參數」,所以,你可以通過@NotInList作爲一個表,然後再加入或執行WHERE NOT EXISTS該表。這可以節省你不得不使用動態SQL。 –

回答

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 
+0

這真的很好 - 謝謝你! – DakotaPaul

1

傳遞給sp_executesql的參數被視爲一個單一值,而不是列表。所以當你在傳遞「AB,CD」您的查詢變得

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

查看回答以下問題的解決方法選項

sp_executesql with 'IN' statement

+0

謝謝。我無法相信當我搜索時我無法找到答案。 – DakotaPaul

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' 

我們所說的splitString功能的where子句中

然後使用你的代碼略有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