SQL, чтобы найти максимум значения между двумя строками в таблице, отсортированными в хронологическом порядке - PullRequest
2 голосов
/ 11 декабря 2010

У меня есть таблица, которая содержит хронологические события.У них есть тип, время и высота.

Данные могут быть обобщены следующим образом:

ID, Type, Time, Height  
1, A, time, 0  
2, XX, time, 0   
3, B, time, 3  
4, B, time, 6  
5, C, time, 0  
6, XX, time, 0  
7, A, time, 0  
8, C, time, 0  
9, A, time, 0  
10, B, time, 2  
11, C, time, 0  

и т. Д. (Столбец времени отсортирован в порядке возрастания)

iхотел бы найти оператор SQL для перечисления всех типов A / B / C, где B - максимум столбца высоты между типами A и C. Таким образом, результат будет выглядеть следующим образом:

1, A, time, 0   
4, B, time, 6  
5, C, time, 0  
7, A, time, 0  
8, C, time, 0  
9, A, time, 0  
10, B, time, 2  
11, C, time, 0  

A/ B и C всегда будут в правильном порядке (т. Е. B всегда будет между A и C), но B может вообще не быть, или может быть несколько B между A и C.

выходные данные могут / не могут перечислять событие B с данными NULL, если между A и C. нет B, гарантируется, что будет C после каждого события типа A.

Все события XX должны игнорироваться в выходных данных,Временные метки в списке никогда не будут дублироваться - никакие два события не будут содержать одинаковое время.

Я предполагаю использовать где-нибудь функцию MAX и выбирать все строки B между A и C в зависимости от времени A иC.

TIA

Ответы [ 3 ]

1 голос
/ 11 декабря 2010

Не уверен, правильно ли я на 100%, но я считаю, что всегда лучше разбить этот материал на меньшие запросы на временные таблицы. Вот в чем дело ... (Кстати, это SQL Server T-SQL)

-- get all the type 'a' and type 'c' IDs to represent time spans
if object_id('tempdb..#tab_ac') is not null drop table #tab_ac
select
    a.ID as A_ID,
    (
        select top 1 c.ID
        from tab c
        where c.Time > a.Time
        and c.Type = 'C'
        order by c.Time
    ) as C_ID
into
    #tab_ac
from
    tab a
where
    a.Type = 'A'

create index ix_#tab_ac on #tab_ac (A_ID, C_ID)    

-- get the id with the max height between those spans
if object_id('tempdb..#result1') is not null drop table #result1
select
    ac.*,
    (
        select x.ID
        from tab x
        where x.Time between ta.Time and tc.Time
        order by a.Height desc
    ) as ID_With_Max_Height
into
    #result1
from
    #tab_ac ac join
    tab ta on ac.A_ID = t.ID join
    tab tc on ac.C_ID = t.ID

-- see if that id is type 'B'
select
    *
from
    #result1 r join
    tab t on r.ID_With_Max_Height = t.ID
where
    t.Type = 'B'

В зависимости от того, как вы хотите обрабатывать связи для максимальной высоты, вы можете изменить предложение ORDER BY этого второго запроса. Удачи.

0 голосов
/ 11 декабря 2010

Я думаю, что это работает. Сначала я построил временные диапазоны, которые идентифицируют время между каждым из A и C, затем сгруппировал B вместе в эти диапазоны и извлек запись с максимальной высотой из каждого, а затем соединил это с необработанными записями для A и C. Вы можете вырезать cte_source_data и заменить ссылки на него вашей фактической таблицей. Я написал и протестировал это в PostgreSQL 9.0, но он должен работать в любой основной базе данных с небольшими изменениями для определенных диалектов SQL.

<code>with cte_source_data as
(
    select
        id, 
        type, 
        timestamp '2010-12-10 21:' || to_char(id, '99') || ':00' as time,
        height
    from
    (
        select 1 as id, 'A' as type, 0 as height union all
        select 2, 'XX', 0 union all
        select 3, 'B', 3 union all
        select 4, 'B', 6 union all
        select 5, 'C', 0 union all
        select 6, 'XX', 0 union all
        select 7, 'A', 0 union all
        select 8, 'C', 0 union all
        select 9, 'A', 0 union all
        select 10, 'B', 2 union all
        select 11, 'C', 0
    ) as data
),
cte_a_to_c_groups as
(
    select 
        row_number() over(order by time) as group,
        time as start_time,
        next_time as end_time 
    from 
    (
        select
            type,
            time,
            lead(type) over(order by time) as next_type,
            lead(time) over(order by time) as next_time
        from 
            cte_source_data
        where
            type in ('A', 'C')
    ) as cte_a_to_c_groups_a
    where
        type = 'A' and next_type = 'C'<br>
)
select
    id,
    type,
    time,
    height
from
(
    select 
        id,
        type,
        time,
        height
    from
        cte_source_data
    where
        type in ('A', 'C')
    union all
    select
        highest_id as id,
        type,
        highest_time as time,
        highest_height as height
    from
    (
        select
            a.id,
            a.type,
            b.group,
            row_number() over(partition by b.group order by a.height desc nulls last) as rn,
            first_value(id) over(partition by b.group order by a.height desc nulls last) as highest_id,
            first_value(time) over(partition by b.group order by a.height desc nulls last) as highest_time,
            first_value(height) over(partition by b.group order by a.height desc nulls last) as highest_height
        from 
            cte_source_data a
        inner join
            cte_a_to_c_groups b
        on
            a.time between b.start_time and b.end_time
        where
            a.type = 'B'
    ) as highest_values
    where
        rn = 1
) as reunification
order by 
    time

Результат:

id  type  time                       height
1   A     2010-12-10 21:00:00  1:00  0
4   B     2010-12-10 21:00:00  4:00  6
5   C     2010-12-10 21:00:00  5:00  0
7   A     2010-12-10 21:00:00  7:00  0
8   C     2010-12-10 21:00:00  8:00  0
9   A     2010-12-10 21:00:00  9:00  0
10  B     2010-12-10 21:00:00 10:00  2
11  C     2010-12-10 21:00:00 11:00  0
0 голосов
/ 11 декабря 2010
select * from table as t1 where `type`!='XX' and height=(
    select MAX(height) from table as t2 where type=t1.type
) order by id
...