Autonumérico entre varias tablas

Tenemos dos tablas de datos en SQL Server en las cuales se van a insertar filas indiscriminadamente. Necesitamos saber en qué orden fueron insertadas estas filas, poder sacar un UNION entre ambas tablas y ordenarla por su inserción. Es necesario además detectar ‘huecos’ en las filas, es decir, queremos crear un autonumérico pero que implique a ambas tablas.

Cómo sabemos, si utlizamos un campo integer la numeración es independiente en cada una de las tablas implicadas, así que no nos sirve. Un timestamp tampoco es la solución, ya que varía con cada versión de la fila: cada update que hagamos sobre la fila actualizará a su vez la columna timestamp.

La otra solución sería utilizar como clave un campo fecha, lo cual nos solucionaría el problema de unir las dos tablas pero no el de la detección de huecos. No habría forma a saber si entre dos filas de la tabla A hay alguna de la tabla B a no ser que hagamos la union directamente.

Desde 2005 tenemos una solución a este problema: newsequentialId(). Esta función sólo se puede utilizar como Default Constraint y nos devuelve un GUID distinto de cada vez, pero de forma secuencial.

En la imagen superior podemos ver el resultado de realizar dos inserciones en la tabla A, una en la B, y a continuación seguir insertando en la A. El byte menos significativo del GUID está representado por los dos primeros caracteres hexadecimales. Cómo se puede ver, son secuenciales E7, E8, falta el E9 y continua con EA, EB… La serie es muy sencilla de predecir: ECD0, EDD0, EED0, EFD0, F0D0, F1D0, …, FFD0, 00D1, 01D1 ,…

Objetivo cumplido: orden, unicidad y densidad! El código de prueba para generar las tablas de ejemplo es el siguiente:

create table #TablaA
(
  
IdNum int identity(1,1),
  
IdGuid uniqueidentifier default(NEWSEQUENTIALID()),
  
valor varchar(50)
);

create table #TablaB
(
   IdNum int identity(1,1),
  
IdGuid uniqueidentifier default(NEWSEQUENTIALID()),
  
valor varchar(50)
);

insert into #TablaA(valor)values (‘a’)
insert into #TablaA(valor)values (‘a’)
insert into #TablaB(valor)values (‘a’)
insert into #TablaA(valor)values (‘a’)
insert into #TablaA(valor)values (‘a’)

select * from #TablaA
select * from #TablaB

3 comentarios en “Autonumérico entre varias tablas”

  1. Hola. Permiteme un comentario que me ha venido a la cabeza con respecto a esta funcion.

    Las secuencias que se generen despues de reiniciar la maquina que hospeda el servidor siguen siendo unicas pero no tienen por que ser mayores que las generadas antes del reinicio.

    Por lo tanto, para unir las 2 tablas en una consulta con las caracteristicas de orden de insercion creo que habria que usar un campo sequentialId para detectar los “huecos” y uno de fecha para el orden de insercion.

    Listar con un ordenado primario por fecha y un ordenado secundario por idGuid en caso de coincidir las fechas te deberia devolver el resultado que comentas.

    ¿Tengo razon?, ¿Se me ha escapado algo y estoy diciendo tonterias?

    Saludos.

  2. Estoy de acuerdo contigo Crowley. No tenemos nada que hacer ante un reinicio de la máquina. Sin irnos tan lejos, un reinicio del servidor de SQL Server nos da al traste con la secuencia: siguen siendo mayores pero aparecen huecos debido a este reinicio.

    Si necesitamos estar preparados para esta casuística una posible solución es la que comentas.

    Una consulta UNION para estas dos tablas sería algo así:
    select * from
    (
    select *,’A’ tabla from TablaA
    union
    select *,’B’ from TablaB
    )u order by IdGuid

    Detectar los huecos es una buena pregunta, necesitamos una función que “reste” dos GUIDS, que si bien no es dificil de hacer es laboriosa. Nos arreglamos con una que sepa “sumar uno” a un GUID. Creo que lo voy a anotar para el siguiente post.

    A pesar de estos inconvenientes creo que es interesante el uso de esta función en algunos casos: por ejemplo, si necesitamos un guid para replicación pero no queremos penalizar las inserciones con una clave desordenada.

    ¡Gracias por vuestros comentarios, un saludo!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *