2011-01-24 13 views
6

Próbuję uzyskać całkowitą liczbę wierszy między określonym amout czasu lub przedziału czasowego. Zasadniczo, powiedzmy poniższej tabeli:mysql wybierz liczbę wierszy między przedziałem czasowym

CREATE TABLE IF NOT EXISTS `downloads` (
`id` int(7) NOT NULL AUTO_INCREMENT, 
`stuff_id` int(7) NOT NULL, 
`user_id` int(7) NOT NULL, 
`dl_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
PRIMARY KEY (`id`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1; 

I ta tabela jest wypełniana za każdym razem ktoś pobiera coś.

Tak naprawdę potrzebuję uzyskać listę użytkowników (user_id), którzy zrobili więcej niż na przykład 100 pobrań w okresach czasu, na przykład 24 godzin. Nie w ciągu ostatnich 24 godzin, ale W tym dokładnym okresie czasu, nawet jeśli było to podczas świąt w zeszłym roku =)

Wszelkie pomysły w ogóle?!

+0

Gdyby tak zapytania przyjąć jeden znacznik czasu jako „początek okresu” oblicz „koniec okresu” i listę wszystkich użytkowników, którzy przekroczyli ilości pobrań w tym okresie, lub czy powinien on zawierać listę wszystkich użytkowników, którzy przekroczyli limit pobierania x na 24 godziny? –

+0

@Patrick bez okresu początkowego ani końcowego ... po prostu wymień wszystkich użytkowników, którzy przekroczyli X pobrań w Y (jako okres, który może być inny niż 24 godziny) limit czasowy – eduardev

+0

Myślałem o (możliwym) rozwiązaniu. Jeśli nadal jesteś zainteresowany, opracuję go i opublikuję tutaj, ale będzie to miało wadę: Wyobraź sobie, że robię 100 pobrań w ciągu zaledwie jednej godziny, wtedy będzie wiele okresów, w których przekroczę limit. Np. 100 pobrań dzisiaj od 8 do 9 rano. Wczoraj 9 rano do dziś 9 rano = 100 pobrań. Wczoraj 10 rano do dziś 10 rano = 100 pobrań. Wczoraj 11 rano do dziś 11 rano = 100 pobrań. Ciąg dalszy nastąpi ... –

Odpowiedz

6

OK, zdaję sobie sprawę, że jestem trochę późno, ale chciałem pisać moją odpowiedź w każdym razie :-)

Czego wymagać można zrobić za pomocą podzapytania, ale może to trwać wieki, aby zakończyć na dużą table ...

Myśląc o tym pytaniu doszedłem do dwóch różnych podejść.

Jeden z nich został już rozwiązany w innych odpowiedziach, działa od początku w określonym momencie, patrząc na interwał, który rozpoczyna się w tym czasie, a następnie patrząc na przedział równego czasu trwania, który następuje bezpośrednio. Prowadzi to do jasnych, zrozumiałych wyników i prawdopodobnie jest to wymagane (np. Użytkownik nie może przekroczyć 100 pobrań w danym dniu kalendarzowym). To jednak całkowicie pomijane sytuacje, w których użytkownik robi 99 pobrań w ciągu godziny przed północą i kolejne 99 w pierwszej godzinie nowego dnia.

Jeśli więc wymagany wynik jest bardziej "pierwszą dziesiątą listy downloaderów", to jest to inne podejście. Wyniki mogą nie być tak zrozumiałe na pierwszy rzut oka, ponieważ jedno pobranie może liczyć się w wielu odstępach czasu. Dzieje się tak dlatego, że odstępy będą (i muszą) pokrywać się.

Oto moja konfiguracja.Utworzyłem tabelę z wyciągu i dodał dwa indeksy:

CREATE INDEX downloads_timestamp on downloads (dl_date); 
CREATE INDEX downloads_user_id on downloads (user_id); 

dane mam wstawiony do tabeli:

SELECT * FROM downloads; 
+----+----------+---------+---------------------+ 
| id | stuff_id | user_id | dl_date    | 
+----+----------+---------+---------------------+ 
| 1 |  1 |  1 | 2011-01-24 09:00:00 | 
| 2 |  1 |  1 | 2011-01-24 09:30:00 | 
| 3 |  1 |  1 | 2011-01-24 09:35:00 | 
| 4 |  1 |  1 | 2011-01-24 10:00:00 | 
| 5 |  1 |  1 | 2011-01-24 11:00:00 | 
| 6 |  1 |  1 | 2011-01-24 11:15:00 | 
| 7 |  1 |  1 | 2011-01-25 09:15:00 | 
| 8 |  1 |  1 | 2011-01-25 09:30:00 | 
| 9 |  1 |  1 | 2011-01-25 09:45:00 | 
| 10 |  1 |  2 | 2011-01-24 08:00:00 | 
| 11 |  1 |  2 | 2011-01-24 12:00:00 | 
| 12 |  1 |  2 | 2011-01-24 12:01:00 | 
| 13 |  1 |  2 | 2011-01-24 12:02:00 | 
| 14 |  1 |  2 | 2011-01-24 12:03:00 | 
| 15 |  1 |  2 | 2011-01-24 12:00:00 | 
| 16 |  1 |  2 | 2011-01-24 12:04:00 | 
| 17 |  1 |  2 | 2011-01-24 12:05:00 | 
| 18 |  1 |  2 | 2011-01-24 12:06:00 | 
| 19 |  1 |  2 | 2011-01-24 12:07:00 | 
| 20 |  1 |  2 | 2011-01-24 12:08:00 | 
| 21 |  1 |  2 | 2011-01-24 12:09:00 | 
| 22 |  1 |  2 | 2011-01-24 12:10:00 | 
| 23 |  1 |  2 | 2011-01-25 14:00:00 | 
| 24 |  1 |  2 | 2011-01-25 14:12:00 | 
| 25 |  1 |  2 | 2011-01-25 14:25:00 | 
+----+----------+---------+---------------------+ 
25 rows in set (0.00 sec) 

Jak widać, wszystkie pliki do pobrania albo wystąpił wczoraj lub dzisiaj i zostały wykonane przez dwóch różnych użytkowników.

Teraz mamy następujące zdanie: Istnieje (matematycznie) nieskończona liczba interwałów 24-godzinnych (lub interwałów o dowolnym innym czasie trwania) między "2011-01-24 0:00" a "2011- 01-25 23:59:59 ". Ale jak precyzja serwera wynosi jedną sekundę, to sprowadza się do 86,400 odstępach:

First interval: 2011-01-24 0:00:00 -> 2011-01-25 0:00:00 
Second interval: 2011-01-24 0:00:01 -> 2011-01-25 0:00:01 
Third interval: 2011-01-24 0:00:02 -> 2011-01-25 0:00:02 
    . 
    . 
    . 
86400th interval: 2011-01-24 23:59:59 -> 2011-01-25 23:59:59 

Więc możemy użyć pętli iteracyjne nad wszystkich tych przedziałów i obliczyć liczbę pobrań na użytkownika i na przedziale. Oczywiście nie wszystkie interwały są dla nas tym samym zainteresowaniem, więc możemy pominąć niektóre z nich, używając znaczników czasu w tabeli jako "początek interwału".

Oto, co robi poniższe zapytanie. Wykorzystuje każdy znacznik czasu pobierania w tabeli jako "początek interwału", dodaje czas trwania interwału, a następnie zapytuje o liczbę pobrań na użytkownika w tym przedziale.

SET @duration = '24:00:00'; 
SET @limit = 5; 
SELECT * FROM 
    (SELECT t1.user_id, 
      t1.dl_date startOfPeriod, 
      ADDTIME(t1.dl_date,@duration) endOfPeriod, 
      (SELECT COUNT(1) 
      FROM downloads t2 
      WHERE t1.user_id = t2.user_id 
      AND t1.dl_date <= t2.dl_date 
      AND ADDTIME(t1.dl_date,@duration) >= t2.dl_date) count 
    FROM downloads t1) t3 
WHERE count > @limit; 

Oto wynik:

+---------+---------------------+---------------------+-------+ 
| user_id | startOfPeriod  | endOfPeriod   | count | 
+---------+---------------------+---------------------+-------+ 
|  1 | 2011-01-24 09:00:00 | 2011-01-25 09:00:00 |  6 | 
|  1 | 2011-01-24 09:30:00 | 2011-01-25 09:30:00 |  7 | 
|  1 | 2011-01-24 09:35:00 | 2011-01-25 09:35:00 |  6 | 
|  1 | 2011-01-24 10:00:00 | 2011-01-25 10:00:00 |  6 | 
|  2 | 2011-01-24 08:00:00 | 2011-01-25 08:00:00 | 13 | 
|  2 | 2011-01-24 12:00:00 | 2011-01-25 12:00:00 | 12 | 
|  2 | 2011-01-24 12:01:00 | 2011-01-25 12:01:00 | 10 | 
|  2 | 2011-01-24 12:02:00 | 2011-01-25 12:02:00 |  9 | 
|  2 | 2011-01-24 12:03:00 | 2011-01-25 12:03:00 |  8 | 
|  2 | 2011-01-24 12:00:00 | 2011-01-25 12:00:00 | 12 | 
|  2 | 2011-01-24 12:04:00 | 2011-01-25 12:04:00 |  7 | 
|  2 | 2011-01-24 12:05:00 | 2011-01-25 12:05:00 |  6 | 
+---------+---------------------+---------------------+-------+ 
12 rows in set (0.00 sec) 
+0

Bardzo dobre rzeczy tam! Nie jestem pewien co do wydajności obu pomysłów, zresztą głównym celem nie jest brak jakiegokolwiek możliwego okresu "24h". Dla kompletności zmieniłem zaakceptowaną odpowiedź na tę. – eduardev

2

ta zwraca listę user_id, które dokonały ponad 100 pobrań w dowolnym okresie 1 dnia:

SELECT user_id, count(user_id) as downloads_count, DATE(dl_date) 
FROM downloads 
GROUP BY user_id, DATE(dl_date) 
HAVING count(user_id) > 100; 
+0

interesujące grupowanie według daty, które przyjmie 1 pełny dzień, dałbym Ci Przydatny punkt, gdybym mógł, w każdym razie powiedzmy, że okres byłby czymś różnym od całego dnia, na przykład tygodnia lub czegoś bardziej złożonego jak 8 godziny ... dość trudne, prawda? Prawdopodobnie zaakceptuję to, ponieważ na razie to zrobi! Dzięki – eduardev

+1

W przypadku innych okresów możesz grupować według znacznika czasu zaokrąglonego do wielokrotności okresu: 'GROUP BY id_użytkownika, unix_timestamp (dl_date) - (unix_timestamp (dl_date)% 3600)' (1 godzina) – arnaud576875

+0

Miejsce docelowe, wielkie dzięki – eduardev

0

chcesz filtrować na dwóch wartości dat, stosując między nimi grupę na user_id, a następnie użyj HAVING do filtrowania zgrupowanych wyników.

Trzy parametry, --Date1--, --Date2-- i --Threshhold--

select user_id 
    , count(*) 
    from downloads 
where dl_date between --Date1-- and --Date2-- 
group by user_id 
having count(*) > --Threshhold-- 
+0

dziękuję za pomysł, ale nie ma wartości daty rozpoczęcia ani daty zakończenia ... Zobacz Patrick Echterbruch powyżej! – eduardev

1

Jeśli masz czas jak ten, który jest mniejszy lub równy 24 godzin:

SET @period_start='2010-10-10 06:00:00'; 
SET @period_end='2010-10-11 05:59:59'; 

wtedy,

SELECT user_id, COUNT(id) AS num 
FROM downloads WHERE dl_date>= @period_start AND dl_date<= @period_end 
GROUP BY user_id HAVING num> 100; 

Ale jeśli masz czas jak ten, który jest większy niż 24h:

SET @period_start='2010-10-10 06:00:00'; 
SET @period_end='2011-09-17 13:15:12'; 

Jak obliczyć liczbę pobrań? Czy to w ciągu 24 godzin od @period_end, czy od @period_start. A może chcesz najnowszą porcję 24h?

Powiązane problemy