¿ Qué es un objeto Sequence y en que escenarios se usa ?

En este post voy a tratar brevemente sobre que es un objeto de base de datos Sequence, y en que escenarios se usa.

Focalizaremos nuestra atención en este post en analizar el uso básico de Sequence, las comparaciones principales entre Identity y Sequence, y las principales razones de requerimientos funcionales que justifican el uso de objetos Sequence, pero tratar en otro post el análisis de razones técnicas para optar por el uso de Identity o de Sequence en determinados tipos de escenarios.

Los puntos principales de este post son los siguientes:

  • ¿ Qué es un objeto de base de datos Sequence ?
  • ¿ Qué similitudes y diferencias existen entre Sequence y Identity ?
  • Escenarios típicos donde se usa Sequence

¿ Qué es un objeto de base de datos Sequence ?

Un Sequence es un objeto de base de datos que permite recuperar el próximo valor numérico entero de una secuencia de números, configurada con un valor mínimo, un valor máximo, un valor inicial y un valor de paso o incremento.

Un objeto Sequence posee algunos elementos parecidos a un Identity, pero a diferencia de este último, el Sequence es un objeto de base de datos independiente de una tabla, mientras que un Identity es una propiedad de un campo de una tabla.

Es importante no confundir Sequence con Identity, por lo tanto, es conveniente que revisemos la sintaxis de la sentencia de creación de un Sequence a través de algunos ejemplos.

Antes de entrar de lleno a los ejemplos, es necesario destacar que la secuencia de valores de un objeto Sequence es un conjunto de números que define el dominio de dicho objeto, donde el dominio es el conjunto de valores válidos.

Dado que la operación básica con un objeto Sequence es la de recuperación del próximo valor en la secuencia, debemos tomar aquellas decisiones de diseño que nos aseguren que la lógica de la solución que opera con un objeto Sequence (en base a solicitar el próximo valor de la secuencia), lo haga a sabiendas que el próximo valor a recuperar pertenece al dominio del objeto Sequence a invocar, porque de lo contrario, el objeto Sequence arrojará una exception por haber solicitado un próximo valor cuando el dominio del mismo ya está agotado.

La sintaxis mínima de creación de un objeto Sequence es así:

CREATE SEQUENCE UnTurno;

GO

Esta sentencia no indica ningún valor numérico (ni un valor inicial, ni un valor mínimo, ni un valor máximo, ni un valor de paso o incremento), por lo tanto, el engine aplica los defaults de la sentencia para ejecutar el comando.

En este caso, los defaults aplicados son los siguientes:

El tipo de dato asignado al Sequence creado (dado que el comando usado no define un tipo de dato en particular para el objeto Sequence) es bigint.

El paso o incremento asignado al Sequence creado (dado que el comando no define paso o incremento) es +1, es decir, por defecto, es un Sequence ascendente de incremento unitario.

En este caso, tanto el valor mínimo como el valor inicial asignado al Sequence creado (dado que el comando no define ninguno de los dos) se corresponden con el valor mínimo del data type para un Sequence ascendente, o el valor máximo del data type para un Sequence descendente. En este caso, como el data type es bigint y el Sequence es ascendente por defecto, el valor mínimo y el valor inicial de este Sequence es el valor mínimo del data type bigint, es decir, el valor inicial es el número entero -9.223.372.036.854.775.808.

El valor máximo asignado al Sequence creado (dado que el comando no define un valor inicial) es el valor máximo del data type para un Sequence ascendente, o el valor mínimo del data type para un Sequence descendente. En este caso, como el data type es bigint y el Sequence es ascendente por defecto, el valor máximo de este Sequence es el valor máximo del data type bigint, es decir, el valor máximo es el número entero 9.223.372.036.854.775.807.

En este caso de comando mínimo que opera con todos los defaults, Sequence no usa Caché de números de la secuencia, ni recicla cuando llega al final de la secuencia: en aquellos casos como este, donde los límites de la secuencia (valores inicial y final) coinciden con los límites del data type asociado a dicha sequencia, el Sequence no puede reciclar.

Ya mencionamos al inicio del post que nos focalizaremos en las razones de uso del Sequences basadas en requerimientos funcionales y dejaremos para otro post el análisis de razones técnicas, por lo tanto, en estos ejercicios veremos el uso de Sequence con reciclado de dominio, pero no veremos el uso de Sequence con Caché, dado que los pros y contras de usar Sequence con Caché son principalmente técnicas y de Quality Attributes.

Tal como es esperable, contamos con una System View, sys.sequences, para consultar sobre la metadata y el estado actual de cada objeto Sequence de una base de datos dada:

SELECT * FROM sys.sequences WHERE name = N’UnTurno’;

Al revisar los detalles del Sequence creado en esta System View, podemos verificar que indica que tanto el valor inicial del mismo es el número -9.223.372.036.854.775.808, y el valor mínimo es también dicho número. De igual forma, podemos revisar el resto de los datos de metadata (valor máximo, incremento, valor actual del Sequence) del Sequence recién creado.

Para que un Sequence sea realmente útil, tenemos que ver que comando usar para recuperar el próximo valor de dicha secuencia:

SELECT NEXT VALUE FOR UnTurno;

Vemos que el valor que retorna este comando para el Sequence recién creado se corresponde con el valor inicial, es decir, en este caso retorna el valor -9.223.372.036.854.775.808.

Queda claro que si al crear un objeto Sequence no le definimos en forma explícita que pertenece a un Schema existente dado, por defecto pertenece al Schema dbo.

Veamos ahora un ejemplo algo más completo, donde definimos en forma explícita el data type, valor inicial, y paso o incremento del Sequence (usamos el data model OLTP de AdventureWorks para SQL Server 2012 en los ejemplos restantes):

USE AdventureWorks2012

GO

IF EXISTS (SELECT * FROM sys.sequences WHERE name = N’UnTurno’)

     DROP SEQUENCE UnTurno;

GO

CREATE SEQUENCE UnTurno AS tinyint

     START WITH 0

     INCREMENT BY 100;

GO

— Cuarto Select ha de fallar

SELECT NEXT VALUE FOR UnTurno;

SELECT NEXT VALUE FOR UnTurno;

SELECT NEXT VALUE FOR UnTurno;

SELECT NEXT VALUE FOR UnTurno; — <= Falla!

Era predecible que la cuarta invocación del “NEXT VALUE FOR” debe arrojar una exception, dado que queda fuera del rango válido de valores (fuera del dominio) que pertenecen a la secuencia configurada en el Sequence creado en este ejercicio.

En relación con los data types que se pueden usar para definir en un Sequence, podemos usar todos los data types de tipo entero built-in que soporta SQL Server, así como todos aquellos alias data types que hemos creado que referencian a cualquiera de los data types enteros built-in (un ejemplo con el uso de Sequence con alias data type es un caso de razón principalmente técnica, y no lo veremos en este post).

A continuación veremos cómo crear un objeto Sequence capaz de retornar valores de una secuencia periódica, y en este caso, definimos en forma explícita tanto el data type, como el valor inicial, el valor mínimo, el valor máximo y el paso o incremento. Para lograr esto, vamos a ver un caso con reciclado.

USE AdventureWorks2012

GO

IF EXISTS (SELECT * FROM sys.sequences WHERE name = N’UnTurno’)

     DROP SEQUENCE UnTurno;

GO

CREATE SEQUENCE UnTurno AS tinyint

     START WITH 0

     INCREMENT BY 1

     MINVALUE 0

     MAXVALUE 3

     CYCLE;

GO

— Estos selects no presentan falla

SELECT NEXT VALUE FOR UnTurno;

SELECT NEXT VALUE FOR UnTurno;

SELECT NEXT VALUE FOR UnTurno;

SELECT NEXT VALUE FOR UnTurno;

SELECT NEXT VALUE FOR UnTurno;

SELECT NEXT VALUE FOR UnTurno;

SELECT NEXT VALUE FOR UnTurno;

SELECT NEXT VALUE FOR UnTurno;

SELECT NEXT VALUE FOR UnTurno;

SELECT NEXT VALUE FOR UnTurno;

Al ejecutar todos estos “NEXT VALUE FOR”, se aprecia claramente el comportamiento periódico o cíclico de la secuencia configurada en el Sequence creado en este ejercicio, y como los parámetros de configuración del mismo determinan dicho comportamiento y el rango de valores del dominio de dicho período.

¿ Qué similitudes y diferencias existen entre Sequence y Identity?

Tal como hemos visto al comienzo de este post, la principal diferencia entre un objeto Sequence y Identity, es que el objeto Sequence es un objeto independiente, mientras que un Identity es una propiedad de una columna de una tabla.

Otras diferencias importantes entre Sequence y Identity es que un objeto Sequence solamente puede retornar valores numéricos de data types enteros, mientras que una columna con propiedad Identity puede estar asociada tanto a data types enteros como a data types con decimales: las columnas Identity solo pueden tener valores enteros, pero el uso de data types como el decimal(x,y) permite tener un rango de valores válidos más amplio que con la mayoría de los data types enteros (el soporte de decimal(18,0) en columas Identity tenía sentido antes de la inclusión del data type bigint).

Una diferencia a destacar es que Sequence permite crear secuencias periódicas de valores numéricos, mientras que Identity solamente puede crear secuencias no periódicas.

Otra diferencia relevante es que Sequence se puede usar en muchos contextos, como por ejemplo, para asignar en forma directa un valor a una variable, formando parte de una expresión en SELECTs, INSERTs o UPDATEs, mientras que un Identity solamente puede asignarle valor a la columna asociada en INSERTs.

En cuanto a las similitudes, ambos permiten crear secuencias de números con un valor inicial, un valor máximo, un valor de paso o incremento, tanto secuencias ascendentes como descendentes.

Un ejemplo sencillo de uso de Sequence con INSERTs y UPDATEs es el siguiente:

USE AdventureWorks2012

GO

IF EXISTS (SELECT * FROM sys.sequences WHERE name = N’UnTurno’)

     DROP SEQUENCE UnTurno;

GO

CREATE SEQUENCE UnTurno AS tinyint

     START WITH 0

     INCREMENT BY 1

     MINVALUE 0

     MAXVALUE 3

     CYCLE;

GO

CREATE TABLE data1

(col1 int not null);

GO

INSERT data1

VALUES (NEXT VALUE FOR UnTurno);

GO 10

UPDATE data1

SET col1 = NEXT VALUE FOR UnTurno;

SELECT col1 from data1;

Escenarios típicos donde se usa Sequence

Vamos a concentrar nuestra atención en algunos ejemplos de requerimientos funcionales que son adecuadamente implementados a través de objetos Sequence.

Por ejemplo, el tipo de requerimiento funcional que claramente se implementa en forma adecuada mediante objetos Sequence es el manejo de turnos por orden de llegada.

En diversos lugares de atención al público o a clientes, como en oficinas públicas o en bancos, es común que el esquema de atención al público es por estricto orden de llegada de los clientes. En forma tradicional, para administrar este manejo y evitar problemas con los clientes, se usan talonarios con tickets pre-impresos con una secuencia de números.

Más recientemente, las organizaciones han mostrado interés en implementar sistemas que emitan los tickets en forma automática, por lo tanto, es conveniente contar con mecanismos robustos que permitan generar secuencias configurables de números similares a las existentes en los talonarios de números.

Es conveniente presentar un caso concreto para apreciar cuan conveniente es usar un Sequence como base de la implementación de un requerimiento funcional de este tipo. Supongamos que tenemos que implementar una solución que sea capaz de generar turnos por orden de llegada en base a tickets numerados secuenciales cíclicos, con una secuencia de números entre 1 y 100, periódica o cíclica, que después del 100 recicle al número 1 y repita la secuencia.

A nivel de la capa de acceso a datos (SQL Server), la implementación tendrá un aspecto similar al siguiente:

USE AdventureWorks2012

GO

IF EXISTS (SELECT * FROM sys.sequences WHERE name = N’UnTurno’)

     DROP SEQUENCE UnTurno;

GO

CREATE SEQUENCE UnTurno AS tinyint

     START WITH 1

     INCREMENT BY 1

     MINVALUE 1

     MAXVALUE 100

     CYCLE;

GO

— Estos selects permiten probar la implementación

SELECT NEXT VALUE FOR UnTurno;

GO 98;

SELECT NEXT VALUE FOR UnTurno;

SELECT NEXT VALUE FOR UnTurno;

SELECT NEXT VALUE FOR UnTurno;

Los SELECTs permiten ver como el Sequence ejecuta el reciclado y vuelve a repetir la secuencia de números del dominio en forma periódica, de tal forma que permite operar de igual forma que con un talonario de tickets pre-impreso que cuando se termina un talonario (con el Nro 100), se empieza a usar un nuevo talonario desde el comienzo, es decir, a partir del primer ticket que aparece, con el Nro 1.

 Con esto concluimos esta presentación introductoria del objeto de base de datos Sequence, donde se han expuesto ejemplos de escenarios funcionales que justifican su uso.

Saludos, GEN

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s