Как не повторить себя с SQL, где предложение - PullRequest
0 голосов
/ 19 ноября 2018

У меня есть хранимая процедура, которая включает это:

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, чтобы избежать повторения вышеуказанного кода?

Фактический выборутверждение гораздо сложнее, и повторение его 3 раза просто для изменения условия where кажется нарушением принципа DRY.

Ответы [ 4 ]

0 голосов
/ 20 ноября 2018

Мой стиль:

   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
0 голосов
/ 19 ноября 2018

Поскольку вы используете переменные, почему бы вам просто не изменить переменные:

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)
0 голосов
/ 19 ноября 2018

Основываясь на том, что написал Гордон, вы можете добиться оптимальной производительности с помощью динамического 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 '', которое обрабатывает «ВСЕ», а также все остальное, это нужно будет изменить). Тем не менее, вот план выполнения при выборе «СЕГОДНЯ» или «NEXT5DAYS». Поиск индекса в этой ситуации - в основном лучшее, на что вы можете надеяться.

enter image description here

0 голосов
/ 19 ноября 2018

Вы можете сделать:

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

Недостатком является то, что это, вероятно, не так эффективно, как исходные запросы - если для этого запроса можно использовать индексы (особенно для DueDate).

...