2008-10-06 2 views
12

Недавно я унаследовал базу данных, на которой одна из таблиц имеет первичный ключ, состоящий из закодированных значений (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.

+0

Просто из любопытства, какова мотивация поиска этих «дыр»? Это интеллектуальный ключ? – 2008-10-06 15:32:53

+0

База данных предназначена для системы контроля доступа на моей работе. В старой базе данных Part1 был кодом компании, а Part2 был кодом Employee. Если человек переключил компании, ему придется выпустить новую карточку. Система чтения карт требует 6 цифр, поэтому новые карты начинаются с 100000, за исключением существующих. – pmg 2008-10-06 15:57:58

ответ

12
select ID +1 From Table t1 
where not exists (select * from Table t2 where t1.id +1 = t2.id); 

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

7
SELECT (ID+1) FROM table AS t1 
LEFT JOIN table as t2 
ON t1.ID+1 = t2.ID 
WHERE t2.ID IS NULL 
+2

Это работает. Спасибо, Сантьяго. Query Analyzer в Плане выполнения говорит, что версия подзаголовка лучше, потому что она использует Merge Join, а не Hash Match. – pmg 2008-10-06 16:09:09

+1

Да, всегда лучше избегать подзапросов, если это возможно. Рад помочь! – 2008-10-06 16:15:52

4

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

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.

0

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

выбрать numb + 1 от temp минус выбрать numb from temp;

0

из How do I find a "gap" in running counter with 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 
1

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

чем сделать присоединиться к левой.

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 
0

Это даст вам полную картину, где '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 

Примечание: Первый и Последние Результаты поиска WRONG, и его не следует рассматривать, но изъятие их сделает этот запрос намного сложнее, так что теперь это будет сделано.

1

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

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> 
0

Многие из предыдущего ответа достаточно хорошо. Однако все они пропускают, чтобы вернуть первое значение последовательности и/или пропустить, чтобы рассмотреть нижний предел 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

Для людей, использующих 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; 
Смежные вопросы