1

日付範囲が重複するレコードを特定するための多くのソリューションや、重複する範囲をマージする他の例を見てきました。

ただし、オーバーラップのみが発生する範囲を示す結果に興味があります。実際、私は3つのProductIDを持っており(そして3つだけが存在することになります)、顧客が3つすべてを持っていたときの各顧客の日付範囲を見つけようとしています。

SET NOCOUNT ON; 

CREATE TABLE #tmp
(
   CustomerID integer
  ,ProductID varchar(12)
  ,Eff_Dt DATE
  ,End_Dt DATE
);

-- Customer 1000: Expecting results to show 2 rows:   1/1 - 1/5   and    1/10 - 1/15
INSERT INTO #tmp VALUES (1000,'PRODUCT_A','01-01-2013' ,'01-31-2013' );
INSERT INTO #tmp VALUES (1000,'PRODUCT_B','01-01-2013' ,'01-05-2013' );
INSERT INTO #tmp VALUES (1000,'PRODUCT_B','01-10-2013' ,'01-15-2013' );
INSERT INTO #tmp VALUES (1000,'PRODUCT_C','01-01-2013' ,'01-31-2013' );

-- Customer 2000: Expecting results to show 1 row:    1/19 - 1/31
INSERT INTO #tmp VALUES (2000,'PRODUCT_A','01-01-2013' ,'01-31-2013' );
INSERT INTO #tmp VALUES (2000,'PRODUCT_B','01-01-2013' ,'01-31-2013' );
INSERT INTO #tmp VALUES (2000,'PRODUCT_C','01-19-2013' ,'01-31-2013' );

-- Customer 3000: Expecting results to show no rows (or nulls)
INSERT INTO #tmp VALUES (3000,'PRODUCT_A','01-01-2013' ,'01-10-2013' );
INSERT INTO #tmp VALUES (3000,'PRODUCT_A','01-16-2013' ,'01-31-2013' );
INSERT INTO #tmp VALUES (3000,'PRODUCT_B','01-01-2013' ,'01-12-2013' );
INSERT INTO #tmp VALUES (3000,'PRODUCT_C','01-15-2013' ,'01-31-2013' );

-- Customer 4000: Expecting results to show 1 row:    1/15 - 1/23
INSERT INTO #tmp VALUES (4000,'PRODUCT_A','01-15-2013' ,'01-31-2013' );
INSERT INTO #tmp VALUES (4000,'PRODUCT_B','01-01-2013' ,'01-31-2013' );
INSERT INTO #tmp VALUES (4000,'PRODUCT_C','01-01-2013' ,'01-23-2013' );

-- Customer 5000: Expecting results to show 0 rows
INSERT INTO #tmp VALUES (5000,'PRODUCT_A','01-17-2013' ,'01-31-2013' );
INSERT INTO #tmp VALUES (5000,'PRODUCT_B','01-01-2013' ,'01-10-2013' );
INSERT INTO #tmp VALUES (5000,'PRODUCT_C','01-07-2013' ,'01-19-2013' );

-- Customer 6000: Expecting results to show 3 rows:    1/11 - 1/12   1/17 - 1/22    1/26 - 1/27
INSERT INTO #tmp VALUES (6000,'PRODUCT_A','01-01-2013' ,'01-04-2013' );
INSERT INTO #tmp VALUES (6000,'PRODUCT_A','01-09-2013' ,'01-12-2013' );
INSERT INTO #tmp VALUES (6000,'PRODUCT_A','01-17-2013' ,'01-22-2013' );
INSERT INTO #tmp VALUES (6000,'PRODUCT_A','01-26-2013' ,'01-31-2013' );
INSERT INTO #tmp VALUES (6000,'PRODUCT_B','01-04-2013' ,'01-28-2013' );
INSERT INTO #tmp VALUES (6000,'PRODUCT_C','01-11-2013' ,'01-27-2013' );

SET NOCOUNT OFF;
/* ======   EXPECTED RESULTS  =======================

CustomerID    EFF_DT        END_DT
1000          1/1/2013      1/5/2013
1000          1/10/2013     1/15/2013
2000          1/19/2013     1/31/2013
4000          1/15/2013     1/23/2013
6000          1/11/2013     1/12/2013
6000          1/17/2013     1/22/2013
6000          1/26/2013     1/27/2013

===================================================*/
4

3 に答える 3

4

答えは次のとおりです。

select t.customerid, t.eff_dt, count(distinct t2.productId),
       MIN(t2.end_dt) as end_dt
from #tmp t join
     #tmp t2
     on t.CustomerID = t2.CustomerID and
        t.Eff_Dt between t2.Eff_Dt and t2.End_Dt
group by t.CustomerID, t.eff_dt
having count(distinct t2.productId) = 3

これは、自己結合を使用して、それぞれの異なる製品の数をカウントしていますeff_dt。あなたは3つの異なる製品が欲しいので、それがhaving条項が行っていることです。

それらの1つが終了するまで、3つの異なる製品があります。end_dtこれは、発効日の後の最初の日付になります。これは、によって計算されますmin(end_dt)

于 2013-03-26T15:21:34.700 に答える
1

試す:

select ab.CustomerID, 
       case when ab_Eff_Dt > c.Eff_Dt then ab_Eff_Dt else c.Eff_Dt end abc_Eff_Dt,
       case when ab_End_Dt < c.End_Dt then ab_End_Dt else c.End_Dt end abc_End_Dt
from
(select a.CustomerID, 
        case when a.Eff_Dt > b.Eff_Dt then a.Eff_Dt else b.Eff_Dt end ab_Eff_Dt,
        case when a.End_Dt < b.End_Dt then a.End_Dt else b.End_Dt end ab_End_Dt
 from #tmp a
 join #tmp b 
   on a.CustomerID = b.CustomerID and a.Eff_Dt < b.End_Dt and b.Eff_Dt < a.End_Dt
 where a.ProductID = 'PRODUCT_A' and b.ProductID = 'PRODUCT_B') ab
join #tmp c 
  on ab.CustomerID = c.CustomerID and ab_Eff_Dt < c.End_Dt and c.Eff_Dt < ab_End_Dt
where c.ProductID = 'PRODUCT_C' 

(SQLFiddleはこちら

于 2013-03-26T15:28:34.333 に答える
0
CREATE TABLE #TMP
(
    CustomerID INT,
    ProductID VARCHAR(12),
    Eff_Dt DATE,
    End_Dt DATE
);
--
INSERT INTO #TMP
VALUES
-- Customer 1000: Expecting results to show 2 rows: "1/1 - 1/5"; "1/10 - 1/15"
(1000, 'PRODUCT_A', N'2013-01-01T00:00:00', N'2013-01-31T00:00:00'),
(1000, 'PRODUCT_B', N'2013-01-01T00:00:00', N'2013-01-05T00:00:00'),
(1000, 'PRODUCT_B', N'2013-01-10T00:00:00', N'2013-01-15T00:00:00'),
(1000, 'PRODUCT_C', N'2013-01-01T00:00:00', N'2013-01-31T00:00:00'),
-- Customer 2000: Expecting results to show 1 row: "1/19 - 1/31"
(2000, 'PRODUCT_A', N'2013-01-01T00:00:00', N'2013-01-31T00:00:00'),
(2000, 'PRODUCT_B', N'2013-01-01T00:00:00', N'2013-01-31T00:00:00'),
(2000, 'PRODUCT_C', N'2013-01-19T00:00:00', N'2013-01-31T00:00:00'),
-- Customer 3000: Expecting results to show NO rows (or nulls)
(3000, 'PRODUCT_A', N'2013-01-01T00:00:00', N'2013-01-10T00:00:00'),
(3000, 'PRODUCT_A', N'2013-01-16T00:00:00', N'2013-01-31T00:00:00'),
(3000, 'PRODUCT_B', N'2013-01-01T00:00:00', N'2013-01-12T00:00:00'),
(3000, 'PRODUCT_C', N'2013-01-15T00:00:00', N'2013-01-31T00:00:00'),
-- Customer 4000: Expecting results to show 1 row: "1/15 - 1/23"
(4000, 'PRODUCT_A', N'2013-01-15T00:00:00', N'2013-01-31T00:00:00'),
(4000, 'PRODUCT_B', N'2013-01-01T00:00:00', N'2013-01-31T00:00:00'),
(4000, 'PRODUCT_C', N'2013-01-01T00:00:00', N'2013-01-23T00:00:00'),
-- Customer 5000: Expecting results to show 0 rows
(5000, 'PRODUCT_A', N'2013-01-17T00:00:00', N'2013-01-31T00:00:00'),
(5000, 'PRODUCT_B', N'2013-01-01T00:00:00', N'2013-01-10T00:00:00'),
(5000, 'PRODUCT_C', N'2013-01-07T00:00:00', N'2013-01-19T00:00:00'),
-- Customer 6000: Expecting results to show 3 rows: "1/11 - 1/12"; "1/17 - 1/22"; "1/26 - 1/27"
(6000, 'PRODUCT_A', N'2013-01-01T00:00:00', N'2013-01-04T00:00:00'),
(6000, 'PRODUCT_A', N'2013-01-09T00:00:00', N'2013-01-12T00:00:00'),
(6000, 'PRODUCT_A', N'2013-01-17T00:00:00', N'2013-01-22T00:00:00'),
(6000, 'PRODUCT_A', N'2013-01-26T00:00:00', N'2013-01-31T00:00:00'),
(6000, 'PRODUCT_B', N'2013-01-04T00:00:00', N'2013-01-28T00:00:00'),
(6000, 'PRODUCT_C', N'2013-01-11T00:00:00', N'2013-01-27T00:00:00');
--
--
;
WITH MIN_MAX
AS (SELECT MIN(Eff_Dt) S,
           MAX(End_Dt) E
    FROM #TMP),
     ALL_DATES
AS (SELECT MIN_MAX.S DT
    FROM MIN_MAX
    UNION ALL
    SELECT DATEADD(DAY, 1, ALL_DATES.DT)
    FROM ALL_DATES
    WHERE ALL_DATES.DT < (SELECT MIN_MAX.E FROM MIN_MAX)),
     GROUPED
AS (SELECT Q.CustomerID,
           Q.ProductID,
           Q.DT,
           CASE WHEN MAX(TX.CustomerID) IS NULL THEN 0 ELSE 1 END YES
    FROM
    (
        SELECT *FROM ALL_DATES
            CROSS JOIN
            (SELECT DISTINCT CustomerID, ProductID FROM #TMP) AS AQ
    ) AS Q
        LEFT JOIN #TMP AS TX
            ON TX.CustomerID = Q.CustomerID
               AND TX.ProductID = Q.ProductID
               AND Q.DT
               BETWEEN TX.Eff_Dt AND TX.End_Dt
    GROUP BY Q.CustomerID,
             Q.ProductID,
             Q.DT),
     BuildFlags
AS (SELECT G.CustomerID,
           G.DT,
           ROW_NUMBER() OVER (PARTITION BY G.CustomerID ORDER BY G.DT) RN,
           CASE
               WHEN WQ.tot =
               (
                   SELECT COUNT(DISTINCT g2.ProductID)
                   FROM GROUPED AS g2
                   WHERE g2.CustomerID = G.CustomerID
                         AND g2.DT = G.DT
                         AND g2.YES = 1
               ) THEN 1
               ELSE 0
           END FLAG
    FROM GROUPED AS G
        CROSS APPLY
    (
        SELECT COUNT(DISTINCT E9.ProductID) tot
        FROM #TMP AS E9
        WHERE E9.CustomerID = G.CustomerID
    ) AS WQ ),
     AddRanks
AS (SELECT *,
           BuildFlags.RN - ROW_NUMBER() OVER (PARTITION BY BuildFlags.CustomerID,
                                                           BuildFlags.FLAG
                                              ORDER BY BuildFlags.DT
                                             ) groupRank
    FROM BuildFlags)
SELECT AddRanks.CustomerID,
       MIN(AddRanks.DT) AS StartDate,
       MAX(AddRanks.DT) AS EndDate
FROM AddRanks
WHERE AddRanks.FLAG = 1
GROUP BY AddRanks.CustomerID,
         AddRanks.groupRank
ORDER BY AddRanks.CustomerID,
         MIN(AddRanks.DT)
OPTION (MAXRECURSION 0);

あなたはDBフィドルで実行を見つけることができます

于 2019-01-21T13:31:50.910 に答える