2011-01-24 2 views
6

Я пытаюсь получить общее количество строк между определенным временем времени или промежутком времени. В основном, скажем, в следующей таблице:mysql select количество строк между временными интервалами

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; 

И эта таблица заполняется каждый раз, когда кто-то скачивает что-то.

Так что мне действительно нужно получить список пользователей (user_id), которые сделали больше, чем, например, 100 загрузок за период времени, например, 24 часа. Не в последние 24 часа, но в тот точный период времени, даже если это было во время рождества в прошлом году =)

Любые идеи вообще?!

+0

Если этот запрос принимает одну метку времени, как «начало периода», вычислить «конец периода» и список всех пользователей, которые превосходили количество загрузок в этот период, или должен ли он перечислять всех пользователей, которые когда-либо превышали загрузку x за 24 часа? –

+0

@Patrick нет начального или конечного периода ... просто укажите всех пользователей, которые превышают загрузку X в Y (как период времени, который может быть чем-то отличным от 24 часов). Ограничение времени – eduardev

+0

Я думал о (возможном) решении. Если вы все еще заинтересованы, я поработаю и опубликую его здесь, но у него будет недостаток: представьте, что я делаю 100 загрузок всего за один час, тогда будет много периодов, в которых я превышу лимит. Пример: 100 загрузок сегодня с 8 до 9 утра. Вчера 9:00 до сегодняшнего дня 9 AM = 100 загрузок. Вчера 10:00 до сегодняшнего дня 10 AM = 100 загрузок. Вчера 11:00 до сегодняшнего дня 11 AM = 100 загрузок. Продолжение следует ... –

ответ

6

Хорошо, я понимаю, что я немного поздно, но я хотел отправить свой ответ в любом случае :-)

Что вам требуется может быть сделано с использованием подзапроса, но это может занять возрастов, чтобы закончить на большой стол ...

Думая о вопросе, я пришел к двум различным подходам.

Один из них уже рассмотрен в других ответах, он работает, начиная с определенного момента времени, глядя на интервал, который начинается в это время, а затем смотрит на промежуток равной продолжительности, который следует за ним сразу. Это приводит к ясным, понятным результатам и, вероятно, потребует (например, пользователь не должен превышать 100 загрузок за календарный день). Это, однако, полностью упустило бы ситуации, когда пользователь делает 99 загрузок за час до полуночи и еще 99 в первый час нового дня.

Так что, если требуемый результат является скорее «списком лучших десятков», то это другой подход. Результаты здесь могут быть не столь понятными с первого взгляда, потому что одна загрузка может рассчитывать на несколько интервалов. Это связано с тем, что интервалы будут (и должны) перекрываться.

Вот мои настройки.Я создал таблицу из вашего заявления и добавлен два индекса:

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

данные, которые я вставил в таблицу:

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) 

Как вы можете видеть, все загрузки произошла ни вчера, ни сегодня и выполнялись двумя разными пользователями.

Теперь мы должны иметь в виду следующее: существует математически бесконечное количество интервалов в 24 часа (или интервалы любой другой продолжительности) между '2011-01-24 0:00' и '2011- 01-25 23:59:59 '. Но точность сервера, составляет одну секунду, это сводится к 86400 интервалов:

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 

Таким образом, мы могли бы использовать цикл для перебора всех этих интервалов и вычислить количество загрузок для каждого пользователя и за интервал. Конечно, не все интервалы представляют для нас такой же интерес, поэтому мы можем пропустить некоторые из них, используя временные метки в таблице как «начало интервала».

Это то, что делает следующий запрос. Он использует каждую временную метку загрузки в таблице как «начало интервала», добавляет продолжительность интервала и затем запрашивает количество загрузок на пользователя за этот интервал.

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; 

Вот результат:

+---------+---------------------+---------------------+-------+ 
| 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

Довольно хороший материал! В любом случае, не уверенный в производительности по обеим идеям, основная цель не пропускает какой-либо возможный период «24 часа». Ради полноты я изменил принятый ответ на этот вопрос. – eduardev

2

Это возвращает список user_id, которые составили более 100 загрузок в течение любого периода 1 день:

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

интересная группировка по дате будет предполагать 1 полный день, я бы дал вам полезный полет, если бы мог, все равно скажем, что период будет чем-то отличным от полного дня, например недели или чего-то более сложного, такого как 8 часов ... довольно сложно? Я, вероятно, соглашусь с этим, потому что это будет сделано сейчас! Спасибо – eduardev

+1

В течение других периодов времени вы можете группировать по временной шкале, округленной до кратного периода времени: 'GROUP BY user_id, unix_timestamp (dl_date) - (unix_timestamp (dl_date)% 3600)' (1 час) – arnaud576875

+0

Пятно на, большое спасибо – eduardev

0

Вы хотите отфильтровать два значения даты, используя между ними, группы по user_id, а затем используйте HAVING для фильтрации сгруппированных результатов.

три параметра, --Date1--, --Date2-- и --Threshhold--

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

благодарю за идею, но, ну нет начальных или конечных значений даты ... См. Patrick Echterbruch выше! – eduardev

1

Если у вас есть период, как это, которое меньше или равно 24 часа:

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

тогда

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; 

Но если у вас есть период, как это, что больше чем за 24 часа:

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

Как вы хотите рассчитать число загрузок? Это в 24h кусках от @period_end или от @period_start. Или вам просто нужен последний 24-часовой кусок?