SQL,如何使用Dynamic Condition逻辑?

时间:2012-07-09 13:02:29

标签: sql sql-server dynamic conditional-statements

当您需要动态WHERE条款时,我可以使用;

CREATE PROCEDURE [dbo].[sp_sel_Articles]
      @articleId            INT              = NULL
    , @title            NVARCHAR(250)    = NULL
    , @accessLevelId    INT              = NULL
AS
BEGIN
     SELECT *
     FROM table_Articles Art
     WHERE
          (Art.ArticleId = @articleId OR @articleId IS NULL)
          AND (Art.Title LIKE '%' + @title + '%' OR @title IS NULL)
          AND (Art.AccessLevelId = @accessLevelId OR @accessLevelId IS NULL)
END

所以,我可以通过ArticleId调用此程序 - 例如

EXEC [sp_sel_Articles] @articleId = 3

但是,有时我需要通过AccessLevelId调用,有时不是通过精确值调用。例如,我需要比给定的accesslevelId更多或更少。

当前程序可以使用

处理 EXACT
Art.AccessLevelId = @accessLevelId

还可以将 CONDITION 类型以及值添加到过程中吗?在这个例子中看起来很奇怪,但请跟我一起承担:

CREATE PROCEDURE [dbo].[sp_sel_Articles]
          @articleId            INT              = NULL
        , @title            NVARCHAR(250)    = NULL
        , @accessLevelId    INT              = NULL
        , **@accessLevelIdCondition**
    AS
    BEGIN
         SELECT *
         FROM table_Articles Art
         WHERE
              (Art.ArticleId = @articleId OR @articleId IS NULL)
              AND (Art.Title LIKE '%' + @title + '%' OR @title IS NULL)
              AND (Art.AccessLevelId **@accessLevelIdCondition** @accessLevelId OR @accessLevelId IS NULL)
    END

也许可以使用一个功能,我不知道。因为,至少有20个程序需要这种灵活性,我需要尽可能多地采用更全面的解决方案,而不是在每个程序中编写IF ELSE条件。

提前致谢,

4 个答案:

答案 0 :(得分:3)

您可能需要使用动态SQL传递运算符。或者你可以传入两个值,例如

@MinAccessLevelID INT,
@MaxAccessLevelID INT

...
WHERE (
   (@MinAccessLevelID IS NULL AND @MaxAccessLevelID IS NULL)
   OR 
   (AccessLevelID >= @MinAccessLevelID AND AccessLevelID <= @MaxAccessLevelID)
)

当你想要精确(例如只有3)时,只需将3传递给两个值。当你想要任何高于3的东西时,将20000000000传递给@Max参数,如果你想要低于3的所有东西,则传递0。

但是你会发现,随着这些排列变得越来越复杂,你最好只使用动态SQL(并设置optimize for ad hoc workloads,这对于计划缓存重用和阻止参数嗅探更好)。

答案 1 :(得分:2)

在申请之前阅读此www.sommarskog.se/dynamic_sql.html

  CREATE PROCEDURE [dbo].[sp_sel_Articles] 
              @articleId            INT              = NULL 
            , @title            NVARCHAR(250)    = NULL 
            , @accessLevelId    INT              = NULL 
            , @accessLevelIdCondition varchar(100)
        AS 
        BEGIN 
                DECLARE @SQL varchar(8000)
    SET @SQL='
         SELECT * 
         FROM table_Articles Art 
         WHERE 
              (Art.ArticleId = '+cast(@articleId as varchar(100))+' OR '+cast(@articleId as varchar(100))+'IS NULL) 
              AND (Art.Title LIKE ''%'' + @title + ''%'' OR @title IS NULL) 
              AND (Art.AccessLevelId '+@accessLevelIdCondition+ cast(@accessLevelId as varchar(100))+' OR '+cast(@accessLevelId as varchar(100))+' IS NULL) '
    EXEC(@sql)
        END 

答案 2 :(得分:1)

您只需制作查询字符串即可创建动态查询

execute ('select count(*) from table' ) 

因此,通过在存储过程中输入的参数,您还可以形成一个可以执行的查询字符串。

答案 3 :(得分:1)

您可以使用案例陈述 - 如果格式不正确,它看起来会有点滑稽,但您可以尝试以下内容:

SELECT Columns FROM SomeTable
WHERE 1 = CASE
              WHEN @SomeOption = '<>' AND SomeValue >= @SomeMinParam AND SomeValue <= SomeMaxParam THEN 1
              WHEN @SomeOption '=' AND SomeValue = @SomeMinParam THEN 1
              ELSE 0
          END     

(尽管Aaron指出 - 你传入的&lt;&gt;并没有真正反映声明中的比较运算符 - 将其更改为有意义的东西:))

在你的情况下:

CREATE PROCEDURE [dbo].[sp_sel_Articles]
    @articleId            INT              = NULL,
    @title            NVARCHAR(250)    = NULL,
    @MinaccessLevelId    INT              = NULL,
    @MaxaccessLevelId     INT          = NULL,
    @accessType       varchar(5)       = '<>'
AS
BEGIN
 SELECT *
 FROM table_Articles Art
 WHERE
      (Art.ArticleId = @articleId OR @articleId IS NULL)
      AND (Art.Title LIKE '%' + @title + '%' OR @title IS NULL)
      AND 1 = CASE 
                  WHEN @accessType = '<>' AND (Art.AccessLevelId = @MinaccessLevelId OR @accessLevelId IS NULL) THEN 1
                  WHEN @accessType = '=' AND (Art.AccessLevelId >= @MinaccessLevelId OR Art.AccessLevelId <= @MaxaccessLevelId) THEN 1
                  ELSE 0
              END
END

对于@accessType参数,可能使用@CompareAccessLevelToMin而不是varchar()。仍然有一个麻烦,就是不要告诉你,将它设置为“假”意味着什么。