Actualiza, pero también selecciona
Todos los días se aprende algo nuevo, aunque sea a golpes. Hoy he descubierto un truco que me simplificará montones de líneas escritas en Transact SQL, para Microsoft SQL Server 7. Lo destacable del "truco" es que ha estado perfectamente documentado desde siempre, pero nunca había prestado la suficiente atención a los manuales como para darme cuenta. ¿Cuántos trucos "salvavidas" esperan por nosotros en las grises páginas de la documentación?
Al grano. Para ilustrar el truco utilizaré un ejemplo frecuente: queremos obtener un número secuencial a partir de una tabla con contadores; esta es la técnica apropiada, especialmente, cuando necesitamos que no haya saltos entre números, como puede suceder con las columnas de identidad. Lo usual es crear una tabla CONTADORES con una sola fila, como muestro a continuación:
create table CONTADORES (Proximo integer not null)
go
insert into CONTADORES values(1)
go
A continuación, suele proporcionarse un procedimiento para obtener el siguiente valor. Este mismo código puede también ejecutarse "directamente", sin necesidad de un procedimiento, pero este detalle no afecta al truco que quiero explicar:
create procedure ProximoCodigo @cod integer output as
begin
select @cod = Proximo
from CONTADORES holdlock
update CONTADORES
set Proximo = Proximo + 1
end
Resulta que este patrón de código es muy común en cualquier dialecto de procedimientos SQL. Uno lee cierto valor de determinado registro; a continuación, modifica dicho valor en ese registro. Y el valor leído originalmente es necesario más adelante, porque en caso contrario, nos habría bastado con un simple update.
Bien, pues lo que descubrí es que el procedimiento anterior también puede programarse del siguiente modo, con una simple instrucción:
create procedure ProximoCodigo @cod integer output as
update CONTADORES
set @cod = Proximo, Proximo = Proximo + 1
¡Efectivamente! Transact SQL permite asignaciones a variables locales y parámetros en la cláusula set de una actualización. Al parecer, SQL Server siempre evalúa primero las asignaciones a variables, y a continuación las asignaciones a columnas. Si lo desea, haga pruebas con el orden de las dos asignaciones, y comprobará que siempre la variable local termina con un valor inferior en 1 al de la columna de CONTADORES.
¿Y qué tal si lo que queremos, por el contrario, es el valor que se queda en la columna después de efectuada la actualización? Agárrese a la butaca, porque también es posible. Sólo tiene que apretarse la nariz para no oler el hedor del siguiente código asqueroso:
create procedure ProximoCodigo @cod integer output as
update CONTADORES
set @cod = Proximo = Proximo + 1
Ajá, puede ser todo lo sucio que usted quiera (y más), ¡pero no negará que puede también llegar a ser muy útil!
|