sql, большой запрос: объединить все записи между двумя строками в переменной - PullRequest
3 голосов
/ 18 февраля 2020

Я должен решить эту проблему в рамках BigQuery. У меня есть этот столбец в моей таблице:

event            | time
_________________|____________________
start            | 1
end              | 2
random_event_X   | 3
start            | 4 
error_X          | 5 
error_Y          | 6
end              | 7
start            | 8
error_F          | 9
start            | 10
random_event_Y   | 11
error_z          | 12
end              | 13

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

Выход желания будет выглядеть так:

string_agg                            | count
"start, end"                          |  1
"start, error_X, error_Y, end"        |  1
"start, random_event_Y error_Z, end"  |  1

Так что все между каждым началом и концом, если start имеет end. Поэтому без random_event_X во время 3, start во время 8 или error_F во время 9.

Я не смог найти решение и не смог понять, как подойти к этой проблеме. Любая помощь или совет приветствуются.

Ответы [ 2 ]

2 голосов
/ 18 февраля 2020

Ниже для BigQuery Standard SQL

#standardSQL
SELECT agg_events, COUNT(1) cnt 
FROM (
  SELECT STRING_AGG(event ORDER BY time) agg_events, COUNTIF(event IN ('start', 'end')) flag   
  FROM (
    SELECT *, COUNTIF(event = 'start') OVER(PARTITION BY grp1 ORDER BY time) grp2     
    FROM (
      SELECT *, COUNTIF(event = 'end') OVER(ORDER BY time DESC) grp1 
      FROM `project.dataset.table`
    )
  )
  GROUP BY grp1, grp2
)
WHERE flag = 2
GROUP BY agg_events   

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

Row agg_events                          cnt  
1   start,random_event_Y,error_z,end    1    
2   start,error_X,error_Y,end           1    
3   start,end                           1   
0 голосов
/ 18 февраля 2020

SQL таблицы представляют неупорядоченные наборы - это особенно верно для массивно параллельных столбчатых баз данных, таких как BigQuery.

Итак, я должен предположить, что у вас есть какой-то другой столбец это определяет порядок. Если это так, вы можете использовать кумулятивную сумму для определения групп и затем агрегирования:

select grp,
       string_agg(event, ',' order by time)
from (select t.*,
             countif(event = 'start') over (order by time) as grp
      from t
     ) t
group by grp
order by min(time);

Примечание. Я бы также посоветовал вам использовать array_agg() вместо string_agg(). С массивами, как правило, легче работать, чем со строками.

РЕДАКТИРОВАТЬ:

Я вижу, вы хотите только до end. В этом случае другой уровень оконных функций:

select grp,
       string_agg(event, ',' order by <ordering col>)
from (select t.*,
             max(case when event = 'end' then time end) over (partition by grp) as max_end_time
      from (select t.*,
                   countif(event = 'start') over (order by <ordering col>) as grp
            from t
           ) t
     ) t
where max_end_time is null or time <= max_end_time
group by grp
order by min(<ordering col>);
...