Problema: Una tabla tiene un campo numérico que tiene valores en secuencia, pero algunos faltan. Quiero una consulta que me de el primer "agujero" o vacante en la columna.
Un primer acercamiento es seleccionar todos los valores que no pertenecen a la tabla cuyos valores tienen su sucesor inmediato en la misma tabla. Por ejemplo, si la tabla tiene los valores 1, 2, 3, 5, y 7, entonces 1 y 2 tienen su sucesor presente en ella, y 3, 5 y 7 no. Entonces, si 3 no tiene sucesor, 3 + 1 = 4 no está. En SQL:
select a+1 from A
where a not in (select x.a from A x, A y where y.a = x.a+1)
Ahora bien, hay una solución mejor, usando outer joins. La idea es buscar todos los pares de números en la tabla junto con su sucesor,
si es que existe. Si
no existe, entonces tenemos el valor que buscamos, menos uno:
select isnull(0*y.a,x.a+1) from A x, A y
where x.a *= y.a-1
Si
x.a = y.a-1, y.a es el sucesor no nulo de
x.a, por lo tanto
0 * y.a = 0. Si no existe
y.a sucesor de
x.a, y.a es
NULL, 0 * y.a = NULL y por lo tanto el resultado del
isnull es
x.a+1, es decir, la vacante.
Pero nuestro problema original era encontrar la
mínima vacante, el mínimo de entre los sucesores que no existen; y el problema es encontrar alguna manera de filtrar el 0. La función que devuelve H cuando z = 0, y 0 si no, es H*(1 - abs(sign(z))); si sabemos que z es no negativo, la función se simplifica a H*(1 - sign(z)). Si H es lo suficientemente grande, nos aseguramos de que
min() elegirá sólo los valores que nos interesan; por ejemplo, podemos usar el máximo número positivo representable en un
int. Usando H = 231-1 = 2147483647, nuestra consulta es:
select min(2147483647 * (1 - sign(z)) + z)
from A x, A y
where x.a *= y.a-1
donde
z = isnull(0*y.a,x.a+1) es la solución que buscamos.
Notar de paso que la suma no genera overflow aritmético.