【问题标题】:How to not repeat yourself with SQL where clause如何不重复使用 SQL where 子句
【发布时间】:2019-04-21 19:10:43
【问题描述】:

我有一个存储过程,其中包括:

declare @dueTypeCode nvarchar(3) = 'ALL' --Is actually a parameter that is passed into the stored procedure
declare @today DateTime = GetUtcDate();
declare @tomorrow DateTime = dateadd(day,datediff(day,-1,GETUTCDATE()),0);
declare @fiveDaysFromNow DateTime = dateadd(day,datediff(day,-5,GETUTCDATE()),0);

if(@dueTypeCode = 'ALL')

    Select * from Items

ELSE IF (@dueTypeCode = 'TODAY')

    Select * from Items where DueDate =  @today

ELSE IF (@dueTypeCode = 'NEXT5DAYS')    

    Select *  from Items where  DueDate  >= @today and DueDate <= @fiveDaysFromNow 
END

有没有什么办法可以把它包装成一条SQL语句来避免上面代码的重复?

实际的select语句要复杂得多,重复3次只是为了改变where子句似乎违反了DRY原则。

【问题讨论】:

  • 您是否考虑过使用动态 SQL 或将所有逻辑放在单个 WHERE 中并包含 RECOMPILE 选项?
  • 是和不是。您可以使用布尔逻辑来根据 @dueTypeCode 设置的内容来控制逻辑的哪一部分胜出,但是您会冒着创建低效查询的风险。逻辑的更大复杂性可能会使查询优化器感到困惑。因此,请注意“捕获所有”查询效率低下。
  • @Larnu,不,我没有考虑过动态 Sql。是的,我考虑过单个 where 子句,但不知道该怎么做。
  • 仔细阅读这个dynamic search conditions article
  • @DanGuzman,这正是我想要的,谢谢。

标签: sql sql-server tsql datetime sql-server-2016


【解决方案1】:

你可以这样做:

select i.*
from Items i
where (@dueTypeCode = 'ALL') or
      (@dueTypeCode = 'TODAY' and DueDate = @today) or
      (@dueTypeCode = 'NEXT5DAYS' and DueDate >= @today and DueDate <= @fiveDaysFromNow);

缺点是这可能不如原始查询高效 - 如果索引可以用于该查询(特别是在 DueDate 上)。

【讨论】:

  • 我不明白为什么这样的东西不能使用索引(即效率更低)。添加RECOMPILE 选项会解决这个问题吗?
  • 添加OPTION(RECOMPILE) 提示可能会提供更好的计划,但会以查询编译为代价,如果查询不经常执行,则无需担心。
  • 构建查询,然后查看查询执行计划和/或做一些测试。
  • @GregGum 。 . .这与参数嗅探以及 SQL Server 根据查询的第一次执行选择执行计划这一事实有关。
【解决方案2】:

既然你在使用变量,为什么不直接修改变量:

DECLARE @dueTypeCode NVARCHAR(10) = 'NEXT5DAYS';
DECLARE @date1 DATE = NULL;
DECLARE @date2 DATE = NULL;

IF @dueTypeCode = 'TODAY'
BEGIN
    SET @date1 = GETUTCDATE();
    SET @date2 = GETUTCDATE() + 1;
END
ELSE IF @dueTypeCode = 'NEXT5DAYS'
BEGIN
    SET @date1 = GETUTCDATE();
    SET @date2 = GETUTCDATE() + 6;
END

SELECT * FROM Items
WHERE (@date1 IS NULL OR DueDate >= @date1)
AND   (@date2 IS NULL OR DueDate <  @date2)

【讨论】:

    【解决方案3】:

    我的风格:

       declare @dueTypeCode nvarchar(3) = 'ALL' --Is actually a parameter that is passed 
       into the stored procedure
       declare @today DateTime = GetUtcDate();
       declare @tomorrow DateTime = dateadd(day,datediff(day,-1,GETUTCDATE()),0);
       declare @fiveDaysFromNow DateTime = dateadd(day,datediff(day,-5,GETUTCDATE()),0);
    
       Select * 
       from Items
       where 1= case @dueTypeCode 
            when 'ALL' then 1
            when 'TODAY' then
                case when DueDate =  @today then 1 else 0 end
            when 'NEXT5DAYS' then
                case when DueDate  >= @today and DueDate <= @fiveDaysFromNow  then 1 else 0 end
            else
                0
            end
    

    【讨论】:

      【解决方案4】:

      以 Gordon 发布的内容为基础 - 您可以使用动态 SQL 获得最佳性能。从这个示例数据开始:

      SET NOCOUNT ON;
      USE tempdb;
      GO
      
      IF OBJECT_ID('dbo.items','U') IS NOT NULL DROP TABLE dbo.items;
      GO
      
      SELECT col1    = CAST(NEWID() AS VARCHAR(100)), 
             DueDate = ISNULL(DATEADD(DAY,t.c*2,d.dt),d.dt)
      INTO   dbo.items
      FROM (VALUES(0),(0),(1),(2),(3),(4)) AS t(c)
      CROSS JOIN (VALUES(CAST(GETDATE() AS DATE))) AS d(dt);
      GO
      CREATE CLUSTERED INDEX cl_nu__dbo_items__DueDate ON dbo.items(DueDate);
      GO
      

      解决方案:

      DECLARE @dueTypeCode VARCHAR(100) = 'TODAY' --'NEXT5DAYS --'ALL';
      
      DECLARE @sql NVARCHAR(4000) = 
      N'SELECT i.*
      FROM dbo.Items i'+CHAR(10);
      
      SELECT @sql += 
        CASE @dueTypeCode 
          WHEN 'TODAY'     THEN 'WHERE DueDate = CAST(getdate() AS date);'
          WHEN 'NEXT5DAYS' THEN 'WHERE DueDate >= CAST(getdate() AS date) AND DueDate <= DATEADD(DAY,5,CAST(getdate() AS date))'
          ELSE ''
        END;
      
      EXEC sp_executesql @statement = @sql;
      

      显然需要更新业务逻辑以满足您的需求(例如,请注意我懒惰地使用 ELSE '' 来处理“ALL”以及其他任何内容,这需要更改)。也就是说,这是选择“TODAY”或“NEXT5DAYS”时的执行计划。在这种情况下,索引搜索基本上是您所希望的最好的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-08-30
        • 1970-01-01
        • 2019-06-09
        • 2013-05-05
        • 2016-07-21
        • 1970-01-01
        • 2013-01-06
        • 1970-01-01
        相关资源
        最近更新 更多