Common Table Expression (CTE): la sentencia WITH de SQL-Server

Las expresiones de tabla común (common table expression o CTE) fueron añadidas por Microsoft a SQL-Server a partir de la versión 2008. Cuando hablamos de CTE hablamos de un artefacto que nos mantiene en memoria el resultado de una consulta, que podremos llamar luego dentro de esa misma consulta. Esta cláusula también se puede utilizar en una instrucción CREATE VIEW como parte de la instrucción SELECT que la define.

Sintaxis de una CTE

La sintaxis básica de una CTE sería la siguiente:

WITH nombreDeNuestra_CTE (Columna1, Columna2, Columna3)  
AS  
-- Aquí definimos la consulta que la crea.  
(  
    SELECT Columna1, Columna2, Columna3
    FROM BaseDatos.dbo.Tabla
    Where Condicion=Condicion
)  

Por ejemplo, imagina que tenemos una tabla con vendedores y otra con sus ventas, donde el Id del vendedor actúa como clave externa en cada línea de ventas. Podemos usar una CTE para tener un resultado temporal con su total de ventas de 2018. Algo así:

WITH Ventas_CTE (IdVendedor, NombreVendedor, TotalVendido)  
AS  
(  
    SELECT 
      v.Id as IdVendedor, 
      v.Nombre as NombreVendedor, 
      SUM(vt.Importe) AS TotalVentas 
    FROM Vendedores v inner join ventas vt on v.Id = vt.IdVendedor
    WHERE YEAR(vt.Fecha)=2018
)  

Ahora podríamos usar la CTE del ejemplo de arriba en una consulta como si de una tabla normal se tratase. Por ejemplo, podríamos sacar todos los departamentos donde haya vededores que hayan superado los 100.000 euros en ventas haciendo un join contra una tabla de departamentos:

SELECT Distinct
  d.Nombre
FROM
  Departamentos d
inner join
  Departamentos_Vendedores vd
on
  d.Id = vd.Idep
inner join
  Ventas_CTE v
on
  v.IdVendedor = vd.IdVend
where
  vd.TotalVendido > 100000

Hay que recordar que las cláusulas INTO, ORDER BY, FOR BROWSE y OPTION no están permitidas en una consulta de definición de una CTE. Sí permite el uso de operadores de conjuntos como UNION ALL, UNION, INTERSECT o EXCEPT y también permite referencias a tablas externas, e incluso a tablas situadas en servidores remotos.

En el ejemplo hemos usado un SELECT después de crear nuestra CTE, pero podríamos haber usado una instrucción INSERT, DELETE o UPDATE también. Incluso la podemos utilizar para definir un cursor.

¿Por qué usar una CTE?

Aparte del potencial que tienen las CTE por permitir crear una CTE recursiva, cosa de la que ya hablaremos más adelante en otro artículo, las CTE principalmente nos permiten sustituir a subconsultas y a variables de tabla.

En el caso de las subconsultas realmente la CTE no nos da ninguna ventaja en el rendimiento, pero sí nos permitirá tener un código más ordenado y más manejable, lo que facilita la legibilidad del mismo y las tareas de mantenimiento.

En el caso de la variables de tipo tabla ahí sí que las CTE nos dan un rendimiento mejor en la consulta, por lo que siempre serán una opción más recomendable.

También uno podría pensar en cambiar una Vista por una CTE. Si se trata de conjuntos de datos muy grandes, con muchas líneas, la Vista tendrá un rendimiento mayor por tratarse de un objeto creado en la base de datos que no permite definir índices (ya hablamos aquí sobre cuándo esto es recomendable y cuándo no).

MySQL: Formateando fechas con DATE_FORMAT()

La función DATE_FORMAT() de MySQL, que fue incorporada en la versió 4.0 del conocido SGBD, nos permite dentro de un select definir el formato de salida de una fecha. La función recibe dos parámetros: una fecha o campo de fecha y una cadena de texto con el formato.

Los siguientes valores pueden ser usados en el formateo de fecha:

%aNombre del día abreviado (Sun a Sat)
%bNombre de mes abreviado (Jan a Dec)
%cMes en formato número (0 a 12)
%DDía del mes en formato numérico seguido de sufijo numeral (1st, 2nd, 3rd, …)
%dDía del mes en formato numérico, forzando dos dígitos  siempre ( de 01 a 31)
%eDía del mes en formato numérico, sin forzar dos dígitos (1 a 31)
%fMicrosegundos (000000 to 999999)
%HHora en formato 24 horas, con dos dígitos
%hHora en formato 12 horas, dos dígitos
%IHora en formato 12 horas, dos dígitos
%iMinutos
%jDía del año (001 a 366)
%kHora en formato 24 horas, sin forzar los dos dígitos
%lHora en formato 12 horas, sin forzar los dos dígitos
%MNombre del mes (January a December)
%mMes en formato numérico, forzando dos dígitos.
%pAM o PM
%rHora en formato 12 horas AM o PM (hh:mm:ss AM/PM)
%SSegundos (00 a 59)
%sSegundos (00 a 59)
%THora en formato 24 horas (hh:mm:ss)
%USemana, tomando el domingo como primer día (00 a 53)
%uSemana, tomando el lunes como primer día (00 to 53)
%VSemana, tomando el domingo como primer día (01 a 53). Usado con %X
%vSemana, tomando el lunes como primer día (01 a 53). Usado con %X
%WNombre del día completo (Sunday to Saturday)
%wNúmero del día en la semana, siendo el domindo 0 y el sábado 6.
%XAño para la semana, tomando el domingo como primer día. Usado con %V
%xAño para la semana, tomando el lunes como primer día. Usado con %V
%YAño en formato de cuatro dígitos
%yAño en formato de dos dígitos

Veamos unos ejemplos si quieres probarlo en casa:

Select DATE_FORMAT("2018-11-20", "%d/%m/%Y"); 
#Pintaría 20/11/2018, el formato europeo
Select DATE_FORMAT("2018-11-20", "%V %X"); 
#Pintaría 46 2018
Select DATE_FORMAT("2018-11-20", "%j-%y"); 
#Pintaría 324-2018
Select DATE_FORMAT("2018-11-20", "%W %d %M"); 
#Pintaría Tuesday 20 November

En el ejemplo le hemos pasado una cadena con una fecha como primer parámetro, pero puedes probar una consulta sobre un campo de una tabla que almacena alguna fecha.

Crear y borrar una vista en SQLite

¿Qué es una vista?

Una vista es una consulta cuyo resultado se presenta como una tabla, puesto que tienen la misma estructura: filas y columnas. La diferencia es que sólo se almacena de ellas la definición y no los datos, que se extraen de las otras tablas de la base de datos. En SQLite la vistas son de solo lectura, de forma que no se pueden ejecutar instrucciones INSERT, DELETE o UPDATE sobre ellas para actualizar las tablas.

¿Cómo creo una vista en SQLite?

Para crear una vista en SQLite tenemos la instrucción CREATE VIEW. Tendremos que asignar un nombre a la vista que creamos y ejecutar una consulta que nos devuelva los datos que formarán nuestra vista. La sintaxis sería:

CREATE VIEW Vista_Nueva 
AS 
SELECT
	t1.Id,
        t1.Campo1,
        t2.Campo1 as Campo2
FROM
	tabla t1
INNER JOIN tabla2 t2 ON t1.IdEnTabla2 = t2.Id;

Vamos a poner como ejemplo que tenemos una tabla con variedades de quesos, otra con países y otra con tipos de leche. Y queremos una vista que nos muestre de qué país y con qué tipo de leche está hecho cada queso. Pues podríamos sacarlo con una consulta así:

CREATE VIEW v_QuesosDetallada 
AS 
SELECT
	q.Id,
        q.Nombre,
        p.Nombre as Pais,
        l.Descripcion as TipoLeche
FROM
	Quesos q
INNER JOIN paises p ON q.IdPais = p.Id
INNER JOIN leches l ON q.IdTipoLeche = l.Id;

¿Cómo borro una vista?

Para eliminar una vista en SQLite tenemos la instruccion DROP VIEW, que iría seguida del nombre de la tabla. Siguiendo con el ejemplo de arriba, para borrar la vista v_QuesosDetallada tendríamos que ejecutar la siguiente instrucción:

DROP VIEW v_QuesosDetallada;

De esta forma eliminaríamos la vista de nuestra base de datos.

Si intentamos crear una vista con un nombre que ya existe o si intentamos borrar una que no existe recibiremos un mensaje de error. Por tanto es interesante asegurarse utilizando IF NOT EXISTS a la hora de crear e IF EXISTS a la de borrar.

Calcular la edad a partir de una fecha en SQLite.

Otra entrada sobre SQLite, vendrán algunas más ya que he tenido que estudiar un poco sobre ello para utilizarlo en una aplicación. Y ya que nos ponemos con consultas y truquitos vamos con el siguiente: ¿Cómo calculamos la edad a partir de una fecha? Ya lo vimos en el pasado con SQL-Server, con MySQL y con PostgreSQL, incluso fuera del tema de las bases de datos también lo hicimos con Javascript.

Supongamos que tenemos una base de datos Usuarios con un campo FechaNacimiento donde almacenamos, como te puedes imaginar, una fecha. Para calcular la edad a día de hoy podemos hacer dos cosas:

La primera es el clásico “Restamos un año a otro y luego si el día del cumpleaños todavía no llegó restamos uno año más“, que podríamos expresar así en una consulta:

SELECT (strftime('%Y', 'now') - strftime('%Y', FechaNacimiento )) - (strftime('%m-%d', 'now') < strftime('%m-%d', FechaNacimiento )) from Usuarios;

Como ves, en primer lugar extraemos los años de las dos fechas y los restamos. En la segunda resta añadimos la compración entre mes y día actual y mes y día de la fecha, dicha evaluación devolverá 1 si es anterior y 0 si no lo es, de esta forma resta 1 en ese caso.

Otra solución, convertir ambas fechas a un formato numérico y restarlas, y después convertir a un formato de número entero:

Select cast(strftime('%Y.%m%d', 'now') - strftime('%Y.%m%d', FechaNacimiento ) as int) from Usuarios;

SQL-Server: Usar Try…Catch en una transacción.

Desde SQL-Server 2008 es posible utilizar la instrucción Try…Catch. Estas instrucciones nos permiten implementar un mecanismo de control de errores: metemos un bloque de código dentro de la instrucción Try, para intentar ejecutarlo, y dentro de Catch metemos las instrucciones para controlar la respuesta en caso de error.

La sintaxis básica de un Try…Catch en SQL-Server es la siguiente:

BEGIN TRY  
     ---Bloque de código 
END TRY  
BEGIN CATCH  
     --Código en caso de error. 
END CATCH  

Cuando realizamos una transacción podemos exprimir al 100% la instrucción Try…Catch. La idea es la siguiente: Comenzamos la transacción, intentamos una acción en un bloque Try. En caso de que falle mostramos el error y ejecutamos un rollback para anular la transacción. En caso de éxito confirmamos la ejecución de la transacción.

Veamos un código de ejemplo:

BEGIN TRANSACTION;  --Comienza

BEGIN TRY  --Aquí empieza el try
    Insert into Ejemplo(Id,Nombre) values(22,'Manuel');
    Delete From Espera Where Nombre = 'Manuel' 
--Intentaremos esas dos acciones
END TRY  
BEGIN CATCH  
--El primer paso en el Catch
--Será recoger y mostrar
--Todos los errores
    SELECT   
        ERROR_NUMBER() AS ErrorNumber  
        ,ERROR_SEVERITY() AS ErrorSeverity  
        ,ERROR_STATE() AS ErrorState  
        ,ERROR_PROCEDURE() AS ErrorProcedure  
        ,ERROR_LINE() AS ErrorLine  
        ,ERROR_MESSAGE() AS ErrorMessage;  
--Si hay transacción abierta
--Hacemos un rollback sobre ella
--Para anularla
    IF @@TRANCOUNT > 0  
        ROLLBACK TRANSACTION;  
END CATCH;  
--Fuera del bloque vamos a
--comprobar que haya transacción abierta.
--Si la hay es que no tuvo que ir por el CATCH
--Por tanto confirmamos.
IF @@TRANCOUNT > 0  
    COMMIT TRANSACTION;  
GO  

SQL-Server: Intercambiar valores 0 y 1 en un Update

Vamos con una cuestión que seguro que nos hemos encontrado más de una vez: Tenemos un campo en una base de datos donde almacenamos un valor verdadero o falso y tenemos que realizar un Update para cambiar dicho estado. ¿Cómo hacemos para modificarlo con el menor costo posible? Te doy un par de soluciones, todas válidas para SQL-Server desde la versión 2008 hasta la 2017 (y supongo que seguirán valiendo en las posteriores):

Para la primera vamos a usar la operación del OR exclusivo bit a bit:

Update TablaEjemplo SET ValorVF = Convert(Bit,ValorVF ^ 1)

Otra opción lógica es usar el operador lógico de negación para invertir el valor:

Update TablaEjemplo SET ValorVF = Convert(Bit,~ ValorVF)

¿Y qué pasa si la tabla no está bien diseñada y en lugar de almacenar un valor bit estamos almacenando un entero? Si no podemos cambiar el diseño de la tabla todavía podemos recurrir a la primera solución, el OR exclusivo bit a bit, pero sin realizar la conversión a tipo bit:

Update TablaEjemplo SET ValorVF = ValorVF ^ 1

Y además tenemos otra opción, con una simple resta y con la función ABS(), que nos devuelve el valor absoluto del parámetro que recibe:

Update TablaEjemplo SET ValorVF = ABS(ValorVF - 1)

O que é a Tríade CID?

Esta é uma tradução ao português dum artigo antigo. Cá uma ligação ao orixinal.

A Tríade CID não tem relação com poemas medievais castelhanos nem com as bandas desenhadas do Hernández Palacios, mas é um conceito da cibersegurança que faz ênfase em três princípios que têm de trabalhar em conjunto para garantir a segurança de um sistema informático: Confidencialidade, Integridade e Disponibilidade.

 

  1. Confidencialidade: Os dados têm de estar só na mão dos utentes autorizados. Umas políticas de controlo do acesso têm de ser aplicadas para evitar que a informação classificada cair em mãos, seja ou não de forma intencionada, de utentes sem acesso.
  2. Integridade: O conceito integridade faz referência  a que os dados que temos armazenados sejam corretos e completos. Tem de se poder garantizar que os dados não foram mudados de forma não autorizada, que não é possível a perda destes dados é que os mesmos são consistentes, isto é que a informação é mesmo correcta à que temos no mundo exterior.
  3. Disponibilidade: Afinal, o conceito disponibilidade implica que a informação tem de estar acessível sempre para os utentes autorizados num tempo razoável. Isto é que os dados têm de estar sempre disponíveis para os utentes.

Na altura de desenhar uma estratégia de cibersegurança para uma empresa ou simplesmente na altura de armazenar qualquer coleção de dados confidenciais um deve considerar que estes três pontos têm de ser atingidos.