Ir al contenido principal

SEQUENCE, nuevo objeto en SQL Server 2012 ¿Sustituto de Identity?

SQL Server 2012 lleva tan solo unas semanas entre nosotros y, como ocurre con cada nueva versión de un producto, lo primero en lo que nos hemos fijado es en sus novedades.

En lo que se refiere a su lenguaje de programación, el Transact SQL, la que más nos llama la atención es la introducción de las Secuencias, o SEQUENCE.



Un secuencia es un nuevo tipo de objeto que, según nos cuenta la MSDN, "genera una secuencia de números de acuerdo a la especificación con la que fue creada". Dicha especificación recoge el tipo de datos (entero), el valor inicial, el incremento, el rango de la secuencia y si la secuencia es cíclica o no. Este es el código para generar una secuencia:

CREATE SEQUENCE [schema_name . ] sequence_name
    [ AS [ built_in_integer_type | user-defined_integer_type ] ]
    [ START WITH  ]
    [ INCREMENT BY  ]
    [ { MINVALUE [  ] } | { NO MINVALUE } ]
    [ { MAXVALUE [  ] } | { NO MAXVALUE } ]
    [ CYCLE | { NO CYCLE } ]
    [ { CACHE [  ] } | { NO CACHE } ]
    [ ; ]

Una aplicación puede hacer referencia a una secuencia solamente para obtener su siguiente valor. Así podemos garantizarnos, de una forma sencilla, la obtención de números consecutivos no repetidos. De tal forma, una aplicación puede solicitar el próximo valor de la secuencia y usarlo para insertarlo en cualquier campo del mismo tipo de datos de cualquiera de nuestras tablas en la base de datos.

¿Cuál es el propósito de todo esto?


Muchos usuarios de SQL Server hemos tenido que recurrir a la propiedad IDENTITY de las columnas para generar una clave numérica única y autoincrementada. Dicha propiedad tiene la desventaja de que solamente genera el nuevo número cuando un registro es insertado, por lo que es imposible conocerlo y, por tanto, reusarlo desde la aplicación cliente sin evitar hacer una nueva consulta a la base de datos (o usando la cláusula OUTPUT); en el caso en que nos sea posible identificar el registro insertado por nosotros.

La alternativa era prescindir de la propiedad IDENTITY y delegar en nuestra aplicación la generación de nuevas claves únicas, típicamente de la forma:
  1. Obtener el valor máximo en dicha columna
  2. Generar el siguiente valor sumándole 1
Dicho método obliga a tu aplicación a tener control de concurrencia (para que nadie pida el mismo número que tú) y la hace propensa a errores de violación de clave primaria (si alguien pidió el mismo número que tú, seguramente intentará insertar ese número más uno en la base de datos, igual que tu aplicación).

En cualquier caso, conocer el valor a insertar con anterioridad suele ser de vital importancia cuando éste ha de insertarse en múltiples tablas -como clave primaria en una y ajena en las demás, por ejemplo.

Con las secuencias se solucionan estos problemas. Nuestra aplicación solicitaría el siguiente valor a la misma, y lo usaría para insertarlo en tantas tablas como considerase necesario. Si alguien quisiese insertar más registros a la vez que nosotros, la secuencia devolvería los siguientes valores, independientemente de cuál fuese el máximo valor para la columna en la base de datos o de los valores que hubiesen recuperado los demás usuarios, y de si finalmente han realizado inserciones o no.

Un ejemplo típico de creación de una secuencia destinada a ser usada para generar valores únicos e insertarlos en un campo de una tabla podría ser el siguiente:

CREATE SEQUENCE Schema.SequenceName
    AS int
    START WITH 1
    INCREMENT BY 1 ;

Por otro lado, las secuencias pueden ser utilizadas para, como su propio nombre indica, generar una secuencia de números como columna de un conjunto de resultados, de un modo similar al que hace ROW_NUMBER, ya que admite ordenación, rangos, offset inicial, reinicios de la secuenca e incluso añade la posibilidad de intervalos distintos de 1 con la cláusula INCREMENT BY.

¿Y los inconvenientes?


Si nuestra intención es sustituir columnas con la propiedad IDENTITY por columnas que usen secuencias para ser rellenadas por una clave numérica única, hay que prestar atención a algunas desventajas con las que nos podemos encontrar.

Para comenzar, el hecho de que una secuencia sea un objeto independiente de la tabla en la base datos la expone a ser reiniciada, modificada o borrada sin que la tabla o tablas que la usan sean conscientes de ello. Un instrucción como la siguiente bastaría para invalidar el uso de nuestra secuencia:

ALTER SEQUENCE SequenceName
RESTART WITH 1 ;

El segundo inconveniente deriva de la misma raíz. Nadie puede obligar a un usuario a usar una secuencia para obtener el siguiente valor a insertar en una columna. Aunque sea por el simple desconocimiento de la existencia de la misma. Si el usuario inserta un nuevo registro con el siguiente valor, la secuencia habrá quedado desincronizada con el campo para el que debía generar valores únicos.

Finalmente, nadie nos asegura que el tipo de datos definido para la columna sea el mismo con el que se ha definido la secuencia. Si la columna es de tipo int y la secuencia es smallint, muy pronto acabaremos encontrándonos con un error de violación de clave primaria, una vez la secuencia haya reiniciado su ciclo (si así la hemos configurado) o una excepción, en el caso de que haya alcanzado su valor máximo.

Así pues, queda mucho por descubrir todavía sobre cuál y cómo será el uso óptimo de las secuencias en SQL Server. Si ya has hecho uso de ellas, no dudes en contarnos tu experiencia.

Comentarios

Entradas populares de este blog

Aprendiendo a usar LEFT OUTER JOIN

En esta entrada pretendemos explicar los diferentes resultados obtenidos por distintas construcciones de consultas que, aparentemente, deberían producir el mismo conjunto de resultados. Así, veremos las diferencias entre filtrar los resultados de una query en la unión (Join) mediante condiciones ON y mediante cláusulas WHERE.

Variantes del SELECT COUNT con DISTINCT

Seguramente, muchos de vosotros habréis usado en innumerables ocasiones la función de T-SQL COUNT , que no hace sino devolver un número de registros: de una tabla, de un conjunto de resultados, etc... En una de sus aplicaciones, combinado con el DISTINCT -uno de los dos argumentos que admite- COUNT nos devuelve el número de valores únicos no nulos de la tabla o conjunto de resultados que estemos consultando. Pero ¡ojo! Cuidado con la sintaxis , o podemos obtener el valor equivocado sin darnos cuenta. No es lo mismo: SELECT COUNT (DISTINCT NombreCampo) FROM NombreTabla que: SELECT COUNT(*), DISTINCT NombreCampo FROM NombreTabla

Script para obtener el tamaño de todas las tablas de la base de datos

En algunas ocasiones podemos vernos con la necesidad de conocer qué tablas de nuestra base de datos están ocupando más espacio en disco. Por ejemplo, si disponemos de SQL Server Express , cuyas bases de datos están limitadas a 4GB o 10GB, según la versión que estemos usando -4, hasta 2005; 10, a partir de 2008-, aparte de usar las opciones de comprimir la base de datos, poner el log en el modo simple de recuperación o ajustar las políticas de crecimiento automático de nuestros ficheros, podemos necesitar averiguar qué tablas crecen más para tomar las decisiones oportunas.