Как найти «дыры» в столе - PullRequest
       22

Как найти «дыры» в столе

14 голосов
/ 06 октября 2008

Я недавно унаследовал базу данных, в которой одна из таблиц имеет первичный ключ, состоящий из закодированных значений (Part1 * 1000 + Part2).
Я нормализовал этот столбец, но я не могу изменить старые значения. Так что теперь у меня есть

select ID from table order by ID
ID
100001
100002
101001
...

Я хочу найти «дыры» в таблице (точнее, первое «отверстие» после 100000) для новых строк.
Я использую следующий выбор, но есть ли лучший способ сделать это?

select /* top 1 */ ID+1 as newID from table
where ID > 100000 and
ID + 1 not in (select ID from table)
order by ID

newID
100003
101029
...

База данных - Microsoft SQL Server 2000. Я в порядке с использованием расширений SQL.

Ответы [ 10 ]

15 голосов
/ 07 октября 2008
select ID +1 From Table t1
where not exists (select * from Table t2 where t1.id +1 = t2.id);

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

9 голосов
/ 06 октября 2008
SELECT (ID+1) FROM table AS t1
LEFT JOIN table as t2
ON t1.ID+1 = t2.ID
WHERE t2.ID IS NULL
5 голосов
/ 17 октября 2008

Это решение должно дать вам первое и последнее значения идентификатора "дыр", которые вы ищете. Я использую это в Firebird 1.5 на таблице записей по 500 КБ, и хотя это занимает немного времени, это дает мне то, что я хочу.

SELECT l.id + 1 start_id, MIN(fr.id) - 1 stop_id
FROM (table l
LEFT JOIN table r
ON l.id = r.id - 1)
LEFT JOIN table fr
ON l.id < fr.id
WHERE r.id IS NULL AND fr.id IS NOT NULL
GROUP BY l.id, r.id

Например, если ваши данные выглядят так:

ID
1001
1002
1005
1006
1007
1009
1011

Вы получите это:

start_id   stop_id
1003       1004
1008       1008
1010       1010

Хотелось бы получить полную оценку за это решение, но я нашел его по адресу Xaprb .

1 голос
/ 22 октября 2015

Недавно задумался над этим вопросом и похоже, что это самый элегантный способ сделать это:

SELECT TOP(@MaxNumber) ROW_NUMBER() OVER (ORDER BY t1.number) 
FROM master..spt_values t1 CROSS JOIN master..spt_values t2 
EXCEPT
SELECT Id FROM <your_table>
1 голос
/ 14 декабря 2013

Лучший способ - создать временную таблицу со всеми идентификаторами

Чем сделать левое соединение.

declare @maxId int
select @maxId = max(YOUR_COLUMN_ID) from YOUR_TABLE_HERE


declare @t table (id int)

declare @i int
set @i = 1

while @i <= @maxId
begin
    insert into @t values (@i)
    set @i = @i +1
end

select t.id
from @t t
left join YOUR_TABLE_HERE x on x.YOUR_COLUMN_ID = t.id
where x.YOUR_COLUMN_ID is null
1 голос
/ 02 июля 2013

из Как найти «пробел» в работе счетчика с SQL?

select
    MIN(ID)
from (
    select
        100001 ID
    union all
    select
        [YourIdColumn]+1
    from
        [YourTable]
    where
        --Filter the rest of your key--
    ) foo
left join
    [YourTable]
    on [YourIdColumn]=ID
    and --Filter the rest of your key--
where
    [YourIdColumn] is null
0 голосов
/ 23 октября 2017

Для людей, использующих Oracle, может использоваться следующее:

select a, b from (
  select ID + 1 a, max(ID) over (order by ID rows between current row and 1 following) - 1 b from MY_TABLE
) where a <= b order by a desc;
0 голосов
/ 17 декабря 2016

Многие из предыдущего ответа довольно хороши. Однако все они не могут вернуть первое значение последовательности и / или не учитывают нижний предел 100000. Все они возвращают промежуточные отверстия, но не самое первое (100001, если отсутствует).

Полное решение вопроса следующее:

select id + 1 as newid from
    (select 100000 as id union select id from tbl) t
    where (id + 1 not in (select id from tbl)) and
          (id >= 100000)
    order by id
    limit 1;

Номер 100000 должен использоваться, если первый номер последовательности равен 100001 (как в исходном вопросе); в противном случае он должен быть соответственно изменен «предел 1» используется для того, чтобы иметь только первый доступный номер вместо полной последовательности

0 голосов
/ 31 декабря 2013

Это даст вам полную картину, где 'Bottom' обозначает начало разрыва и 'Top' обозначает конец разрыва :

    select *
    from 
    ( 
       (select <COL>+1 as id, 'Bottom' AS 'Pos' from <TABLENAME> /*where <CONDITION*/>
       except
       select <COL>, 'Bottom' AS 'Pos' from <TABLENAME> /*where <CONDITION>*/)
    union
       (select <COL>-1 as id, 'Top' AS 'Pos' from <TABLENAME> /*where <CONDITION>*/
       except
       select <COL>, 'Top' AS 'Pos' from <TABLENAME> /*where <CONDITION>*/)
    ) t
    order by t.id, t.Pos

Примечание: первые и последние результаты НЕПРАВИЛЬНО и не должны рассматриваться, но их удаление сделает этот запрос намного сложнее, так что это будет делать сейчас.

0 голосов
/ 23 февраля 2010

Это решение не дает все дыры в таблице, только следующие свободные + первые доступные максимальные числа в таблице - работает, если вы хотите заполнить пропуски в идентификаторах, + получить бесплатный номер идентификатора, если у вас нет пробел ..

выберите numb + 1 из временного минус выберите numb from temp;

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...