2016-09-07 5 views
1

I Start- und Enddatum aus der folgenden Datensatz muß, um zu bestimmen, um zu bestimmen:Wie Start- und Enddaten aus einer einzigen Spalte

item  date    cost   
12345 01/01/15    2.00 
12345 01/02/15    2.00 
12345 01/03/15    2.00 
12345 01/04/15    2.00 
12345 01/05/15    2.00 
12345 01/06/15    2.00 
12345 01/07/15    1.50 
12345 01/08/15    1.50 
12345 01/09/15    1.50 
12345 01/10/15    1.50 
12345 01/11/15    1.50 
12345 01/12/15    1.50 
12345 01/13/15    1.50 
12345 01/14/15    2.00 
12345 01/15/15    2.00 
12345 01/16/15    2.00 
12345 01/17/15    2.00 
12345 01/18/15    2.00 
12345 01/19/15    2.00 
12345 01/20/15    2.00 
12345 01/26/15    2.00 
12345 01/27/15    2.00 
12345 01/28/15    2.00 
12345 01/29/15    2.00 

Wenn möglich, was ich will, ist die folgende Ausgabe:

item  start    end     cost 
12345 01/01/15    01/06/15    2.00 
12345 01/07/15    01/13/15    1.50 
12345 01/14/15    01/20/15    2.00 
12345 01/26/15    01/29/15    2.00 

Grundsätzlich jederzeit die Änderungen, oder es gibt mehr als eine 4-Tage-Lücke zwischen den Daten zu beliebigen Kosten.

Danke.

+0

Welche Version von SQL haben Sie eigentlich? – ZLK

+0

SQL Server 2008 R2 –

Antwort

0

Hier ist eine Art und Weise Sie es tun könnte:

DECLARE @myTable TABLE (item INT, date DATE, cost DECIMAL(5, 2)); 
INSERT @myTable VALUES (12345, '01/01/15', 2.00), (12345, '01/02/15', 2.00) 
, (12345, '01/03/15', 2.00), (12345, '01/04/15', 2.00), (12345, '01/05/15', 2.00) 
, (12345, '01/06/15', 2.00), (12345, '01/07/15', 1.50), (12345, '01/08/15', 1.50) 
, (12345, '01/09/15', 1.50), (12345, '01/10/15', 1.50), (12345, '01/11/15', 1.50) 
, (12345, '01/12/15', 1.50), (12345, '01/13/15', 1.50), (12345, '01/14/15', 2.00) 
, (12345, '01/15/15', 2.00), (12345, '01/16/15', 2.00), (12345, '01/17/15', 2.00) 
, (12345, '01/18/15', 2.00), (12345, '01/19/15', 2.00), (12345, '01/20/15', 2.00) 
, (12345, '01/26/15', 2.00), (12345, '01/27/15', 2.00), (12345, '01/28/15', 2.00) 
, (12345, '01/29/15', 2.00); 

WITH CTE1 AS (
    SELECT item, date, cost, ROW_NUMBER() OVER (PARTITION BY item ORDER BY date) RN 
    FROM @myTable) 
, CTE2 AS (
    SELECT item, T.date TDate, T.cost Tcost, prevRow.date SDate, MD.maxDate, ROW_NUMBER() OVER (PARTITION BY item ORDER BY T.date) RN2 
    FROM CTE1 T 
    OUTER APPLY (
     SELECT date, cost 
     FROM CTE1 
     WHERE RN = T.RN - 1 
     AND item = T.item 
     ) prevRow 
    OUTER APPLY (
     SELECT MAX(date) FROM CTE1 
     ) MD(maxDate) 
    WHERE CASE WHEN DATEDIFF(DAY, prevRow.date, T.date) <= 4 THEN T.date END IS NULL 
    OR CASE WHEN prevRow.cost = T.cost THEN T.cost END IS NULL) 
SELECT item, TDate startDate, ISNULL(nextRow.SDate, C.maxDate) endDate, TCost cost 
FROM CTE2 C 
OUTER APPLY (
    SELECT SDate 
    FROM CTE2 
    WHERE RN2 = C.RN2 + 1 
    AND item = C.item) nextRow; 

Grunde wollen Sie jede Zeile mit der nächsten vergleichen, ob zu überprüfen, gibt es entweder ein Unterschied in den Kosten oder ein Datum Differenz von mehr als 4 Tage. In neueren Versionen von SQL Server (ab 2012) können Sie dafür LEAD/LAG-Fensterfunktionen verwenden, aber ich denke, in SQL Server 2008 ist es wahrscheinlich der einfachste Weg, jeder Zeile eine Zeilennummer zuzuweisen, und die Verwendung gilt.

CTE1 weist die Zeilennummern zu. CTE2 verwendet gilt für den Vergleich mit der nächsten Zeile. Die Select-Anweisung am Ende zieht das Start- und Enddatum jeder Periode heraus, wenn sich entweder die Kosten ändern oder wenn ein Datum länger als 4 Tage ist.

+0

Vielen Dank. Ich werde es versuchen. –

0

Hier ist eine Variante, die @ZLK bereitgestellt hat, aber Joins nur ohne Anwenden verwendet. Auch hier verwenden wir Zeilennummern. 2. CTE erhält die erste und letzte Zeile und jede Zeile, in der sich eine Änderung befindet. Die letzte Abfrage kümmert sich um die Auswahl des richtigen Enddatums.

--set up data 
declare @table table (item int, tdate date, cost decimal(5,2)); 
insert @table values 
(12345,'01/01/15',2.00), 
(12345,'01/02/15',2.00), 
(12345,'01/03/15',2.00), 
(12345,'01/04/15',2.00), 
(12345,'01/05/15',2.00), 
(12345,'01/06/15',2.00), 
(12345,'01/07/15',1.50), 
(12345,'01/08/15',1.50), 
(12345,'01/09/15',1.50), 
(12345,'01/10/15',1.50), 
(12345,'01/11/15',1.50), 
(12345,'01/12/15',1.50), 
(12345,'01/13/15',1.50), 
(12345,'01/14/15',2.00), 
(12345,'01/15/15',2.00), 
(12345,'01/16/15',2.00), 
(12345,'01/17/15',2.00), 
(12345,'01/18/15',2.00), 
(12345,'01/19/15',2.00), 
(12345,'01/20/15',2.00), 
(12345,'01/26/15',2.00), 
(12345,'01/27/15',2.00), 
(12345,'01/28/15',2.00), 
(12345,'01/29/15',2.00); 
--select * from @table order by tdate; 

--query 
with cte as (
     select *, row_number() over (partition by item order by tdate) row_num 
     from @table 
), 
cte2 as (
     select c.item, c.tdate, c_pre.tdate pre_date, c_post.tdate post_date, c.cost, row_number() over (partition by c.item order by c.tdate) row_num 
     from cte c 
     left join cte c_pre 
       on c_pre.row_num = c.row_num - 1 
     left join cte c_post 
       on c_post.row_num = c.row_num + 1 
     where c_pre.row_num is null --first row 
     or  c_post.row_num is null --last row 
     or  c_pre.cost != c.cost --cost difference 
     or  datediff(d, c_pre.tdate, c.tdate) > 4 --more than 4 days 
) 
select c.item, c.tdate start, 
     case when c_post.post_date is null then c_post.tdate else c_post.pre_date end [end], --adjustment for last row 
     c.cost 
from cte2 c 
left join cte2 c_post 
     on c_post.row_num = c.row_num + 1 
where c.post_date is not null 
order by c.tdate; 
+0

Danke! Ich werde diese Lösung auch versuchen. ZLK hat mit nur ein paar Feinabstimmungen für meine Bedürfnisse gearbeitet, danke auch für ihn/sie. –

+0

Wenn eine der Antworten hilfreich ist, stellen Sie sicher, dass Sie sie aktualisieren. Wenn Sie das eine verwenden, können Sie es für zukünftige Benutzer als akzeptiert markieren. – SMM

+0

wirkten beide für mich mit vernachlässigbarem Leistungsunterschied. Danke an beide :) –

Verwandte Themen