2012-04-02 3 views
6

У меня есть таблица активности со структурой, как это:Oracle GROUP BY аналогичные временные метки?

id prd_id act_dt    grp 
------------------------------------ 
1 1  2000-01-01 00:00:00 
2 1  2000-01-01 00:00:01 
3 1  2000-01-01 00:00:02 
4 2  2000-01-01 00:00:00 
5 2  2000-01-01 00:00:01 
6 2  2000-01-01 01:00:00 
7 2  2000-01-01 01:00:01 
8 3  2000-01-01 00:00:00 
9 3  2000-01-01 00:00:01 
10 3  2000-01-01 02:00:00 

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

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

Для примера данных, группы будут:

id prd_id act_dt    grp 
------------------------------------ 
1 1  2000-01-01 00:00:00 1 
2 1  2000-01-01 00:00:01 1 
3 1  2000-01-01 00:00:02 1 
4 2  2000-01-01 00:00:00 2 
5 2  2000-01-01 00:00:01 2 
6 2  2000-01-01 01:00:00 3 
7 2  2000-01-01 01:00:01 3 
8 3  2000-01-01 00:00:00 4 
9 3  2000-01-01 00:00:01 4 
10 3  2000-01-01 02:00:00 5 

Какой метод будет использовать для достижения этой цели?

Размер таблицы составляет ~ 20 миллионов строк, если это влияет на метод, используемый для решения проблемы.

+0

Не могли бы вы подключиться к предыдущей или аналогичной конструкции? –

+3

Подобный вопрос был задан ранее сегодня - у него очень интересный ответ с наивысшим рейтингом: http://stackoverflow.com/questions/9977371/group-by-values-that-are-in-sequence –

ответ

2

Я не волшебник Oracle, поэтому я предполагаю, что в самый лучший вариант для одной линии:

(CAST('2010-01-01' AS DATETIME) - act_dt) * 24 * 60 * 60  AS time_id, 

Это просто должно быть «количество секунд от [aDateConstant] для act_dt». Результат может быть отрицательным. Это просто должно быть количество секунд, чтобы превратить ваш act_dt в INT. Остальное должно работать нормально.

WITH 
    sequenced_data 
AS 
(
    SELECT 
    ROW_NUMBER() OVER (PARTITION BY prd_id ORDER BY act_dt)  AS sequence_id, 
    (CAST('2010-01-01' AS DATETIME) - act_dt) * 24 * 60 * 60  AS time_id, 
    * 
    FROM 
    yourTable 
) 
SELECT 
    DENSE_RANK() OVER (PARTITION BY prd_id ORDER BY time_id - sequence_id) AS group_id, 
    * 
FROM 
    sequenced_data 

Пример данных:

sequence_id | time_id | t-s | group_id 
-------------+---------+-----+---------- 
     1  | 1  | 0 | 1 
     2  | 2  | 0 | 1 
     3  | 3  | 0 | 1 
     4  | 8  | 4 | 2 
     5  | 9  | 4 | 2 
     6  | 12 | 6 | 3 
     7  | 14 | 7 | 4 
     8  | 15 | 7 | 4 


Примечание: Это делает предположить, есть не несколько записей с одинаковым временем. Если они есть, их сначала нужно отфильтровать. Возможно, просто используя GROUP BY в предыдущем CTE.

+0

За http: // forums.dbasupport.com/forums/showthread.php?t=60099, вы можете использовать это для абсолютных # секунд: 'TO_CHAR (act_dt, 'J') * 86400 + TO_CHAR (act_dt, 'SSSSS')' – mellamokb

+1

Я сделал несколько незначительных корректировок в вашем запросе, чтобы получить желаемый результат, и включая подсчет секунд, который я опубликовал: http://www.sqlfiddle.com/#!4/21099/3. Теперь нам просто нужен эффективный оператор 'update' для интеграции этих данных в исходную таблицу. – mellamokb

+0

@mellamokb - Вы можете написать инструкцию UPDATE на самом CTE, это похоже на обновляемое представление. 'UPDATE myCTE SET group_field = расчет_using _fields_from_my_cte' – MatBailie