使用NULL忽略参数(如果为空)时,存储过程不返回过滤结果

时间:2015-07-16 10:37:21

标签: sql sql-server stored-procedures

我有一个查询,用户可以在其中选择要搜索的列(每个列在Web上都有相应的过滤器)。我已经使用NULL方法尝试忽略参数,如果它作为NULL传递到DB。使所有字段NULL按预期工作并返回所有记录,但在尝试过滤信息时,结果是相同的,查询将返回所有内容。

我似乎无法找出为什么会发生这种情况,它可能是一些非常小而且显而易见的东西,但我却看不到它。

ALTER PROCEDURE [dbo].[GetChatListFilter] 
    @SiteKey int,
    @invited int = NULL,
    @starttime varchar(15),
    @finishtime varchar(15) = NULL,
    @visitor varchar(50) = NULL,
    @wait int = NULL,
    @operators varchar(max) = NULL,
    @department varchar(max) = NULL,
    @skills varchar(max) = NULL,
    @chattime int = NULL,
    @rating int = NULL,
    @email varchar(max) = NULL,
    @message varchar(max) = NULL,
    @dns varchar(max) = NULL,
    @visit varchar(max) = NULL,
    @city varchar(max) = NULL,
    @country varchar(max) = NULL
AS

    SELECT        
        UserChats.Invited, 
        UserChats.StartTime AS Start, 
        UserChats.FinishTime AS Finish, 
        UserChats.VisitorName As Visitor, 
        UserChats.WaitedForSecs AS Wait, 
        UserChats.TakenByUser AS Operator, 
        UserChats.TakenByDept AS [Dept.], 
        dbo.SkillIDsToName(UserChats.SkillIDList) AS Skill,
        UserChats.ChattedForSecs AS [Time], 
        UserChats.Rating AS Rate, 
        UserChats.MessageEmail AS Email, 
        UserChats.MessageText AS [Message], 
        UserChats.VisitorIP AS DNS, 
        UserChats.VisitorVisitNumber AS Visit, 
        VisitDetail.City, 
        VisitDetail.Country
    FROM             
        UserChats 
    INNER JOIN
        VisitDetail ON  UserChats.VisitID =  VisitDetail.VisitID
    WHERE 
        UserChats.SiteKey = @SiteKey AND
        UserChats.Invited = @invited OR @invited is NULL AND
        UserChats.StartTime = @starttime OR @starttime = '01/01/1900' AND
        UserChats.FinishTime = @finishtime OR @finishtime = '01/01/1900' AND
        UserChats.VisitorName = @visitor OR @visitor is NULL AND
        UserChats.WaitedForSecs = @wait OR @wait is NULL AND
        UserChats.TakenByUser = @operators OR @operators is NULL AND
        UserChats.TakenByDept = @department OR @department is NULL AND
        dbo.SkillIDsToName(UserChats.SkillIDList) = @skills OR @skills is NULL AND
        UserChats.ChattedForSecs = @chattime OR @chattime is NULL AND
        UserChats.Rating = @rating OR @rating is NULL AND
        UserChats.MessageEmail = @email OR @email is NULL AND
        UserChats.MessageText = @message OR @message is NULL AND
        UserChats.VisitorIP = @dns OR @dns is NULL AND
        UserChats.VisitorVisitNumber = @visit OR @visit is NULL AND
        VisitDetail.City = @city OR @city is NULL AND
        VisitDetail.Country = @country or @country is NULL

4 个答案:

答案 0 :(得分:3)

你需要括号:

WHERE UserChats.SiteKey = @SiteKey AND
      (UserChats.Invited = @invited OR @invited is NULL) AND
      (UserChats.StartTime = @starttime OR @starttime = '1900-01-01') AND
      (UserChats.FinishTime = @finishtime OR @finishtime = '1900-01-01') AND
      . . .

答案 1 :(得分:2)

正如我上面所说,我可能会在这里寻找动态代码,因为你的查询包含很多OR语句。我已经重写了你的程序。请留下评论或意见。

我能想到所有没有回来的记录 - 你硬编码@starttime@finishtime,只是处理它们。我尝试在这段代码中做到这一点,但是当没有样本数据时,这很难。

ALTER PROCEDURE [dbo].[GetChatListFilter]
(
    @SiteKey INT
    , @invited INT = NULL
    , @starttime VARCHAR(15)
    , @finishtime VARCHAR(15) = NULL
    , @visitor VARCHAR(50) = NULL
    , @wait INT = NULL
    , @operators VARCHAR(MAX) = NULL
    , @department VARCHAR(MAX) = NULL
    , @skills VARCHAR(MAX) = NULL
    , @chattime INT = NULL
    , @rating INT = NULL
    , @email VARCHAR(MAX) = NULL
    , @message VARCHAR(MAX) = NULL
    , @dns VARCHAR(MAX) = NULL
    , @visit VARCHAR(MAX) = NULL
    , @city VARCHAR(MAX) = NULL
    , @country VARCHAR(MAX) = NULL
)
AS
BEGIN
    SET NOCOUNT ON;
    BEGIN TRY
        DECLARE @SQL NVARCHAR(MAX)
            , @SQLParams NVARCHAR(MAX);

        SET @SQL = N'
            SELECT UC.Invited
                , UC.StartTime AS Start
                , UC.FinishTime AS Finish
                , UC.VisitorName AS Visitor
                , UC.WaitedForSecs AS Wait
                , UC.TakenByUser AS Operator
                , UC.TakenByDept AS [Dept.]
                , dbo.SkillIDsToName(UC.SkillIDList) AS Skill
                , UC.ChattedForSecs AS [Time]
                , UC.Rating AS Rate
                , UC.MessageEmail AS Email
                , UC.MessageText AS [Message]
                , UC.VisitorIP AS DNS
                , UC.VisitorVisitNumber AS Visit
                , VD.City
                , VD.Country
            FROM dbo.UserChats AS UC
            INNER JOIN dbo.VisitDetail AS VD
                ON UC.VisitID = VD.VisitID
            WHERE UC.SiteKey = @p0';

        IF NULLIF(@invited, '') IS NOT NULL                 SET @SQL += N' AND UC.Invited = @p1';
        IF NULLIF(@starttime, '01/01/1900') IS NOT NULL     SET @SQL += N' AND UC.StartTime = @p2';
        IF NULLIF(@finishtime, '01/01/1900') IS NOT NULL    SET @SQL += N' AND UC.FinishTime = @p3';
        IF NULLIF(@visitor, '') IS NOT NULL                 SET @SQL += N' AND UC.VisitorName = @p4';
        IF NULLIF(@wait, '') IS NOT NULL                    SET @SQL += N' AND UC.WaitedForSecs = @p5';
        IF NULLIF(@operators, '') IS NOT NULL               SET @SQL += N' AND UC.TakenByUser = @p6';
        IF NULLIF(@department, '') IS NOT NULL              SET @SQL += N' AND UC.TakenByDept = @p7';
        IF NULLIF(@skills, '') IS NOT NULL                  SET @SQL += N' AND dbo.SkillIDsToName = @p8';
        IF NULLIF(@chattime, '') IS NOT NULL                SET @SQL += N' AND UC.ChattedForSecs = @p9';
        IF NULLIF(@rating, '') IS NOT NULL                  SET @SQL += N' AND UC.Rating = @p10';
        IF NULLIF(@email, '') IS NOT NULL                   SET @SQL += N' AND UC.MessageEmail = @p11';
        IF NULLIF(@message, '') IS NOT NULL                 SET @SQL += N' AND UC.MessageText = @p12';
        IF NULLIF(@dns, '') IS NOT NULL                     SET @SQL += N' AND UC.VisitorIP = @p13';
        IF NULLIF(@visit, '') IS NOT NULL                   SET @SQL += N' AND UC.VisitorVisitNumber @p14';
        IF NULLIF(@city, '') IS NOT NULL                    SET @SQL += N' AND VD.City = @p15';
        IF NULLIF(@country, '') IS NOT NULL                 SET @SQL += N' AND VD.Country = @p16';

        SET @SQLParams = N'
              @p0 INT
            , @p1 INT
            , @p2 VARCHAR(15)
            , @p3 VARCHAR(15)
            , @p4 VARCHAR(50)
            , @p5 INT
            , @p6 VARCHAR(MAX)
            , @p7 VARCHAR(MAX)
            , @p8 VARCHAR(MAX)
            , @p9 INT
            , @p10 INT
            , @p11 VARCHAR(MAX)
            , @p12 VARCHAR(MAX)
            , @p13 VARCHAR(MAX)
            , @p14 VARCHAR(MAX)
            , @p15 VARCHAR(MAX)
            , @p16 VARCHAR(MAX)';

        EXECUTE sp_executesql @SQL
            , @SQLParams
            , @p0 = @SiteKey
            , @p1 = @invited
            , @p2 = @starttime
            , @p3 = @finishtime
            , @p4 = @visitor
            , @p5 = @wait
            , @p6 = @operators
            , @p7 = @department
            , @p8 = @skills
            , @p9 = @chattime
            , @p10 = @rating
            , @p11 = @email
            , @p12 = @message
            , @p13 = @dns
            , @p14 = @visit
            , @p15 = @city
            , @p16 = @country;
    END TRY
    BEGIN CATCH
        SELECT ERROR_MESSAGE();
    END CATCH
END

答案 2 :(得分:0)

Gordon Linoff的回答是正确的,但您需要设置每个参数以检查默认值,例如@starttime ='',因为默认情况下它未设置为空。

另请考虑添加OPTION (RECOMPILE) 如果您可以使用它(SQL Server 2005及以上版本)

,则应该提高性能

答案 3 :(得分:0)

您可以通过以下方式使用案例陈述:

    ALTER PROCEDURE [dbo].[GetChatListFilter] 
        @SiteKey int,
        @invited int = NULL,
        @starttime varchar(15),
        @finishtime varchar(15) = NULL,
        @visitor varchar(50) = NULL,
        @wait int = NULL,
        @operators varchar(max) = NULL,
        @department varchar(max) = NULL,
        @skills varchar(max) = NULL,
        @chattime int = NULL,
        @rating int = NULL,
        @email varchar(max) = NULL,
        @message varchar(max) = NULL,
        @dns varchar(max) = NULL,
        @visit varchar(max) = NULL,
        @city varchar(max) = NULL,
        @country varchar(max) = NULL
    AS

        SELECT        
            UserChats.Invited, 
            UserChats.StartTime AS Start, 
            UserChats.FinishTime AS Finish, 
            UserChats.VisitorName As Visitor, 
            UserChats.WaitedForSecs AS Wait, 
            UserChats.TakenByUser AS Operator, 
            UserChats.TakenByDept AS [Dept.], 
            dbo.SkillIDsToName(UserChats.SkillIDList) AS Skill,
            UserChats.ChattedForSecs AS [Time], 
            UserChats.Rating AS Rate, 
            UserChats.MessageEmail AS Email, 
            UserChats.MessageText AS [Message], 
            UserChats.VisitorIP AS DNS, 
            UserChats.VisitorVisitNumber AS Visit, 
            VisitDetail.City, 
            VisitDetail.Country
        FROM             
            UserChats 
        INNER JOIN
            VisitDetail ON  UserChats.VisitID =  VisitDetail.VisitID
        WHERE 
            UserChats.SiteKey = @SiteKey AND
            UserChats.Invited = CASE WHEN @invited IS NOT NULL THEN @invited ELSE UserChats.Invited END
    AND     UserChats.StartTime = CASE WHEN @starttime <> '01/01/1900' THEN @starttime ELSE UserChats.StartTime END
    AND     UserChats.FinishTime = CASE WHEN @finishtime <> '01/01/1900' THEN @finishtime ELSE UserChats.FinishTime END
    AND     UserChats.VisitorName = CASE WHEN @visitor IS NOT NULL THEN @visitor ELSE UserChats.VisitorName END
    AND     UserChats.WaitedForSecs = CASE WHEN @wait IS NOT NULL THEN @wait ELSE UserChats.WaitedForSecs END
    AND     UserChats.TakenByUser = CASE WHEN @operators IS NOT NULL THEN @operators ELSE UserChats.TakenByUser END
    AND     UserChats.TakenByDept = CASE WHEN @department IS NOT NULL THEN @department ELSE UserChats.TakenByDept END
    AND     dbo.SkillIDsToName(UserChats.SkillIDList) = CASE WHEN @skills IS NOT NULL THEN @skills ELSE dbo.SkillIDsToName(UserChats.SkillIDList) END
    AND     UserChats.ChattedForSecs = CASE WHEN @chattime IS NOT NULL THEN @chattime ELSE UserChats.ChattedForSecs END
    AND     UserChats.Rating = CASE WHEN @rating IS NOT NULL THEN @rating ELSE UserChats.Rating END
    AND     UserChats.MessageEmail = CASE WHEN @email IS NOT NULL THEN @email ELSE UserChats.MessageEmail END
    AND     UserChats.MessageText = CASE WHEN @message IS NOT NULL THEN @message ELSE UserChats.MessageText END
    AND     UserChats.VisitorIP = CASE WHEN @dns IS NOT NULL THEN @dns ELSE UserChats.VisitorIP END
    AND     UserChats.VisitorVisitNumber = CASE WHEN @visit IS NOT NULL THEN @visit ELSE UserChats.VisitorVisitNumber END
    AND     VisitDetail.City = CASE WHEN @city IS NOT NULL THEN @city ELSE UserChats.City END
    AND     VisitDetail.Country = CASE WHEN @country IS NOT NULL THEN @country ELSE UserChats.Country END