2015-01-31 2 views
2

Допустим, мы имеем эту таблицу со столбцами RowID и Call:SQL: Получить запуска строки дельту для записей

RowID Call DesiredOut 
1  A  0 
2  A  0 
3  B   
4  A  1 
5  A  0 
6  A  0 
7  B 
8  B 
9  A  2 
10  A  0 

Я хочу SQL запроса последнего столбца DesiredOut следующим образом: Каждый раз, когда Call является «A» вернитесь до тех пор, пока «A» не будет найден снова и не подсчитайте количество записей, находящихся между двумя записями «A».

Пример: RowID 4 имеет «A» и ближайший предшественник в RowID 2. Между RowID 2 и 4 RowID мы имеем один Call «B», поэтому мы считаем 1.

Есть элегантный и производительный способ сделать это с помощью ANSI SQL?

+1

Является ли rowid автоматически генерируется и хранится внутри базы данных? – frlan

+3

Какую базу данных вы используете? –

ответ

1

Вы можете использовать запрос, чтобы найти предыдущую строку Call = A. Затем, можно подсчитать количество строк между этой строкой и текущей строкой:

select RowID 
,  `Call` 
,  (
     select count(*) 
     from YourTable t2 
     where RowID < t1.RowID 
       and RowID > coalesce(
        (
        select RowID 
        from YourTable t3 
        where `Call` = 'A' 
          and RowID < t1.RowID 
        order by 
          RowID DESC 
        limit 1 
        ),0) 
     ) 
from YourTable t1 

Example at SQL Fiddle.

+0

Отлично работает, THX для примера скрипки! – Juergen

+0

@Juergen: строго говоря, ответ не является «ANSI SQL». –

+0

И не дает правильных результатов. – TommCatt

0

Вы могли бы сделать что-то подобное:

SELECT a.rowid - b.rowid 
FROM table as a, 
    (SELECT rowid FROM table where rowid < a.rowid order by rowid) as b 
WHERE <something> 
ORDER BY a.rowid 

Как я не могу сказать, какая СУБД вы используете это еще своего рода псевдо-код, который может работать на базе вашей системы.

2

Я хотел бы подойти к этому сначала найти rowid предыдущего «А» значение. Затем подсчитайте количество значений между ними.

Следующий запрос реализует эту логику с помощью связанных подзапросов:

select t.*, 
     (case when t.call = 'A' 
      then (select count(*) 
        from table t3 
        where t3.id < t.id and t3.id > prevA 
       ) 
     end) as InBetweenCount 
from (select t.*, 
      (select max(rowid) 
       from table t2 
       where t2.call = 'A' and t2.rowid < t.rowid 
      ) as prevA 
     from table t 
    ) t; 

Если вы знаете, что rowid является последовательным без пробелов, вы можете просто использовать вычитание вместо подзапроса для вычисления во внешнем запросе.

+0

У RowID нет пробелов. Я попробую все ответы и сравните время выполнения, так как эта таблица огромна. – Juergen

1

Вот еще одно решение с использованием оконных функций:

with flagged as (
    select *, 
     case 
      when call = 'A' and lead(call) over (order by rowid) <> 'A' then 'end' 
      when call = 'A' and lag(call) over (order by rowid) <> 'A' then 'start' 
      end as change_flag 
    from calls 
) 
select t1.rowid, 
     t1.call, 
     case 
     when change_flag = 'start' then rowid - (select max(t2.rowid) from flagged t2 where t2.change_flag = 'end' and t2.rowid < t1.rowid) - 1 
     when call = 'A' then 0 
     end as desiredout 
from flagged t1 
order by rowid; 

СТЕ первый отмечает начало и конец каждого «А» -блок и окончательный выбор затем использует эти маркеры, чтобы получить разницу между началом один блок и конец предыдущего.

Если rowid не бесщеточный, вы можете легко добавить бесщеточный рулевой колонтитул внутри CTE, чтобы рассчитать разницу.

Я не уверен в производительности. Я не удивлюсь, если ответ Гордона будет быстрее.

SQLFiddle пример: http://sqlfiddle.com/#!15/e1840/1

1

Верьте или нет, это будет довольно быстро, если две колонки индексируются.

select r1.RowID, r1.CallID, isnull(R1.RowID - R2.RowID - 1, 0) as DesiredOut 
from RollCall R1 
left join RollCall R2 
    on R2.RowID =(
     select max(RowID) 
     from RollCall 
     where RowID < R1.RowID 
      and CallID = 'A') 
    and R1.CallID = 'A'; 

Вот Fiddle.

Смежные вопросы