2012-06-18 19 views
6

İki sütun, entry_time ve değer içeren bir veritabanı tablomuz var. entry_time zaman damgası iken, değer başka bir veri türü olabilir. Kayıtlar nispeten tutarlıdır, kabaca x dakika aralıklarla girilir. Ancak, çoğu zaman x için, bir giriş yapılamaz, böylece verilerde bir 'boşluk' üretilir.MySQL'de zaman dizileri verisindeki boşlukları bulma yöntemi?

Verimlilik açısından, en azından bir zaman diliminde (hem yeni hem de eski) bu boşlukları bir sorgulamayla bulmanın en iyi yolu nedir?

+0

Bir boşluğu nasıl tanımlarsınız? Girişler arasında ne kadar zaman geçebileceğine dair bir sınırınız var mı? –

+0

Y değişkenini belirtin. Ben tam olarak ne @sample olarak karıştı rağmen – TheDog

cevap

15

Başlamak için, tablonuzdaki giriş sayısını saat olarak özetleyelim. Bir şey giriş yaparsanız

SELECT CAST(DATE_FORMAT(entry_time,'%Y-%m-%d %k:00:00') AS DATETIME) hour, 
     COUNT(*) samplecount 
    FROM table 
GROUP BY CAST(DATE_FORMAT(entry_time,'%Y-%m-%d %k:00:00') AS DATETIME) 

Şimdi, her altı dakika (on katı bir saat) tüm samplecount değerleri on olmalıdır. Bu ifade: CAST(DATE_FORMAT(entry_time,'%Y-%m-%d %k:00:00') AS DATETIME) kıllı görünüyor, ancak zaman damgalarınızı sadece dakika ve saniye sıfırlayarak meydana geldiği saate kadar keser.

Bu, verimli bir işlemdir ve başlamanıza yardımcı olur. Eğer entry_time sütununa bir indeks koyabilir ve sorgunuzu, burada gösterildiği gibi dünün örneklerine göre kısıtlayabilirseniz çok etkilidir.

SELECT CAST(DATE_FORMAT(entry_time,'%Y-%m-%d %k:00:00') AS DATETIME) hour, 
     COUNT(*) samplecount 
    FROM table 
WHERE entry_time >= CURRENT_DATE - INTERVAL 1 DAY 
    AND entry_time < CURRENT_DATE 
GROUP BY CAST(DATE_FORMAT(entry_time,'%Y-%m-%d %k:00:00') AS DATETIME) 

Ancak, eksik örneklerle devam eden tüm saatleri tespit etmek pek iyi değildir. Ayrıca örneklemenizde titremeye biraz duyarlı. Yani, saat başı örneğiniz bazen yarım saniye erken (10:59:30) ve bazen yarım saniye geç (11:00:30) ise saatlik özet sayımlarınız kapalı olacaktır. Yani, bu saat özeti şey (veya gün özeti, ya da dakika özeti, vb) kurşun geçirmez değildir.

Her şeyi doğru bir şekilde almak için kendi kendine katılma sorgusuna ihtiyacınız var; biraz daha fazla bir kıl yumağı ve neredeyse verimli değil.

Kendimiz gibi numaralandırılmış örneklerle bunun gibi sanal bir tablo (alt sorgu) oluşturarak başlayalım. (Bu MySQL bir ağrı olduğu;. Diğer bazı pahalı DBMSs daha kolay olursa olsun olun.)

SELECT @sample:[email protected]+1 AS entry_num, c.entry_time, c.value 
    FROM (
     SELECT entry_time, value 
     FROM table 
     ORDER BY entry_time 
    ) C, 
    (SELECT @sample:=0) s 

Bu küçük sanal masa entry_num, ENTRY_TIME, değer verir.

Bir sonraki adım, biz kendimize katıyoruz.

SELECT one.entry_num, one.entry_time, one.value, 
     TIMEDIFF(two.value, one.value) interval 
    FROM (
    /* virtual table */ 
) ONE 
    JOIN (
    /* same virtual table */ 
) TWO ON (TWO.entry_num - 1 = ONE.entry_num) 

Bu çizgiler kadar tabloları JOIN ON yan tümcesinde tarafından yönetilen tek bir giriş telafi iki birbirini sonraki.

Son olarak, bu tablodaki değerleri eşikten daha büyük olan interval ile seçiyoruz ve örneklerin kayıp olanlardan hemen önceki zamanları var.

Tüm kendi kendine katılma sorgusu budur. Sana bir saç topu olduğunu söyledim. Büyük bir masada üretimde bunu yapmak varsa

SELECT one.entry_num, one.entry_time, one.value, 
     TIMEDIFF(two.value, one.value) interval 
    FROM (
    SELECT @sample:[email protected]+1 AS entry_num, c.entry_time, c.value 
     FROM (
      SELECT entry_time, value 
      FROM table 
      ORDER BY entry_time 
    ) C, 
     (SELECT @sample:=0) s 
) ONE 
    JOIN (
    SELECT @sample2:[email protected]+1 AS entry_num, c.entry_time, c.value 
     FROM (
      SELECT entry_time, value 
      FROM table 
      ORDER BY entry_time 
    ) C, 
     (SELECT @sample2:=0) s 
) TWO ON (TWO.entry_num - 1 = ONE.entry_num) 

verilerinizin bir alt kümesi için bunu yapmak isteyebilirsiniz. Örneğin, önceki iki güne ait örnekler için her gün yapabilirsiniz. Bu terbiyeli olarak verimli olacak ve aynı zamanda gece yarısı herhangi bir eksik örneği gözden kaçırmadığınızdan emin olabilirsiniz. Bunu yapmak için küçük numaralandırılmış sanal tablolarınız böyle görünürdü.

+0

, bu çözüm için çok teşekkür ederiz: = numunenin + 1 yapar – TheDog

+0

Bu '@ değişken satır sayısını tutar sample' @. '(SELECT @sample: = 0) 'olarak başlatıldığına ve tablonun her satırı için artırıldığına dikkat edin. Eğer Oracle'a ödemek için onbinlerce paranız varsa, sadece ROWNUM diyebilirsiniz, ama aynı şeyi yapmak için MySQL kesmek budur. Arcane, ha? Adım adım açıklama için –

+0

+1 – kirugan

1

Bunu yapmanın çok etkili bir yolu, imleçleri kullanan bir saklı yordamla gerçekleştirmektir.Bunun diğer cevaplardan daha basit ve verimli olduğunu düşünüyorum.

Bu yordam, bir imleç oluşturur ve denetlediğiniz datetime kayıtları boyunca yineler. Belirttiğinizden daha fazla bir boşluk varsa, boşluk başlayacak ve bir tabloya bitecektir.

CREATE PROCEDURE findgaps() 
    BEGIN  
    DECLARE done INT DEFAULT FALSE; 
    DECLARE a,b DATETIME; 
    DECLARE cur CURSOR FOR SELECT dateTimeCol FROM targetTable 
          ORDER BY dateTimeCol ASC; 
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;  
    OPEN cur;  
    FETCH cur INTO a;  
    read_loop: LOOP 
     SET b = a; 
     FETCH cur INTO a; 
     IF done THEN 
      LEAVE read_loop; 
     END IF;  
     IF DATEDIFF(a,b) > [range you specify] THEN 
      INSERT INTO tmp_table (gap_begin, gap_end) 
      VALUES (a,b); 
     END IF; 
    END LOOP;   
    CLOSE cur;  
    END; 

Bu durumda 'tmp_table' var olduğu varsayılır. Bunu prosedürde bir GEÇİCİ tablo olarak kolayca tanımlayabilirsiniz, ancak bu örnekte bıraktım.