2016-02-16 3 views
0

У меня есть таблица транзакций с более чем 2500 000 строк и три столбца (которые имеют значение): id, company_id и created_at. id идентифицирует транзакцию, company_id определяет, какая компания ее получила, created_at - это метка времени с момента выполнения транзакции.Различия между метками времени MySQL между двумя строками большой таблицы

Я хочу получить список различий между каждой последовательной парой транзакций данной компании. Другими словами, если мой стол идет:

id | company_id | created_at 
------------------------------ 
01 |  ab  | 2016/01/02 
02 |  ab  | 2016/01/03 
03 |  cd  | 2016/01/03 
04 |  ab  | 2016/01/03 
05 |  cd  | 2016/01/04 
06 |  ab  | 2016/01/05 

(Обратите внимание, что может быть произвольное количество сделок других компаний между двумя последовательными сделки данной компании.)

Тогда я хочу, выход на быть:

diff | company_id 
------------------- 
    01 |  ab 
    00 |  ab 
    01 |  cd 
    02 |  ab 

(я писал значение created_at и Diff в дни, но это только для удобства визуализации.)

Я попытался с помощью this, но это было слишком медленно.

--edit:

"Это" является:

SELECT (B.created_at - A.created_at) AS diff, A.company_id 
FROM Transactions A CROSS JOIN Transactions B 
WHERE B.id IN (SELECT MIN (C.id) FROM Transactions C WHERE C.id > A.id AND C.company_id = A.company_id) 
ORDER BY A.id ASC 
+1

Мне слишком ленив, чтобы направить вашу ссылку. –

+0

Отредактировано для добавления дополнительной информации –

+0

Я думаю, что наиболее эффективным будет запись хранимой процедуры с помощью курсора и ее идентификация по id. Cross join - убийца ... – wallycz

ответ

1

Чтобы получить результат, как тот, он выглядит, как вы ожидали, я иногда буду использовать MySQL пользовательского переменные, и есть MySQL выполняет обработку строк «в порядке», так что я может сравнить текущую строку со значениями из предыдущей строки.

Для этого, чтобы эффективно работать, нам нужен соответствующий индекс, чтобы избежать дорогостоящей операции «Использование файлового управления». (Нам понадобятся строки в заказе company_id, затем по порядку id, так что это будут первые два столбца в индексе. Пока мы на нем, мы могли бы также включить столбец created_at и сделать его . покрывающий индекс

... ON Transactions (company_id, id, created_at) 

Тогда можно попробовать запрос следующим образом:

SELECT t.diff 
    , t.company_id 
    FROM (
     SELECT IF(r.company_id = @pv_company_id, r.created_at - @pv_created_at, NULL) AS diff 
       , IF(r.company_id = @pv_company_id, 1, 0) AS include_ 
       , @pv_company_id := r.company_id   AS company_id 
       , @pv_created_at := r.created_at   AS created_at 
      FROM (SELECT @pv_company_id := NULL, @pv_created_at := NULL) i 
      CROSS 
      JOIN Transactions r 
      ORDER 
      BY r.company_id 
       , r.id 
     ) t 
WHERE t.include_ 

Справочное руководство MySQL явно предостерегает от использования определенных пользователем переменных, как это в заявлении Но поведение, которое мы наблюдаем в. MySQL 5.1 и 5.5 являются непротиворечивыми. (Большая проблема заключается в том, что в некоторой будущей версии MySQL может использоваться различие nt).

Встроенный просмотр с псевдонимом как i - это просто инициализация пары пользовательских переменных. Мы могли бы так же легко сделать это как отдельный шаг, прежде чем запускать наш запрос. Но мне нравится включать в инициализацию право инициализации, поэтому мне не нужен отдельный оператор SELECT/SET.

MySQL обращается к таблице транзакций и сначала обрабатывает ORDER BY, заказывая строки из Transactions в (company_id, id). (Мы предпочитаем делать это через индекс, а не через дорогостоящую операцию «Использование файловой системы», поэтому мы хотим, чтобы этот индекс определялся с company_id и id в качестве ведущих столбцов.

«Трюк» сохраняется значения из текущей строки в пользовательские переменные. При обработке следующей строки значения из предыдущей строки доступны в пользовательских переменных для выполнения сравнений (является ли текущая строка для той же company_id, что и предыдущая строка?) и для выполнения вычисления (разница между значениями двух строк:

Основываясь на использовании операции вычитания, я предполагаю, что столбцы created_at являются целыми числами/числами РИК. То есть я предполагаю, что created_atнеDATE, DATETIME, или TIMESTAMP тип данных, потому что мы не используем операцию вычитания, чтобы найти разницу.

SELECT a 
    , b 
    , a - b     AS `subtraction` 
    , DATEDIFF(a,b)   AS `datediff` 
    , TIMESTAMPDIFF(DAY,b,a) AS `tsdiff` 
    FROM (SELECT DATE('2015-02-17') AS a 
       , DATE('2015-01-16') AS b 
     ) t 

возвращается:

a   b   subtraction datediff tsdiff 
---------- ---------- ----------- -------- ------ 
2015-02-17 2015-01-16   101  32  32 

(Операция вычитания не бросает ошибку Но то, что он возвращает может быть неожиданным в этом примере, он возвращает разность между двумя целыми значениями 20150217 и 20150116.. , который не количество дней между двумя DATE выражениями.)

EDIT

Я замечаю, что исходный запрос содержит ORDER BY. Если вам нужны строки, возвращаемые в определенном порядке, вы можете включить этот столбец в запрос встроенного представления и использовать внешний код запроса ORDER BY.

SELECT t.diff 
    , t.company_id 
    FROM (
     SELECT IF(r.company_id = @pv_company_id, r.created_at - @pv_created_at, NULL) AS diff 
       , IF(r.company_id = @pv_company_id, 1, 0) AS include_ 
       , @pv_company_id := r.company_id   AS company_id 
       , @pv_created_at := r.created_at   AS created_at 
       , r.id         AS id 
      FROM (SELECT @pv_company_id := NULL, @pv_created_at := NULL) i 
      CROSS 
      JOIN Transactions r 
      ORDER 
      BY r.company_id 
       , r.id 
     ) t 
WHERE t.include_ 
ORDER BY t.id 

К сожалению, нет обходящий в «Использование FileSort» для ORDER BY на внешнем запросе.

+0

Повторяя здесь некоторые примечания, уже включенные в этот ответ: Не используйте операцию вычитания в выражениях 'DATE'; который не вернет ожидаемый результат. например 'SELECT ('2015-02-17' + INTERVAL 0 DAY) - ('2015-01-16' + INTERVAL 0 DAY)' возвращает 101 не 32. Чтобы вернуть разницу в днях, используйте функцию DATEFIFF. И Справочное руководство по MySQL * явно * предостерегает от использования определяемых пользователем переменных таким образом ... "порядок оценки выражений с использованием пользовательских переменных - * undefined *.". Ссылка: [http://dev.mysql.com/doc/refman/5.6/en/user-variables.html]. – spencer7593

0

Попробуйте

SELECT 
    t1.company_id, 
    t2.created_at - t1.created_at as diff 
FROM Transactions t1 
LEFT JOIN Transactions t2 
    on t2.created_at > t1.created_at 
    and t2.company_id = t1.company_id 
+0

Помимо опечаток в самом запросе, это не вернет правильные результаты, так как левое объединение вернет более одной результирующей записи в строку. – randyh22

+0

@ randyh22, что заставляет вас так думать? – IgorM

+0

Я хочу различия между только последовательными парами, а не каждой возможной парой. –

0

Вы можете использовать функциональность курсора. Если вы откроете курсор, вы поместите каждую строку, и каждая из двух линий вы получите разницу. Я думаю, что этот метод более эффективен, потому что слайд всех строк таблицы вместо этого объединяет более двух с половиной миллионов.

+0

На основе выборочных данных, предоставленных OP, сравниваемые строки не всегда являются последовательными в таблице. Вы не всегда можете сравнивать две последовательные строки в таблице. – randyh22

+1

Но вы можете заказать его по company_id, created_at, а затем использовать курсор ... – wallycz

+0

Можете ли вы на самом деле написать код для этого? Я не знаю, как это сделать, и уроки, которые я смог найти, не очень помогли. –

0

Попробуйте это тоже.

SELECT company_id, 
    (SELECT DATEDIFF(created_at,TR.created_at) 
    FROM transactions 
    WHERE id > TR.id AND company_id = TR.company_id LIMIT 0,1) AS diff 
FROM transactions AS TR 
HAVING diff is not null 
+0

Это действие длится десять минут и ничего не дает. –

+0

С транзакциями 2.5M, это займет некоторое время. Особенно, если поля company_id и created_at не индексируются. – randyh22

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