2015-04-08 3 views
0

Допустим, у меня есть следующие транзакционной данные:MySQL: поиск групп записей в течение определенного периода времени

+--------------------------------------------------+ 
| CustomerID | TransactionID |  TransactionDate | 
+--------------------------------------------------+ 
|   1 |    1 | 2015-01-22 15:09:43 | 
|   1 |    2 | 2015-01-22 16:58:13 | 
|   1 |    3 | 2015-01-23 07:05:53 | 
|   2 |    4 | 2015-02-02 21:12:28 | 
|   2 |    5 | 2015-02-02 22:01:59 | 
|   3 |    6 | 2015-02-03 19:50:47 | 
|   2 |    7 | 2015-02-04 16:12:08 | 
|   4 |    8 | 2015-02-05 17:13:29 | 
+--------------------------------------------------+ 

TransactionDate, кстати, хранится в виде метки времени UNIX, а не строковое представление даты. Я просто преобразовал его, чтобы было легче читать здесь.

Я пытаюсь создать запрос, который скажет мне, какие клиенты (по ID) купили 3 или более раз в течение 24-часового окна. В этом (по общему признанию, базовом) примере единственным клиентом, который соответствует этим критериям, является №1, поскольку эти три транзакции произошли в течение 24-часового периода. Клиент № 2 не будет, потому что в течение 24 часов произошло всего две транзакции; третий был за пределами 24-часового окна от первого. Все, о чем я забочусь, это идентификатор клиента; Мне не нужно знать, какие транзакции были задействованы или фактическая дата транзакции. В настоящее время я делаю это с помощью кода - выполнить запрос, который возвращает приведенную выше таблицу в массив PHP, преобразование результатов с помощью кода, как это:

foreach ($results as $result) 
    $data[$result['CustomerID']][] = array(
     'TransactionID' => $result['TransactionID'], 
     'TransactionDate' => $result['TransactionDate'] 
    ); 

... который дает мне массив, как это ...

$data = array(
    '1' => array(
     array(
      'TransactionID' => '1', 
      'TransactionDate' => '2015-01-22 15:09:43' 
     ), 
     array(
      'TransactionID' => '2', 
      'TransactionDate' => '2015-01-22 16:58:13' 
     ), 
     array(
      'TransactionID' => '3', 
      'TransactionDate' => '2015-01-23 07:05:53' 
     ), 
    ), 
    '2' => array(
     array(
      'TransactionID' => '4', 
      'TransactionDate' => '2015-02-02 21:12:28' 
     ), 
     array(
      'TransactionID' => '5', 
      'TransactionDate' => '2015-02-02 22:01:59' 
     ), 
     array(
      'TransactionID' => '7', 
      'TransactionDate' => '2015-02-04 16:12:08' 
     ), 
    ), 
    // etc. 
) 

... и затем пройти через каждый клиент и посмотреть, если третья запись в течение 24 часов после первой записи, если четвёртая запись в течение 24 часов после второй записи и т.д., в зависимости от того, как в течение этого периода для этого клиента имеется много записей о покупке. Если в любой момент я нахожу три записи в течение 24-часового окна, я отмечаю идентификатор клиента и перехожу к следующему (мне все равно, сколько 24-часовых блоков из 3+ транзакций я нахожу, именно так я нашел хотя бы один).

Если бы это было так просто, как 8 записей транзакций, я был бы счастлив оставить это как некоторый процедурный код, как я изложил здесь; но когда я перетаскиваю 92 000 записей из базы данных в PHP для обработки (и каждая запись больше похожа на 70 полей, а не на 3), я начинаю сталкиваться с серьезными проблемами памяти и тайм-аута. Я знаю, потому что раньше я работал с большими наборами данных (в миллионах строк), что любой сервер базы данных, заслуживающий своей соли, может справиться с такими вычислениями намного быстрее, чем PHP, работающий как модуль под Apache. Я не знаю, как я могу рассчитать время между записями непосредственно в среде запросов. Возможна ли такая обработка в MySQL?

+0

Вы можете использовать ['TIMEDIFF()'] (http://www.w3resource.com/mysql/date-and-time-functions/mysql-timediff-function.php) в mysql. –

+0

Я пытаюсь не обрабатывать 90 000 записей в моей хранимой процедуре. В идеале я хотел бы, чтобы результат 'timediff()' был вычисляемым столбцом в запросе, который затем можно выбрать с помощью предложения 'HAVING'. Это скорее вопрос создания запроса, в котором строка n вычисляется на основе содержимого строки n-1. – mounty

ответ

0

Вы можете попробовать что-то вроде этого:

SELECT frst.CustomerID, frst.TransactionDate as firstdate, 
    min(scnd.TransactionDate) as secdate, 
    min(third.TransactionDate) as thirddate FROM so29518803 frst 
    inner join so29518803 scnd on scnd.CustomerID = frst.CustomerID and 
    scnd.TransactionDate > frst.TransactionDate inner join so29518803 third on 
    scnd.CustomerID = third.CustomerID and 
    third.TransactionDate>scnd.TransactionDate group by frst.CustomerID, 
    frst.TransactionDate 
having timestampdiff(second,firstdate, thirddate) < 86400 

с индексом на CustomerID и TransactionDate, что запрос выполняется довольно быстро. Однако, если вам понадобится проверить большее количество транзакций в течение периода времени, этот подход не будет работать.

Смежные вопросы