2008-10-06 16 views
12

Recientemente heredé una base de datos en la que una de las tablas tiene la clave principal compuesta de valores codificados (Parte1 * 1000 + Parte2).
Normalicé esa columna, pero no puedo cambiar los valores anteriores. así que ahora tengoCómo encontrar "agujeros" en una tabla

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

Quiero encontrar los "agujeros" en la mesa (más precisamente, el primer "agujero" después 100000) para las nuevas filas.
Estoy usando la siguiente selección, pero ¿hay una mejor manera de hacerlo?

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

La base de datos es Microsoft SQL Server 2000. Estoy de acuerdo con el uso de extensiones de SQL.

+0

Justo por curiosidad, ¿cuál es la motivación para encontrar estos "agujeros"? ¿Es esta una llave inteligente? –

+0

La base de datos es para el sistema de control de acceso en mi trabajo. En la antigua base de datos Part1 era un código de la Compañía y Part2 era un código de Empleado. Si una persona cambia de compañía, se le debe emitir una nueva tarjeta. El sistema de lectura de tarjetas necesita 6 dígitos, por lo que las tarjetas nuevas comienzan en 100000, excluyendo las existentes. – pmg

Respuesta

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

no está seguro de si esta versión sería más rápida que la que usted ha mencionado al principio.

+1

Me gusta el aspecto de su versión. Comprobaré el plan de ejecución mañana por la mañana y te haré saber. – pmg

+1

No sé acerca de sus datos, pero en mi tabla similar (25,000 entradas, el primer hoyo está en la marca de 3,000 más o menos), la versión de la izquierda tomó aproximadamente un segundo de tiempo del reloj, esta corrió tanto que maté después de 2 minutos. –

+1

Mi tabla de datos reales tiene 17500 registros y MUCHOS agujeros. Mi selección e IronGoofy realizan exactamente lo mismo; La versión de Santiago es un poco más lenta, pero las 3, en mi sistema, devuelven los agujeros en un abrir y cerrar de ojos. – pmg

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

Esto funciona. Gracias Santiago. El Analizador de consultas, en el Plan de ejecución, dice que la versión de subselección es mejor porque usa una combinación de combinación en lugar de una combinación de memoria intermedia. – pmg

+1

Sí, siempre es mejor evitar las subconsultas si es posible. ¡Me alegra que ayude! –

+1

Esto no funcionó para mí en PostgreSQL, posiblemente debido a la forma en que funciona la selección de "id + 1", o posiblemente porque no es una clave primaria en el mío. –

4

Esta solución debe darle el primer y último valor de ID de los "agujeros" que está buscando. Utilizo esto en Firebird 1.5 en una tabla de 500K registros, y aunque toma un poco de tiempo, me da lo que quiero.

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 

Por ejemplo, si los datos se ve así:

ID 
1001 
1002 
1005 
1006 
1007 
1009 
1011 

que recibiría esto:

start_id stop_id 
1003  1004 
1008  1008 
1010  1010 

Me gustaría poder tomar todo el crédito por esta solución, pero me di al Xaprb.

+0

Tardó un poco en ejecutarse en una mesa grande a través de una conexión lenta con SQL2005, pero la espera valió la pena! –

0

Esta solución no da todos los huecos en la tabla, solo los siguientes gratuitos + primer número máximo disponible en la tabla - funciona si quiere completar los huecos en id-es, + obtenga un número de identificación gratuito si no lo hace tener una brecha ...

seleccionar insensible + 1 desde la temperatura menos seleccionar entumecimiento de la temperatura;

0

de 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

La mejor manera es la construcción de una tabla temporal con todos los ID de

de hacer una combinación izquierda.

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

Esto le dará una imagen completa, donde 'inferior' significa brecha empezar y 'Top' significa extremo del huelgo:

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 

Nota: Primero y Los últimos resultados son INCORRECTO y no debe ser considerado, pero sacarlos haría que esta consulta sea mucho más complicada, así que esto hará por ahora.

1

ha pensado en esta pregunta recientemente, y parece que esta es la forma más elegante de hacer eso:

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

Muchas de la respuesta anterior son bastante buenos. Sin embargo, todos fallan al devolver el primer valor de la secuencia y/o fallan al considerar el límite inferior 100000. Todos devuelven los agujeros intermedios pero no el primero (100001 si falta).

Una solución completa a la pregunta es la siguiente:

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; 

El número 100000 es para ser utilizado si el primer número de la secuencia es 100 001 (como en la pregunta original); de lo contrario es que modificarse en consecuencia "límite 1" se utiliza con el fin de tener sólo el primero que esté disponible en lugar de la secuencia completa

0

Para las personas que utilizan Oracle, se puede utilizar la siguiente:

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; 
Cuestiones relacionadas