2015-07-08 3 views
1

У меня есть набор данных, который имеет временные отметки для разных наборов групп.SQL Grouping by Ranges

Timestamp -- Group -- Value 
--------------------------- 
1   -- A  -- 10 
2   -- A  -- 20 
3   -- B  -- 15 
4   -- B  -- 25 
5   -- C  -- 5 
6   -- A  -- 5 
7   -- A  -- 10 

Я хочу суммировать эти значения в Group поле, но анализируется как он появляется в данных. Например, приведенные выше данные приведут к следующему выходу:

Group -- Sum 
A  -- 30 
B  -- 40 
C  -- 5 
A  -- 15 

Я не хочу этого, что все, что я смог придумать на мой собственный до сих пор:

Group -- Sum 
A  -- 45 
B  -- 40 
C  -- 5 

Используя Oracle 11g, это то, чем я до сих пор помчался. Я знаю, что это неправильно, я надеюсь, что я по крайней мере на правильном пути с RANK(). В реальных данных записи с одной и той же группой могут быть разделены на две временные метки или 100; может быть одна запись в группе, или 100 последовательных. Это не имеет значения, мне они нужны.

WITH SUB_Q AS 
    (SELECT K_ID 
    , GRP 
    , VAL 
    -- GET THE RANK FROM TIMESTAMP TO SEPARATE GROUPS WITH SAME NAME 
    , RANK() OVER(PARTITION BY K_ID ORDER BY TMSTAMP) AS RNK 
    FROM MY_TABLE 
    WHERE K_ID = 123) 
SELECT T1.K_ID 
    , T1.GRP 
    , SUM(CASE 
    WHEN T1.GRP = T2.GRP THEN 
     T1.VAL 
    ELSE 
     0 
    END) AS TOTAL_VALUE 
FROM SUB_Q T1 -- MAIN VALUE 
INNER JOIN SUB_Q T2 -- TIMSTAMP AFTER 
ON T1.K_ID = T2.K_ID 
    AND T1.RNK = T2.RNK - 1 
GROUP BY T1.K_ID 
    , T1.GRP 

Можно ли сгруппировать таким образом? Как мне это сделать?

ответ

3

я подойти к этой проблеме, определив группу, которая является отличается от двух row_number():

select group, sum(value) 
from (select t.*, 
      (row_number() over (order by timestamp) - 
       row_number() over (partition by group order by timestamp) 
      ) as grp 
     from my_table t 
    ) t 
group by group, grp 
order by min(timestamp); 

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

+0

Это выглядит большим , и определенно получает меня, что мне нужно! Я отвечу на этот ответ, как только пройдет много времени. – Gaffi

+0

Действительно красивое и чистое и удобное решение. – pvoosten

1

Раствор с помощью LAG и оконной аналитических функций:

SQL Fiddle

Oracle 11g R2 Настройки схемы:

CREATE TABLE TEST ("Timestamp", "Group", Value) AS 
      SELECT 1, 'A', 10 FROM DUAL 
UNION ALL SELECT 2, 'A', 20 FROM DUAL 
UNION ALL SELECT 3, 'B', 15 FROM DUAL 
UNION ALL SELECT 4, 'B', 25 FROM DUAL 
UNION ALL SELECT 5, 'C', 5 FROM DUAL 
UNION ALL SELECT 6, 'A', 5 FROM DUAL 
UNION ALL SELECT 7, 'A', 10 FROM DUAL; 

Запрос 1:

WITH changes AS (
    SELECT t.*, 
     CASE WHEN LAG("Group") OVER (ORDER BY "Timestamp") = "Group" THEN 0 ELSE 1 END AS hasChangedGroup 
    FROM TEST t 
), 
groups AS (
    SELECT "Group", 
     VALUE, 
     SUM(hasChangedGroup) OVER (ORDER BY "Timestamp" ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS grp 
    FROM changes  
) 
SELECT "Group", 
     SUM(VALUE) 
FROM Groups 
GROUP BY "Group", grp 
ORDER BY grp 

Results:

| Group | SUM(VALUE) | 
|-------|------------| 
|  A |   30 | 
|  B |   40 | 
|  C |   5 | 
|  A |   15 | 
0

Это типичный "star_of_group" проблема (см здесь: https://timurakhmadeev.wordpress.com/2013/07/21/start_of_group/)

В вашем случае, это будет выглядеть следующим образом:

with t as (
    select 1 timestamp, 'A' grp, 10 value from dual union all 
    select 2, 'A', 20 from dual union all 
    select 3, 'B', 15 from dual union all 
    select 4, 'B', 25 from dual union all 
    select 5, 'C', 5 from dual union all 
    select 6, 'A', 5 from dual union all 
    select 7, 'A', 10 from dual 
) 
select min(timestamp), grp, sum(value) sum_value 
    from (
    select t.* 
     , sum(start_of_group) over (order by timestamp) grp_id 
     from (
     select t.* 
      , case when grp = lag(grp) over (order by timestamp) then 0 else 1 end 
       start_of_group 
      from t 
    ) t 
) 
group by grp_id, grp 
order by min(timestamp) 
;