合并和重组日期范围(SQL)

时间:2014-08-20 14:10:18

标签: sql tsql date merge sql-server-2008-r2

我有三个表:每种类型的服务一个。每个表都列出了不同日期的特定服务的客户价格:

TABLE: SERVICE_A
 | StartDate    | EndDate      | Price  |
 ----------------------------------------
 | 01/01/2013   | 16/04/2013   | 30     |
 | 17/04/2013   | 20/09/2013   | 33     |
 | 21/09/2013   | 31/12/2013   | 34     |


TABLE: SERVICE_B
 | StartDate    | EndDate      | Price  |
 ----------------------------------------
 | 01/01/2013   | 30/06/2013   | 47     |
 | 01/07/2013   | 31/12/2013   | 49     |


TABLE: SERVICE_C
 | StartDate    | EndDate      | Price  |
 ----------------------------------------
 | 01/01/2013   | 03/03/2013   | 96     |
 | 04/03/2013   | 31/12/2013   | 101    |

我正在尝试创建一个合并此数据的表,并重新构建它以显示适当日期范围内所有三种服务的价格:

RESULTS
 | StartDate    | EndDate      | PriceA | PriceB | PriceC |
 ----------------------------------------------------------
 | 01/01/2013   | 03/03/2013   | 30     | 47     | 96     |
 | 04/03/2013   | 16/04/2013   | 30     | 47     | 101    |
 | 17/04/2013   | 30/06/2013   | 33     | 47     | 101    |
 | 01/07/2013   | 20/09/2013   | 33     | 49     | 101    |
 | 21/09/2013   | 31/12/2013   | 34     | 49     | 101    |

非常感谢您对此查询的任何帮助! (我正在使用SQL Server 2008 R2)。

2 个答案:

答案 0 :(得分:0)

这应该这样做。我考虑到在开始日期你有三个价格:

CREATE TABLE #Services (
    StartDate DATETIME
    , EndDate DATETIME
    , PriceA INT
    , PriceB INT
    , PriceC INt
)

DECLARE @startDt DateTime, @endDt DateTime, @Price Int, @Service Char(1)
DECLARE @PriceA Int, @PriceB Int, @PriceC Int, @previousStartDate DateTime, @finalEndDate DateTime
SET @previousStartDate = '20130101'

DECLARE cursorDatePrices CURSOR FOR
SELECT StartDate, EndDate, Price, 'A' as Service
FROM #SA
UNION ALL
SELECT StartDate, EndDate, Price, 'B' as Service
FROM #SB
UNION ALL
SELECT StartDate, EndDate, Price, 'C' as Service
FROM #SC
ORDER BY StartDate

OPEN cursorDatePrices

FETCH NEXT FROM cursorDatePrices
INTO @startDt, @endDt, @Price, @Service

WHILE @@FETCH_STATUS = 0
BEGIN
    --only insert after having the three prices
    IF (@PriceA IS NOT NULL AND @PriceB IS NOT NULL AND @PriceC IS NOT NULL)
    BEGIN
        INSERT INTO #Services VALUES (@previousStartDate, DATEADD(dd, -1, @startDt), @PriceA, @PriceB, @PriceC)
        SET @previousStartDate = @startDt
    END

    IF (@Service = 'A') SET @PriceA = @Price
    ELSE IF (@Service = 'B') SET @PriceB = @Price
    ELSE IF (@Service = 'C') SET @PriceC = @Price

    FETCH NEXT FROM cursorDatePrices
    INTO @startDt, @endDt, @Price, @Service
END

CLOSE cursorDatePrices
DEALLOCATE cursorDatePrices

INSERT INTO #Services VALUES (@previousStartDate, @endDt, @PriceA, @PriceB, @PriceC)
SELECT * from #Services

答案 1 :(得分:0)

这是一个可怕的解决方案,但它没有使用游标:D

首先,我需要将数据放入表变量中,以便进行测试。

DECLARE @ServiceA TABLE (
    StartDate DATE,
    EndDate DATE,
    Price INT);
INSERT INTO @ServiceA VALUES ('2013-01-01', '2013-04-16', 30);
INSERT INTO @ServiceA VALUES ('2013-04-17', '2013-09-20', 33);
INSERT INTO @ServiceA VALUES ('2013-09-21', '2013-12-31', 34);
DECLARE @ServiceB TABLE (
    StartDate DATE,
    EndDate DATE,
    Price INT);
INSERT INTO @ServiceB VALUES ('2013-01-01', '2013-06-30', 47);
INSERT INTO @ServiceB VALUES ('2013-07-01', '2013-12-31', 49);
DECLARE @ServiceC TABLE (
    StartDate DATE,
    EndDate DATE,
    Price INT);
INSERT INTO @ServiceC VALUES ('2013-01-01', '2013-03-03', 96);
INSERT INTO @ServiceC VALUES ('2013-03-04', '2013-12-31', 101);

现在,我可以编写一个可怕的查询,以您想要的方式将数据拉回:

WITH DistinctDates AS (
    SELECT
        StartDate,
        EndDate
    FROM
        @ServiceA
    UNION
    SELECT
        StartDate,
        EndDate
    FROM
        @ServiceB
    UNION
    SELECT
        StartDate,
        EndDate
    FROM
        @ServiceC),
OrderedDates AS (
    SELECT
        ROW_NUMBER() OVER (PARTITION BY StartDate ORDER BY EndDate) AS StartNumber,
        ROW_NUMBER() OVER (PARTITION BY EndDate ORDER BY StartDate DESC) AS EndNumber,
        *
    FROM
        DistinctDates),
FixOverlaps AS (
    SELECT
        CASE WHEN o2.StartNumber IS NULL THEN o1.StartDate ELSE DATEADD(DAY, 1, o2.EndDate) END AS StartDate,
        CASE WHEN o3.EndNumber IS NULL THEN o1.EndDate ELSE DATEADD(DAY, -1, o3.StartDate) END AS EndDate
    FROM
        OrderedDates o1
        LEFT JOIN OrderedDates o2 ON o2.StartDate = o1.StartDate AND o2.StartNumber = o1.StartNumber - 1
        LEFT JOIN OrderedDates o3 ON o3.EndDate = o1.EndDate AND o3.EndNumber = o1.EndNumber - 1),
MergeDates AS (
    SELECT
        StartDate,
        EndDate,
        ROW_NUMBER() OVER (PARTITION BY StartDate ORDER BY EndDate) AS MergeNumber
    FROM
        FixOverlaps)
SELECT 
    md.StartDate,
    md.EndDate,
    a.Price,
    b.Price,
    c.Price
FROM 
    MergeDates md
    LEFT JOIN @ServiceA a ON a.StartDate <= md.EndDate AND a.EndDate >= md.StartDate
    LEFT JOIN @ServiceB b ON b.StartDate <= md.EndDate AND b.EndDate >= md.StartDate
    LEFT JOIN @ServiceC c ON c.StartDate <= md.EndDate AND c.EndDate >= md.StartDate
WHERE 
    md.MergeNumber = 1;

......结果是:

StartDate   EndDate     PriceA  PriceB  PriceC
2013-01-01  2013-03-03  30      47      96
2013-03-04  2013-04-16  30      47      101
2013-04-17  2013-06-30  33      47      101
2013-07-01  2013-09-20  33      49      101
2013-09-21  2013-12-31  34      49      101