间隔间隔 table

interval in intervals table

我有 table 两列,如下所示:

+-----------+------------+
|   FROM    |     TO     |
+-----------+------------+
|2015-03-01 | 2015-03-04 |
|2015-03-05 | 2015-03-09 |
+-----------+------------+

我想编写一个函数,它将接受两个参数 - DateFrom 和 DateTo,并检查这个时间间隔。例如,如果函数将 DateFrom = 2015-03-03DateTo = 2015-03-08 作为参数,它应该 return true,因为从这个间隔开始的每一天都在 table.

但是如果table是这样的:

+-----------+------------+
|   FROM    |     TO     |
+-----------+------------+
|2015-03-01 | 2015-03-04 |
|2015-03-06 | 2015-03-09 |
+-----------+------------+

函数应该 return false,因为 2015-03-05 不在 table 中。对算法有什么想法吗?感谢您的帮助。

如果要对table执行此操作以遍历table中的每一行。我建议您使用 usp(用户存储过程)

下面是执行相同操作的示例代码:

CREATE PROC usp_CheckInterval
(
@DateFrom Date ,
@DateTo Date ,
@ReturnStatus bit OUTPUT
)
AS
BEGIN TRY

SET NOCOUNT ON;

IF(EXISTS (SELECT TOP 1 * FROM [YourTable] WHERE [StartDate] < @DateFrom AND [EndDate] > @DateTo))
BEGIN
SET @ReturnStatus = 0
END

ELSE
BEGIN
SET @ReturnStatus = 1
END

END TRY
BEGIN CATCH
    --Catch Here
END CATCH;

示例执行:

DECLARE @RC int
DECLARE @DateFrom date = GETDATE()
DECLARE @DateTo date =  GETDATE()
DECLARE @ReturnStatus bit

-- TODO: Set parameter values here.

EXECUTE @RC = [dbo].[usp_CheckInterval] 
   @DateFrom
  ,@DateTo
  ,@ReturnStatus OUTPUT   

SELECT @ReturnStatus

告诉我结果如何。

如果您还没有日历 table,您可以为此使用记事本 table:

DECLARE @t TABLE
    (
      FromDate DATE ,
      ToDate DATE
    )

INSERT  INTO @t
VALUES  ( '2015-03-01', '2015-03-04' ),
        ( '2015-03-05', '2015-03-09' )

DECLARE @from DATE =  '2015-03-03', @to DATE = '2015-03-08'

;WITH cte1 AS(SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS d
             FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
             CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
             CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)),
      cte2 AS(SELECT DATEADD(dd, d - 1, @from) AS d 
              FROM cte1 
              WHERE DATEADD(dd, d - 1, @from) <= @to)

SELECT  CASE WHEN EXISTS ( SELECT *
                           FROM cte2
                           WHERE NOT EXISTS ( SELECT *
                                              FROM   @t t
                                              WHERE  d BETWEEN t.FromDate AND t.ToDate ) )
             THEN 0
             ELSE 1
        END AS IntervalExists

它将在间隔为 1000 天的时间间隔内工作。如果需要更多,只需添加更多 cross joins(一个 cross join 会将间隔乘以 10)。

我的建议是创建 运行 个数字的函数:

CREATE FUNCTION [dbo].[GetRunningNumbers](@counter INT=10000000, @StartAt INT=0)
RETURNS TABLE
AS 
RETURN
WITH E1(N) AS( -- 10 ^ 1 = 10 rows
    SELECT 1 FROM(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(N)
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b), -- 10 ^ 2 = 100 rows
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b), -- 10 ^ 4 = 10,000 rows
E8(N) AS(SELECT 1 FROM E4 a CROSS JOIN E4 b), -- 10 ^ 8 = 10,000,000 rows
CteTally AS(
    SELECT TOP(ISNULL(@counter,1000000)) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) -1 + ISNULL(@StartAt,0) As Nmbr
    FROM E8
)
SELECT * FROM CteTally;
GO

使用此功能,您可以单行执行此操作:

    DECLARE @tbl TABLE(d1 DATETIME, d2 DATETIME);
    INSERT INTO @tbl VALUES({d'2015-03-01'},{d'2015-03-04'})
                          ,({d'2015-03-05'},{d'2015-03-09'}); 

    DECLARE @CheckStart DATETIME={d'2015-03-03'};
    DECLARE @CheckEnd DATETIME={d'2015-03-08'};

    --Check if all dates between these two checkdates are coverd within your table (be aware of the possible time component of a DATETIME!):
WITH AllCheckDates AS
(
    SELECT DATEADD(DAY,Nmbr,@CheckStart) AS RunningDate
    FROM dbo.GetRunningNumbers(DATEDIFF(DAY,@CheckStart,@CheckEnd)+1,0)
)
SELECT CASE WHEN EXISTS ( SELECT * FROM AllCheckDates WHERE NOT EXISTS ( SELECT * FROM @tbl AS t WHERE RunningDate BETWEEN t.d1 AND t.d2 ) )
            THEN 0 ELSE 1 END AS AllDatesCoveredInIntervall 

现在再次与 2015-04-05 的差距:

    DELETE FROM @tbl;
    INSERT INTO @tbl VALUES({d'2015-03-01'},{d'2015-03-04'})
                          ,({d'2015-03-06'},{d'2015-03-09'}); 

    --Same query, should deliver false now...
WITH AllCheckDates AS
(
    SELECT DATEADD(DAY,Nmbr,@CheckStart) AS RunningDate
    FROM dbo.GetRunningNumbers(DATEDIFF(DAY,@CheckStart,@CheckEnd)+1,0)
)
SELECT CASE WHEN EXISTS ( SELECT * FROM AllCheckDates WHERE NOT EXISTS ( SELECT * FROM @tbl AS t WHERE RunningDate BETWEEN t.d1 AND t.d2 ) )
            THEN 0 ELSE 1 END AS AllDatesCoveredInIntervall 
GO

只是为了清理...

DROP FUNCTION dbo.GetRunningNumbers;
GO