我应该使用什么来代替 SQL "OR"语句



我有一个可以通过 6 个下拉框过滤的网格视图,所以在编写 sql 时,最简单的方法是如果下拉列表有选择或 null 等,则使用"or"语句。

但是,我在这里和其他网站上读到使用 sql 或语句是一个坏主意,任何人都可以提供我可以使用的任何其他建议,而不是我编写每个 ddl 选择是否为空的变体?下面是第一个查询的示例,每个 ddl 返回一个值

@ruleID int = null, 
@engagementStatusID int = null,
@areaOfWorkID int = null,
@registered bit = null,
@staffGroupID int = null,
@assignmentTypeID int = NULL
AS
SET NOCOUNT ON
IF (@ruleID IS NOT NULL and @engagementStatusID IS NOT NULL and @areaOfWorkID IS NOT NULL and 
        @registered IS NOT NULL and @staffGroupID IS NOT NULL and @assignmentTypeID IS NOT NULL)
BEGIN
    SELECT            r.dbRuleId AS RuleID,r.dbEngagementStatusId AS EngagementStatusID, 
                  r.dbIsAllStaffGroups AS AllStaffGroups,r.dbIsAllAssignments AS AllAssignments, 
                  r.dbIsAllRegistered AS AllRegistered,r.dbIsAllUnregistered AS AllUnregistered, 
                  r.dbSoftDelete AS Softdelete, es.dbName AS EngagementName, 
                  sgc.dbName AS StaffGroupName, aow.dbName AS AreaOfWorkName, 
                  at.dbName AS AssignmentName, at.dbIsRegistered AS Registered,sgc.dbStaffGroupCodeId AS StaffGroupCodeID, 
                  at.dbAssignmentTypeId AS AssignmentID, aow.dbAreaOfWorkId AS AreaOfWorkID
FROM              dbo.tbRule r INNER JOIN
                  dbo.EngagementStatus es ON r.dbEngagementStatusId = es.dbEngagementStatusId INNER JOIN
                  dbo.RuleStaffGroup rsg ON r.dbRuleId = rsg.dbRuleId INNER JOIN
                  dbo.StaffGroupCode sgc ON rsg.dbStaffGroupId = sgc.dbStaffGroupCodeId INNER JOIN
                  dbo.RuleAssignmentCode rac ON r.dbRuleId = rac.dbRuleId INNER JOIN
                  dbo.AssignmentCode ac ON 
                  rac.dbAssignmentCodeId = ac.dbAssignmentCodeId INNER JOIN
                  dbo.AssignmentType at ON ac.dbAssignmentId = at.dbAssignmentTypeId INNER JOIN
                  dbo.AreaOfWork aow ON ac.dbAreaOfWorkId = aow.dbAreaOfWorkId
    WHERE   ((r.dbRuleId = @ruleID) and (r.dbEngagementStatusId = @engagementStatusID) and (aow.dbAreaOfWorkId = @areaOfWorkID) and
                (at.dbIsRegistered = @registered) and (sgc.dbStaffGroupCodeId = @staffGroupID) and (at.dbAssignmentTypeId = @assignmentTypeID))

对此的任何建议都会很棒

更新我觉得我应该澄清一下我的代码,当我说 null 时,这是我分配给下拉列表的"all"选择的值,所以例如 imn 在大多数情况下,我会做这样的事情来获取需要传递给数据库的值

int? Type = (this.ddlType.SelectedValue.ToString() == "All") ? (int?)null : Convert.ToInt32(this.ddlType.SelectedValue.ToString());

因此,如果用户选择了所有 Db 接收"null",那么我可以在"如果@blah不为空"等上使用。我意识到这可能不是最好的方法

您似乎正在执行此存储过程,然后在数据库级别验证用户输入。 如果下拉列表值null,则不应调用数据库存储过程,您可以在客户端(或服务器端)处理此问题。

客户端 (JavaScript) 对用户体验更好,如果用户选择了所有适当的下拉列表值,则可以调用存储过程。

当您执行以下操作时,问题就出现了:

WHERE   (r.dbRuleId = @ruleID or @ruleID is null)
and     (r.dbEngagementStatusId = @engagementStatusID
                 or @engagementStatusID is null)
-- ... lots more

这很快就会降级为非常糟糕的查询计划。然后,诀窍是让 TSQL 与您的确切查询参数集匹配。

解决此问题的

难以维护的方法是为每种可能性编写DML,并分支到正确的一种 - 但这真的很丑陋,并且混淆了很多工具。

最简单的方法是在调用方适当地构建 TSQL - 但是如果您的系统要求您使用存储过程(现在,其好处充其量是可疑的 - 顺便说一句),那么最简单的选择是动态 SQL。显然,你在这里需要小心 - 你仍然不想连接输入(出于注入和查询计划的原因),但是 - 你可以做这样的事情:

declare @sql nvarchar(4000) = N'...start of query...';
if(@ruleID is not null)
    set @sql = @sql + N' and r.dbRuleId = @ruleID';
if(@engagementStatusID is not null)
    set @sql = @sql + N' and r.dbEngagementStatusId = @engagementStatusID';

然后你需要用 sp_executesql 来执行它,声明参数:

exec 'sp_executesql', @sql,
      N'@ruleID int, @engagementStatusID int',
      @ruleID, @engagementStatusID

我不确定我是否理解您的问题,但是如果您正在考虑避免重复OR运算符,请考虑使用IN('x','y','z') - 列出可能的值。这比 [某物] = 'x' 或 [某物] = 'y' 或 [某物] = 'z' 更容易阅读。

最新更新