Как мне найти «пробел» в работе счетчика с SQL? - PullRequest
92 голосов
/ 21 августа 2009

Я бы хотел найти первый «пробел» в столбце счетчика в таблице SQL. Например, если есть значения 1,2,4 и 5, я бы хотел узнать 3.

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

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

Ответы [ 18 ]

0 голосов
/ 15 марта 2019

Если ваш счетчик начинается с 1, и вы хотите сгенерировать первый номер последовательности (1), когда он пуст, вот исправленный фрагмент кода из первого ответа, действительный для Oracle:

SELECT
  NVL(MIN(id + 1),1) AS gap
FROM
  mytable mo  
WHERE 1=1
  AND NOT EXISTS
      (
       SELECT  NULL
       FROM    mytable mi 
       WHERE   mi.id = mo.id + 1
      )
  AND EXISTS
     (
       SELECT  NULL
       FROM    mytable mi 
       WHERE   mi.id = 1
     )  
0 голосов
/ 07 февраля 2019

Следующее решение:

  • предоставляет данные испытаний;
  • внутренний запрос, который создает другие пробелы; и
  • это работает в SQL Server 2012.

Пронумеровывает упорядоченные строки последовательно в предложении " с ", а затем дважды использует результат с внутренним объединением номера строки, но смещением на 1, чтобы сравнить строку до строки со , ища идентификаторы с пробелом больше 1. Больше, чем просили, но более широко применимые.

create table #ID ( id integer );

insert into #ID values (1),(2),    (4),(5),(6),(7),(8),    (12),(13),(14),(15);

with Source as (
    select
         row_number()over ( order by A.id ) as seq
        ,A.id                               as id
    from #ID as A WITH(NOLOCK)
)
Select top 1 gap_start from (
    Select 
         (J.id+1) as gap_start
        ,(K.id-1) as gap_end
    from       Source as J
    inner join Source as K
    on (J.seq+1) = K.seq
    where (J.id - (K.id-1)) <> 0
) as G

Внутренний запрос выдает:

gap_start   gap_end

3           3

9           11

Внешний запрос выдает:

gap_start

3
0 голосов
/ 10 января 2019

Обнаружено, что большинство подходов работают очень, очень медленно mysql. Вот мое решение для mysql < 8.0. Проверено на записях 1M с промежутком в конце ~ 1сек до конца. Не уверен, что он подходит для других разновидностей SQL.

SELECT cardNumber - 1
FROM
    (SELECT @row_number := 0) as t,
    (
        SELECT (@row_number:=@row_number+1), cardNumber, cardNumber-@row_number AS diff
        FROM cards
        ORDER BY cardNumber
    ) as x
WHERE diff >= 1
LIMIT 0,1
Я предполагаю, что последовательность начинается с `1`.
0 голосов
/ 06 августа 2018
            -- PUT THE TABLE NAME AND COLUMN NAME BELOW
            -- IN MY EXAMPLE, THE TABLE NAME IS = SHOW_GAPS AND COLUMN NAME IS = ID

            -- PUT THESE TWO VALUES AND EXECUTE THE QUERY

            DECLARE @TABLE_NAME VARCHAR(100) = 'SHOW_GAPS'
            DECLARE @COLUMN_NAME VARCHAR(100) = 'ID'


            DECLARE @SQL VARCHAR(MAX)
            SET @SQL = 
            'SELECT  TOP 1
                    '+@COLUMN_NAME+' + 1
            FROM    '+@TABLE_NAME+' mo
            WHERE   NOT EXISTS
                    (
                    SELECT  NULL
                    FROM    '+@TABLE_NAME+' mi 
                    WHERE   mi.'+@COLUMN_NAME+' = mo.'+@COLUMN_NAME+' + 1
                    )
            ORDER BY
                    '+@COLUMN_NAME

            -- SELECT @SQL

            DECLARE @MISSING_ID TABLE (ID INT)

            INSERT INTO @MISSING_ID
            EXEC (@SQL)

            --select * from @MISSING_ID

            declare @var_for_cursor int
            DECLARE @LOW INT
            DECLARE @HIGH INT
            DECLARE @FINAL_RANGE TABLE (LOWER_MISSING_RANGE INT, HIGHER_MISSING_RANGE INT)
            DECLARE IdentityGapCursor CURSOR FOR   
            select * from @MISSING_ID
            ORDER BY 1;  

            open IdentityGapCursor

            fetch next from IdentityGapCursor
            into @var_for_cursor

            WHILE @@FETCH_STATUS = 0  
            BEGIN
            SET @SQL = '
            DECLARE @LOW INT
            SELECT @LOW = MAX('+@COLUMN_NAME+') + 1 FROM '+@TABLE_NAME
                    +' WHERE '+@COLUMN_NAME+' < ' + cast( @var_for_cursor as VARCHAR(MAX))

            SET @SQL = @sql + '
            DECLARE @HIGH INT
            SELECT @HIGH = MIN('+@COLUMN_NAME+') - 1 FROM '+@TABLE_NAME
                    +' WHERE '+@COLUMN_NAME+' > ' + cast( @var_for_cursor as VARCHAR(MAX))

            SET @SQL = @sql + 'SELECT @LOW,@HIGH'

            INSERT INTO @FINAL_RANGE
             EXEC( @SQL)
            fetch next from IdentityGapCursor
            into @var_for_cursor
            END

            CLOSE IdentityGapCursor;  
            DEALLOCATE IdentityGapCursor;  

            SELECT ROW_NUMBER() OVER(ORDER BY LOWER_MISSING_RANGE) AS 'Gap Number',* FROM @FINAL_RANGE
0 голосов
/ 15 ноября 2017

Если вы используете Firebird 3, это наиболее элегантно и просто:

select RowID
  from (
    select `ID_Column`, Row_Number() over(order by `ID_Column`) as RowID
      from `Your_Table`
        order by `ID_Column`)
    where `ID_Column` <> RowID
    rows 1
0 голосов
/ 15 июля 2017

Работает для пустых таблиц или с отрицательными значениями. Только что протестировано в SQL Server 2012

 select min(n) from (
select  case when lead(i,1,0) over(order by i)>i+1 then i+1 else null end n from MyTable) w
0 голосов
/ 15 июля 2017

Вот стандартное решение SQL, которое работает на всех серверах баз данных без изменений:

select min(counter + 1) FIRST_GAP
    from my_table a
    where not exists (select 'x' from my_table b where b.counter = a.counter + 1)
        and a.counter <> (select max(c.counter) from my_table c);

Смотрите в действии для;

0 голосов
/ 16 июля 2016
select min([ColumnName]) from [TableName]
where [ColumnName]-1 not in (select [ColumnName] from [TableName])
and [ColumnName] <> (select min([ColumnName]) from [TableName])
...