2017-02-22 21 views
3

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

enter image description here

Для каждого OwnerID, я хотел бы, чтобы вычислить разницу в столбце creationtime для текущей записи и следующая запись (для тех же ownerID), в виде новой колонки TimeDiff. Я считаю, что здесь требуется самостоятельное объединение, но я не уверен, как использовать самоподключение для вычисления разницы между текущей записью и следующей записью.

При этом самая последняя запись для любого ownerID может иметь значение по умолчанию «NA», так как следующей записи не будет (для того же ownerID), чтобы вычислить разницу.

Вот запрос, который я использовал, чтобы получить этот набор данных:

SELECT DISTINCT ga.ownerid, 
     mr.name, 
     SPLIT_PART(SPLIT_PART(ga.activitydata,' ',2),',',1) AS Assignmentid, 
     EXTRACT(YEAR FROM ga.creationtime) AS YEAR, 
     EXTRACT(MONTH FROM ga.creationtime) AS MONTH, 
     EXTRACT(DAY FROM ga.creationtime) AS DAY, 
     EXTRACT(DOW FROM ga.creationtime) AS DOW, 
     ga.creationtime, 
     a.encodedid, 
     a.name 
    FROM flx2.groupactivities ga 
    JOIN flx2.memberstudytrackitemstatus mstis ON SPLIT_PART (SPLIT_PART (ga.activitydata,' ',2),',',1) = mstis.assignmentid 
    JOIN flx2.artifacts a ON mstis.studytrackitemid = a.id 
    JOIN auth.memberhasroles mhr ON mhr.memberid = ga.ownerid 
    JOIN flx2.memberroles mr ON mr.id = mhr.roleid 
    WHERE ga.activitytype = 'assign' 
    AND ga.ownerid NOT IN (SELECT memberid FROM auth.memberhasroles WHERE roleid = 25) 
    AND a.artifacttypeid = 54 
    AND a.encodedid IS NOT NULL 
    ORDER BY ga.ownerid, 
      ga.creationtime, 
      a.encodedid 

Я использую Amazon Redshift, чтобы получить эти данные.

Любая помощь будет оценена по достоинству.

TIA!

UPDATE:

Я использовал метод, предложенный @systemjack. Вот результаты, которые я получаю:

enter image description here

Мы можем ясно заметить здесь, что колонна encodedid становится повторяется для того же assignmentID (MAT.PRB.410, как подчеркивается в изображении выше), который не должен быть дело. Это не происходило без функции LEAD в запросе, упомянутом выше. Вот обновленный запрос, который я использую (только имеет дополнительную функцию LEAD):

SELECT DISTINCT ga.ownerid, 
     mr.name, 
     SPLIT_PART(SPLIT_PART(ga.activitydata,' ',2),',',1) AS Assignmentid, 
     EXTRACT(YEAR FROM ga.creationtime) AS YEAR, 
     EXTRACT(MONTH FROM ga.creationtime) AS MONTH, 
     EXTRACT(DAY FROM ga.creationtime) AS DAY, 
     EXTRACT(DOW FROM ga.creationtime) AS DOW, 
     ga.creationtime, 
     LEAD(ga.creationtime,1) OVER (PARTITION BY ga.ownerid ORDER BY ga.creationtime) AS nexttime, 
     a.encodedid, 
     a.name 
FROM flx2.groupactivities ga 
    JOIN flx2.memberstudytrackitemstatus mstis ON SPLIT_PART (SPLIT_PART (ga.activitydata,' ',2),',',1) = mstis.assignmentid 
    JOIN flx2.artifacts a ON mstis.studytrackitemid = a.id 
    JOIN auth.memberhasroles mhr ON mhr.memberid = ga.ownerid 
    JOIN flx2.memberroles mr ON mr.id = mhr.roleid 
WHERE ga.activitytype = 'assign' 
AND ga.ownerid NOT IN (SELECT memberid FROM auth.memberhasroles WHERE roleid = 25) 
AND a.artifacttypeid = 54 
AND a.encodedid IS NOT NULL 
ORDER BY ga.ownerid, 
     ga.creationtime, 
     a.encodedid LIMIT 1000 

Значения в колонке nexttime также, кажется, измученный. Кажется, что следующее значение в столбце creationtime на ocassion. Например: во 2-й записи значение nexttime должно быть 2013-09-18 06:14:59 вместо 2014-01-18 12:16:49

Почему мы получаем больше записей, чем ожидалось? Как исправить эти проблемы?

+0

вы можете изменить таблицу и добавить еще один столбец? – DCR

+0

Я получаю эти данные, используя запрос, который я опубликовал в вопросе. Так что это набор результатов, а не сама таблица. Если вы попросите добавить столбец в любую из 'flx2.groupactivities',' flx2.memberstudytrackitemstatus', 'flx2.artifacts',' auth.memberhasroles', 'flx2.memberroles', тогда это невозможно. – Patthebug

+0

В случае 'encodeid' вы должны получать повторяющиеся строки из одного из соединений. Глобальный DISTINCT скрывает, что, когда у вас нет LEAD, который делает две записи отличными, поскольку один получает свое преимущество от дубликата, а второй получает его от фактической следующей записи. В результате они выходят из строя, потому что «порядок» не включает в себя следующее время, и они имеют одинаковые другие значения, поэтому они произвольны. Я бы рекомендовал разделить ваш базовый запрос ... может вставить его в предложение WITH или подвыбор и применить LEAD и любую агрегацию после того, как DISTINCT будет эффективен. – systemjack

ответ

0

Итак, я наконец-то выяснил, как это достичь. Я использовал Dense_Rank() функции и использовать следующий запрос, чтобы получить результат:

WITH t AS 
(
    SELECT DISTINCT ga.ownerid, 
     SPLIT_PART(SPLIT_PART(ga.activitydata,' ',2),',',1) AS Assignmentid, 
     EXTRACT(YEAR FROM ga.creationtime) AS YEAR, 
     EXTRACT(MONTH FROM ga.creationtime) AS MONTH, 
     EXTRACT(DAY FROM ga.creationtime) AS DAY, 
     EXTRACT(DOW FROM ga.creationtime) AS DOW, 
     ga.creationtime, 
     DENSE_RANK() OVER (PARTITION BY ga.ownerid ORDER BY ga.ownerid,ga.creationtime,a.encodedid) AS RowNum, 
     a.encodedid, 
     a.name 
    FROM flx2.groupactivities ga 
    JOIN flx2.memberstudytrackitemstatus mstis ON SPLIT_PART (SPLIT_PART (ga.activitydata,' ',2),',',1) = mstis.assignmentid 
    JOIN flx2.artifacts a ON mstis.studytrackitemid = a.id 
    JOIN auth.memberhasroles mhr ON mhr.memberid = ga.ownerid 
    JOIN flx2.memberroles mr ON mr.id = mhr.roleid 
    WHERE ga.activitytype = 'assign' 
    AND ga.ownerid NOT IN (SELECT memberid FROM auth.memberhasroles WHERE roleid = 25) 
    AND a.artifacttypeid = 54 
    AND a.encodedid IS NOT NULL 
    ORDER BY ga.ownerid, 
      ga.creationtime, 
      RowNum, 
      a.encodedid 
) 
SELECT top 100 t1.ownerid, 
     t1.assignmentid, 
     t1.year, 
     t1.month, 
     t1.day, 
     t1.dow, 
     t1.creationtime, 
     t2.creationtime, 
     datediff(day,t1.creationtime,t2.creationtime), 
     t1.encodedid, 
     t1.name 
FROM t AS t1 
    LEFT JOIN t AS t2 
     ON t1.ownerid = t2.ownerid 
     AND t1.rownum + 1 = t2.rownum 
ORDER BY t1.ownerid, 
     t1.creationtime, 
     t1.rownum, 
     t1.encodedid 

Это дало мне следующее:

enter image description here

1

Я лично не вижу декларативного (чистого SQL) способа достижения этого. Сожалею. Вы не можете ссылаться на значение в определенных записях (даже будь то следующее или предыдущее) в наборе, и это по своей природе.

Таким образом, есть три способа, я могу видеть здесь:

1) Использование процедурного расширения для SQL (MySQL имеет один тоже).

2) Получите весь набор и обработайте его извне, со стороны «клиент» (для РСУБД).

3) Добавьте столбец timediff в таблицу + после запуска INSERT/UPDATE, где вы будете вычислять эту разницу и добавлять к ней запись.

+0

MySQL не поддерживает функции окна, но есть такие хаки, как этот для LAG: http://stackoverflow.com/questions/11303532/simulate-lag-function-in-mysql – systemjack

+0

правильно, это именно то, что я сказал - «использовать процедурное расширение ":-) –

2

Обновление: Это выглядит лучше?

with dataset as (
    SELECT DISTINCT ga.ownerid, 
     mr.name, 
     SPLIT_PART(SPLIT_PART(ga.activitydata,' ',2),',',1) AS Assignmentid, 
     EXTRACT(YEAR FROM ga.creationtime) AS YEAR, 
     EXTRACT(MONTH FROM ga.creationtime) AS MONTH, 
     EXTRACT(DAY FROM ga.creationtime) AS DAY, 
     EXTRACT(DOW FROM ga.creationtime) AS DOW, 
     ga.creationtime, 
     a.encodedid, 
     a.name 
    FROM flx2.groupactivities ga 
    JOIN flx2.memberstudytrackitemstatus mstis ON SPLIT_PART (SPLIT_PART (ga.activitydata,' ',2),',',1) = mstis.assignmentid 
    JOIN flx2.artifacts a ON mstis.studytrackitemid = a.id 
    JOIN auth.memberhasroles mhr ON mhr.memberid = ga.ownerid 
    JOIN flx2.memberroles mr ON mr.id = mhr.roleid 
    WHERE ga.activitytype = 'assign' 
     AND ga.ownerid NOT IN (SELECT memberid FROM auth.memberhasroles WHERE roleid = 25) 
     AND a.artifacttypeid = 54 
     AND a.encodedid IS NOT NULL 
) 
select d.*, 
    LEAD(creationtime,1) OVER (PARTITION BY ownerid ORDER BY creationtime) AS nexttime 
from dataset d 
ORDER BY ownerid, creationtime, encodedid, nextime 
LIMIT 1000 

Что-то вроде этого (непроверенных код) может работать. Идея состоит в том, чтобы использовать LEADwindow function, чтобы получить creationtime следующей записи для каждого владельца, которая будет равна NULL, если это последняя запись, а затем используйте обычный DATEDIFF, чтобы получить единицы, которые вы хотите. Оператор CASE во внешнем запросе обрабатывает последний край записи, и вы можете настроить его, чтобы получить нужный результат.

select ownerid, creationtime, 
    case when nextime is not null 
     then datediff('second', creationtime, nextime) 
     else datediff('second', creationtime, sysdate) 
     end as timediff 
from (
    select distinct ownerid, creationtime, 
     lead(creationtime,1) over (partition by ownerid order by creationtime) as nexttime 
    from yourdata 
) 
+0

Большое спасибо за ваш ответ, это имеет смысл в теории. Когда я пытался его реализовать, я вижу странное поведение. Если вы заметили, что в наборе данных образца, который я опубликовал, «время создания» может быть одинаковым для «идентификатора назначения». Столбец 'nexttime', похоже, путается с этим. После включения функции окна «LEAD» я вижу, что я получаю увеличенный набор результатов (больше записей) по сравнению с исходным набором результатов. Я буду обновлять исходный вопрос с результатами после реализации функции «LEAD». – Patthebug

+0

Если идентификатор assignID представляет группу и может быть агрегирован ... то есть. вам нужно только разделить время создания между отдельными назначениями, вы можете добавить отдельное ключевое слово в подвыбор. – systemjack

+0

Нет, я не обязательно забочусь о разнице во времени между различными заданиями. Я просто буквально хочу разницу во времени со следующей записи, для каждой записи, для 'ownerID' (даже если у них одинаковый идентификатор присваивания). – Patthebug