как сгруппировать данные на основе их последовательности и сгруппировать по другим столбцам

У меня есть таблица с 3 столбцами c1,c2,c3 в Oracle, как показано ниже:

c1  c2  c3
 1  34   2
 2  34   2
 3  34   2
 4  24   2
 5  24   2
 6  34   2
 7  34   2
 8  34   1

Мне нужно сгруппировать col1 и получить минимальное и максимальное число (из col1) на основе его последовательности, col2 и col3.

т. е. мне нужен результат, как показано ниже:

c1_min  c1_max  c2  c3
     1       3  34   2
     4       5  24   2
     6       7  34   2
     8       8  34   1

person Mars    schedule 08.11.2014    source источник


Ответы (2)


Насколько я понимаю, вы хотите сгруппировать последовательные строки вместе. Это далеко не тривиально. По крайней мере, сейчас я не могу найти простого способа сделать это. Для простоты понимания я разобью запрос на несколько шагов:

Шаг 1:

Прежде всего, необходимо определить границы ваших «групп». Здесь вам может помочь аналитическая функция LAG:

CASE WHEN LAG("c2", 1) OVER(ORDER BY "c1") = "c2" 
      AND LAG("c3", 1) OVER(ORDER BY "c1") = "c3" 
     THEN 0 
     ELSE 1
END CLK,
T.* FROM T
ORDER BY "c1"

Шаг 2:

Второй шаг – пронумеровать каждую из ваших групп. Простой SUM над разделом поможет. Это приводит к:

SELECT SUM(CLK) OVER (ORDER BY "c1"
                      ROWS BETWEEN UNBOUNDED PRECEDING 
                      AND CURRENT ROW) GRP,
       V.* 
FROM (
  SELECT
    CASE WHEN LAG("c2", 1) OVER(ORDER BY "c1") = "c2" 
          AND LAG("c3", 1) OVER(ORDER BY "c1") = "c3" 
         THEN 0 
         ELSE 1
    END CLK,
    T.* FROM T
) V
ORDER BY "c1";

Заключительный этап:

Наконец, вы можете обернуть это простым запросом GROUP BY, чтобы получить желаемый результат:

SELECT MIN("c1"), MAX("c1"), "c2", "c3" FROM
(
    SELECT SUM(CLK) OVER (ORDER BY "c1"
                          ROWS BETWEEN UNBOUNDED PRECEDING 
                          AND CURRENT ROW) GRP,
           V.* 
    FROM (
      SELECT
        CASE WHEN LAG("c2", 1) OVER(ORDER BY "c1") = "c2"
              AND LAG("c3", 1) OVER(ORDER BY "c1") = "c3"
             THEN 0 
             ELSE 1
        END CLK,
        T.* FROM T
    ) V
)
GROUP BY GRP, "c2", "c3"
ORDER BY GRP

См. http://sqlfiddle.com/#!4/7d57c/10.

person Sylvain Leroux    schedule 08.11.2014
comment
я использую жабу для выполнения запросов, когда я запускаю вышеуказанный запрос. это дает мне сообщение об ошибке ORA-00904: c3: неверный идентификатор' - person Mars; 10.11.2014
comment
@Mars - Сильвен процитировал ваши идентификаторы, я думаю, потому что вы показали их в нижнем регистре в результатах запроса. Если они на самом деле не указаны, просто удалите все двойные кавычки из запроса. - person Alex Poole; 10.11.2014

Существует несколько способов решения проблемы пробелов и островов. В качестве альтернативы версии Sylvain lag — не лучше, а просто по-другому — вы можете использовать трюк с номерами строк, рассчитанными аналитически на основе ваших полей группировки. Это добавляет псевдостолбец «цепочка» к значениям таблицы, которые будут уникальными для каждой непрерывной группы пар c2/c3:

select c1, c2, c3,
  dense_rank() over (partition by c2, c3 order by c1)
    - dense_rank() over (partition by null order by c1) as chain
from t42
order by c1, c2, c3;

(Я не могу взять на себя ответственность за это - я впервые увидел это здесь). Затем вы можете использовать это как встроенное представление для расчета суммы:

select min(c1) as c1_min, max(c1) as c1_max, c2, c3
from (
  select c1, c2, c3,
    dense_rank() over (partition by c2, c3 order by c1)
      - dense_rank() over (partition by null order by c1) as chain
  from t42
)
group by c2, c3, chain
order by c1_min;

    C1_MIN     C1_MAX         C2         C3
---------- ---------- ---------- ----------
         1          3         34          2 
         4          5         24          2 
         6          7         34          2 
         8          8         34          1 

SQL Fiddle также показывает промежуточный этап.

Вы можете использовать другие аналитические функции, такие как row_number() вместо dense_rank(); они могут давать несколько разные результаты для некоторых данных, но в этом примере вы получите один и тот же результат.

person Alex Poole    schedule 10.11.2014
comment
Этот ответ решил мою аналогичную проблему для SQL Server 2008 R2, которая не поддерживает функцию LAG. - person dpw; 10.02.2015