2012-09-05 14 views
7

Mam tabelę zawierającą kategorie, daty i stawki. Każda kategoria może mieć różne stawki dla różnych dat, jedna kategoria może mieć tylko jedną stawkę w danej dacie.Jak grupować ciągłe zakresy przy użyciu MySQL

Id  CatId Date  Rate 
------ ------ ------------ --------- 
000001  12 2009-07-07  1 
000002  12 2009-07-08  1 
000003  12 2009-07-09  1 
000004  12 2009-07-10  2 
000005  12 2009-07-15  1 
000006  12 2009-07-16  1 
000007  13 2009-07-08  1 
000008  13 2009-07-09  1 
000009  14 2009-07-07  2 
000010  14 2009-07-08  1 
000010  14 2009-07-10  1 

Indeks Unique (CatID, Data Rate) chciałbym dla każdej kategorii do grupy wszystkich ciągłych zakresów dat i zachować tylko początek i koniec zakresu. Na poprzednim przykładzie, to mamy:

CatId Begin   End   Rate 
------ ------------ ------------ --------- 
12  2009-07-07 2009-07-09  1 
12  2009-07-10 2009-07-10  2 
12  2009-07-15 2009-07-16  1 
13  2009-07-08 2009-07-09  1 
14  2009-07-07 2009-07-07  2 
14  2009-07-08 2009-07-08  1 
14  2009-07-10 2009-07-10  1 

znalazłem podobnego rozwiązania w the forum które nie dokładnie daje wynik

WITH q AS 
     (
     SELECT *, 
       ROW_NUMBER() OVER (PARTITION BY CatId, Rate ORDER BY [Date]) AS rnd, 
       ROW_NUMBER() OVER (PARTITION BY CatId ORDER BY [Date]) AS rn 
     FROM my_table 
     ) 
SELECT CatId AS catidd, MIN([Date]) as beginn, MAX([Date])as endd, Rate 
FROM q 
GROUP BY CatId, rnd - rn, Rate 

zobaczyć SQL FIDDLE Jak mogę zrobić to samo w mysql ? Proszę o pomoc!

+0

Dlaczego swój przykładowy pokaz dla '(CatID, Rate) = (14,1) "wynikowy zakres od" 2009-07-08 "do" 2009-07-10 ", gdy nie ma" 2009-07-09 "w tabeli podstawowej? c.f. '(CatId, Rate) = (12,1)', który daje dwa wynikające zakresy ze względu na swoją nieciągłość. – eggyal

+0

Dzięki eggyal, teraz jest to poprawione – Fouzi

Odpowiedz

6

MySQL nie obsługuje funkcji analitycznych, ale można naśladować takiego zachowania z user-defined variables:

SELECT CatID, Begin, MAX(Date) AS End, Rate 
FROM (
    SELECT my_table.*, 
      @f:=CONVERT(
      IF(@c<=>CatId AND @r<=>Rate AND DATEDIFF(Date, @d)=1, @f, Date), DATE 
      ) AS Begin, 
      @c:=CatId, @d:=Date, @r:=Rate 
    FROM  my_table JOIN (SELECT @c:=NULL) AS init 
    ORDER BY CatId, Rate, Date 
) AS t 
GROUP BY CatID, Begin, Rate 

Zobacz na sqlfiddle.

+0

Wydaje się działać zgodnie z oczekiwaniami! Wielkie dzięki! – Fouzi

+1

Co to jest "<=>"? –

+1

@valabel: To MySQL [NULL-safe równy operatorowi] (http://dev.mysql.com/doc/en/comparison-operators.html#operator_equal-to). – eggyal

3
SELECT catid,min(ddate),max(ddate),rate 
FROM (
    SELECT 
     Catid, 
     Ddate, 
     rate, 
     @rn := CASE WHEN (@prev <> rate 
      or DATEDIFF(ddate, @prev_date)>1) THEN @rn+1 ELSE @rn END AS rn, 
     @prev := rate, 
     @prev_id := catid , 
     @prev_date :=ddate 
    FROM (
     SELECT CatID,Ddate,rate 
     FROM rankdate 
     ORDER BY CatID, Ddate) AS a , 
     (SELECT @prev := -1, @rn := 0, @prev_id:=0 ,@prev_date:=-1) AS vars  

) T1 group by catid,rn 

Uwaga: Linia (SELECT @prev: = 1, @rn: = 0, @prev_id: = 0 @ prev_date: = - 1) vars nie jest konieczne Mysql obszarze roboczym ale jest w funkcji PHP mysql_query.

SQL FIDDLE HERE

+0

, jeśli usuniemy rekord z identyfikatorem = '000004' twoja prośba o zwrot (rozpocznij: 2009-07-07, koniec: 2009-07-16, stawka: 1) To nie jest poprawne, ponieważ istnieje luka, powinna wrócić (zacząć: 2009-07-07, koniec: 2009-07-09, stawka: 1) i (zacząć: 2009-07-15, koniec: 2009-07-16, stawka: 1). [SQL FIDDLE TUTAJ] (http://sqlfiddle.com/#!2/513b2/1) – Fouzi

+0

@BoussahelBachir, zredagowałem odpowiedź. w takim przypadku należy podać warunek datownika, aby uwzględnić sytuację, o której wspomniałeś. – sel

+0

Nie wydajesz się testować '@ prev_id' w dowolnym miejscu ... co się stanie, jeśli dwie sąsiadujące daty z tym samym' Rate' mają inne 'CatId'? – eggyal

0

Wiem, że jestem spóźniony, wciąż zamieszczam rozwiązanie, które zadziałało dla mnie. miał ten sam problem, oto jak mam go

Znaleziono dobre rozwiązanie przy użyciu zmiennych

SELECT MIN(id) AS id, MIN(date) AS date, MIN(state) AS state, COUNT(*) cnt 
FROM (
    SELECT @r := @r + (@state != state OR @state IS NULL) AS gn, 
      @state := state AS sn, 
      s.id, s.date, s.state 
    FROM (
      SELECT @r := 0, 
        @state := NULL 
      ) vars, 
      t_range s 
    ORDER BY 
      date, state 
    ) q 
GROUP BY gn 

Więcej szczegółów na stronie: https://explainextended.com/2009/07/24/mysql-grouping-continuous-ranges/

Powiązane problemy