【问题标题】:SQL Server - Query With Multiple Date Ranges in subquerySQL Server - 在子查询中使用多个日期范围进行查询
【发布时间】:2021-12-30 11:43:54
【问题描述】:

我在另一个需要使用多个日期范围的查询中使用了条件聚合。在这种情况下,子查询中需要日期范围。

我想知道是否可以在一个查询中获得所需的结果(不使用 UNION)。

我需要检查给定记录是否存在于具有日期范围的子查询中。由于我需要使用 EXISTS 而不是 join - 我遇到了这个问题。

这是一个示例脚本/数据。预期结果表用于演示。

IF OBJECT_ID('tempdb..#Entity') IS NOT NULL DROP TABLE #Entity
IF OBJECT_ID('tempdb..#EntityDate') IS NOT NULL DROP TABLE #EntityDate
IF OBJECT_ID('tempdb..#ExpectedOutput') IS NOT NULL DROP TABLE #ExpectedOutput

> `DECLARE @FortnightStart DATETIME = '2020/08/01', @FortnightEnd DATETIME = '2020/08/14 23:59:59'
DECLARE @QuarterStart DATE = '2020/04/01', @QuarterEnd DATE = '2020/06/30 23:59:59'


> `SELECT 'Fortnight' DateRange, @FortnightStart 'Start', @FortnightEnd 'End'
UNION
SELECT 'Quarter', @QuarterStart, @QuarterEnd 

CREATE TABLE #Entity (
    EntityId INT IDENTITY(1, 1),
    EntityName VARCHAR(50)
)

CREATE TABLE #EntityDate (
    EntityDateId INT IDENTITY(1, 1),
    EntityId INT,
    SubmittedDate DATETIME
)

ALTER TABLE #EntityDate ADD CONSTRAINT FK_EntityDate_Entity FOREIGN KEY (EntityId) REFERENCES Entity(EntityId)


INSERT INTO #Entity (EntityName) 
SELECT 'Alice'
UNION
SELECT 'Bob'
UNION
SELECT 'Cameron'
UNION
SELECT 'Diego'
UNION
SELECT 'Elliot'



SELECT * FROM #Entity


INSERT INTO #EntityDate(EntityId, SubmittedDate)
SELECT 1, '08/01/2020 11:00:00' -- only 1 record is expected in the output for this Entity
UNION
SELECT 1, '08/10/2020 10:00:00' 
UNION
SELECT 1, '04/10/2020 10:00:00'  -- this record should show up for the quarter date range
UNION
SELECT 2, '06/01/2020 11:00:00'  -- 
UNION
SELECT 3, '05/01/2020'  -- only 1 record is expected in the output for this Entity
UNION
SELECT 3, '06/01/2020' 
UNION
SELECT 4, '10/01/2021' -- does not fit in any date range
UNION
SELECT 5, '08/02/2020' 


SELECT *
FROM #EntityDate d
    INNER JOIN #Entity e ON d.EntityId = e.EntityId


SELECT * 
FROM #Entity E
WHERE EXISTS (  SELECT 1 
                FROM #EntityDate d
                WHERE SubmittedDate BETWEEN @FortnightStart AND @FortnightEnd AND e.EntityId = D.EntityId
                )

SELECT * 
FROM #Entity E
WHERE EXISTS (  SELECT 1 
                FROM #EntityDate d
                WHERE SubmittedDate BETWEEN @QuarterStart AND @QuarterEnd AND e.EntityId = D.EntityId
                )

CREATE TABLE #ExpectedOutput
(       
    EntityId INT,   
    DateRange VARCHAR(50)
)

INSERT INTO #ExpectedOutput (EntityId, DateRange)
SELECT 1, 'Fortnight'
UNION 
SELECT 5, 'Fortnight'
UNION 
SELECT 1, 'Quarter'
UNION 
SELECT 2, 'Quarter'
UNION 
SELECT 3, 'Quarter'


SELECT o.*, e.EntityName
FROM #ExpectedOutput o
    INNER JOIN #Entity e ON o.EntityId = e.EntityId
ORDER BY O.DateRange, o.EntityId

【问题讨论】:

    标签: sql sql-server date-range


    【解决方案1】:

    使用您在脚本顶部创建的虚拟日期表,您需要将其加入到Entity,使用EXISTS 作为ON 条件

    DECLARE @FortnightStart DATETIME = '2020/08/01', @FortnightEnd DATETIME = '2020/08/14 23:59:59';
    DECLARE @QuarterStart DATE = '2020/04/01', @QuarterEnd DATE = '2020/06/30 23:59:59';
    
    WITH Dates AS (
        SELECT 'Fortnight' DateRange, @FortnightStart Start, @FortnightEnd [End]
        UNION ALL
        SELECT 'Quarter', @QuarterStart, @QuarterEnd
    )
    SELECT
      e.EntityId,
      d.DateRange
    FROM Dates d
    JOIN #Entity E ON EXISTS (SELECT 1 
        FROM #EntityDate ed
        WHERE ed.SubmittedDate BETWEEN d.Start AND d.[End]
          AND ed.EntityId = e.EntityId
        );
    

    db<>fiddle

    【讨论】:

      【解决方案2】:

      试试这样的

      SELECT * FROM (VALUES(1, '08/01/2020 11:00:00'),
      (1, '08/10/2020 10:00:00'),
      (1, '04/10/2020 10:00:00'),
      (2, '06/01/2020 11:00:00'),  -- 
      (3, '05/01/2020'),
      (3, '06/01/2020'), 
      (4, '10/01/2021'),
      (5, '08/02/2020') 
      ) EntityIDate(EntityId,SubmittedDate)
      

      文档:https://docs.microsoft.com/en-us/u-sql/statements-and-expressions/select/from/select-selecting-from-the-values-table-value-constructor

      【讨论】:

      • 如果存在,每个实体在日期范围(例如两周或季度)内应该只出现一次。
      【解决方案3】:

      你为什么有这样的要求?使用多个 UNION ALL 有什么害处?性能方面没有伤害。

      希望我正确理解了您的要求。

      DECLARE @FortnightStart DATETIME = '2020/08/01', @FortnightEnd DATETIME = '2020/08/14 23:59:59'
      DECLARE @QuarterStart DATE = '2020/04/01', @QuarterEnd DATE = '2020/06/30 23:59:59'
      
      ;WITH CTE
           AS (SELECT 1 AS Orderflg, 
                      'Fortnight' DateRange, 
                      @FortnightStart 'StartDate', 
                      @FortnightEnd 'EndDate'
               UNION ALL
               SELECT 2, 
                      'Quarter', 
                      @QuarterStart, 
                      @QuarterEnd),
           CTE1
           AS (SELECT *, 
                      ROW_NUMBER() OVER(PARTITION BY EntityId, 
                                                     Orderflg
                      ORDER BY SubmittedDate) rn
               FROM #EntityDate d
                    CROSS APPLY
               (
                   SELECT TOP 1 DateRange, 
                                Orderflg
                   FROM CTE C
                   WHERE SubmittedDate >= StartDate
                         AND SubmittedDate < EndDate
               ) ca -- e.EntityId = D.EntityId
               )
           SELECT e.EntityId, 
                  DateRange, 
                  EntityName
           FROM CTE1 C1
                INNER JOIN #Entity E ON c1.EntityId = e.EntityId
           WHERE rn = 1
           ORDER BY Orderflg;
      

      【讨论】:

      • 我认为 union all 不会有效率 - 也只是试图检查这是否可行 - 我不确定是在我发布问题时。
      • 当然 UNION 是我没有注意到的愚蠢错误。
      猜你喜欢
      • 1970-01-01
      • 2017-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-11
      • 1970-01-01
      相关资源
      最近更新 更多