SQL-Server ¿Es mejor usar IN o BETWEEN?

Otro tip rápido de SQL-Server ¿Es mejor hacer uso de IN o de BETWEEN? Bueno, en este caso la respuesta es rápida: debemos usar BETWEEN siempre que sea posible. Veamos dos consultas:

//primero con in 
Select Nombre from Pacientes where Id IN (3000,3001,3002,3003,3004);
//ahora between
Select Nombre from Pacientes where Id BETWEEN 3000 AND 3004

¿Por qué es mejor usar BETWEEN? Bueno, si no existe un índice para el campo sobre el que ejecutamos la búsqueda no notaremos mucho cambio, pero si hay índices la respuesta de BETWEEN será mucho más rápida. Siempre que se trate de buscar un resultado dentro de un rango BETWEEN será la mejor opción. ¿Cuándo debemos usar IN? Pues cuando busquemos un resultado dentro de un conjunto que no sea consecutivo. Por ejempolo en una situación como la siguiente:

//En este caso IN es lo correcto
Select Nombre from Pacientes where Id IN (3000,4001,5002,6003,8004);

SQL-Server: Rendimiento de los operadores en el WHERE

Un tip rápido sobre SQL-Server. De cara a mejorar el rendimiento de las queries es importante considerar qué operadores dentro del WHERE tienen mejor rendimiento:

  • La mejor respuesta siempre la conseguiremos usando el operador =
  • Después vendrían los operadores >, >=, <, <=
  • Tras esos estaría el LIKE
  • Finalmente tendríamos con el peor rendimiento el <> o los operadores !=, !>,!< que no forman parte de la especificación de TSQL.

Por eso recuerda que es importante construir las consultas de forma correcta para usarlos de la forma más optimizada posible.

 

SQL: Formas Normales

Recuperados ya del fin de semana de rock and roll vamos a volver al código con algo de SQL teórico: la normalización de datos. El diseño lógico de las tablas y sus relaciones es fundamental para la optimización de una base de datos. Existen cinco reglas de normalización que debemos cumplir para aseverar que nuestra base de datos SQL está normalizada, aunque es cierto que hay quien considera que la cuarta y la quinta son rizar el rizo y que con cumplir la tres primeras generalmente ya tenemos una base de datos normalizada. Estas son las cinco formas normales:

  1. Primera Forma Normal: La primera forma requiere la eliminación de todas las columnas duplicadas de una tabla, la separación en otras tablas de esos datos que se duplicarían y la identificación de cada tabla con una atributo de clave primaria. Por ejemplo, si tenemos una tabla donde registramos ventas no guardamos el nombre y el precio del producto vendido en ella varias veces, sino que tendremos una tabla de productos con esos datos separado de la tabla de ventas, y ambas tendrían claves primarias que identifican a cada fila.
  2. Segunda forma Normal: Implica que se cumpla lo dicho en la primera forma normal y que, además, se creen relaciones entre tablas a través de claves externas. Es decir, la tabla Ventas del ejemplo anterior incluye como Clave Externa un valor único que lo relaciona con la tabla Productos, generalmente su clave primaria.
  3. Tercera Forma Normal: Esta norma implica que se cumplan las dos anteriores y que, además, todas las columnas de un registro deben hacer referencia únicamente a la clave primaria, y además elimina todos los atributos derivados. Volvemos al ejemplo: la tabla Productos nos dará el nombre del producto, su precio, en qué almacén se guarda y su fecha de caducidad. No tendremos, por ejemplo, un registro para decir en qué piso del almacén se guarda, ya que ese dato sería de la tabla Almacén. Tampoco tendríamos una columna con los días que faltan hasta que caduque, ya que ese sería un atributo derivado que podemos calcular con la fecha de caducidad.
  4. Cuarta Forma Normal: Agrega un requisito adicional, que es la eliminación de cualquier dependencia multivaluada en las relaciones. Una tabla con una dependencia multivaluada es una donde la existencia de dos o más relaciones independientes muchos a muchos causa redundancia.
  5. Quinta Forma Normal: Rizando el rizo, vendría a decir que sólo se podrían realizar relaciones entre tablas utilizando claves candidatas, con la idea de reducir la redundancia de datos entre múltiples tablas.

A la hora de normalizar bases de datos hay una frase en inglés que lo resume todo: «The key, the whole key, and nothing but the key.» En la propia web de Microsoft la primera recomendación que dan para mejorar el rendimiento de SQL-Server y tener un diseño eficiente de la base de datos es usar un índice autonumérico como clave primaria de cada tabla, identificando así de forma unívoca cada registro y facilitando la relación entre ellos.

Abrir SQL-Server Management Studio desde el intérprete de comandos

¿Cómo abro el Management Studio de SQL-Server desde el terminal de comandos? Pues si es el de SQL-Server 2005 ejecutando sqlwb.exe

sqlwb.exe

Acepta varios parámetros:

  • -S: La instancia a la que nos conectamos
  • -d: La base de datos a la que nos conectamos
  • -E: Usar autenticación de Windows
  • -U: Usuario de SQL-Server
  • -P: Contraseña del usuario
  • [file_name[, file_name]]: Ficheros a cargar

Para SQL-Server 2008 o SQL-Server 2012 se usa SSMS.exe

SSMS.exe

La lista de parámetros que acepta es la misma que en el 2005, podéis verla arriba.

SQL-Server: Crear una tabla auxiliar con Fechas comprendidas entre dos fechas dadas

A veces necesitamos una tabla calendario. De hecho muchas veces la necesitamos. ¿Cómo podemos crear una tabla ad-hoc con todas las fechas comprendidas entre dos fechas dadas en SQL-Server? Pues de la siguiente forma:

DECLARE @FechaInicio date
DECLARE @FechaFin date

--En este punto le doy valor a las fechas

Select @FechaInicio = '01/01/2017'
Select @FechaFin = '01/07/2017'

--Aquí creamos la variable tabla con los valores
DECLARE @dim TABLE ([Fecha] DATE)

	INSERT @dim([Fecha])
	SELECT d
	FROM
	(
	  SELECT
		  d = DATEADD(DAY, rn - 1, @FechaInicio)
	  FROM 
	  (
		  SELECT TOP (DATEDIFF(DAY, @FechaInicio, @FechaFin)) 
			  rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
		  FROM
			  sys.all_objects AS s1
		  CROSS JOIN
			  sys.all_objects AS s2
		  ORDER BY
			  s1.[object_id]
	  ) AS x
	) AS y;

--Aquí hacemos un select sobre dim para ver el resultado
Select Fecha from @dim

SQL Server: Obtener la primera o la última palabra de una cadena.

El caso: tenemos una cadena de caracteres en SQL-Server (2008 R2 para más señas), donde las palabras están separadas por un caracter concreto. ¿Cómo obtenemos la primera palabra mediante una consulta? ¿Y la última?

La primera es fácil, sólo tenemos que hacer un SUBSTRING() que llegue hasta la primera aparición del caracter separador, que en este caso es un espacio:

DECLARE @test NVARCHAR(255)
SET @test = 'Esto es una frase'
Select SUBSTRING(@test, 1, CHARINDEX(' ', @test, 1) - 1)

¿Y la última? La cosa se torna compleja en SQL-Server 2008 (no se si en versiones posteriores es más sencillo). Y es que tenemos CHARINDEX(), que nos localiza la primera aparición del caracter empezando a contar desde una posición fija, pero no algo similar a LASTINDEX que nos diga la última posición. Entonces ¿cómo vamos a sacar esta última palabra? Pues sirviéndonos de la función REVERSE() para poder encontrar la última aparición del espacio a base de darle la vuelta a la cadena:

DECLARE @test NVARCHAR(255)
SET @test = 'Esto es una frase'
SELECT REVERSE(LEFT(REVERSE(@test), CHARINDEX(' ', REVERSE(@test))-1 ))

SQL-Server: Consulta para saber en qué tabla está una columna

Vamos con un tip rápido de SQL-Server, y seguramente útil en muchas ocasiones. Me acaba de pasar que ejecutaba un script para pasar datos de una base de datos vieja a una nueva (donde las tablas tienen algunas diferencias, al ser un versión posterior de la aplicación) y me devolvía como error que el tipo de datos no era válido para la columna Envases. Y me asalta la duda y la necesidad ¿en qué tabla está esa columna? Pues podemos saberlo con una consulta:

SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME = 'Envases'
ORDER BY DATA_TYPE

En vuestro caso debéis cambiar Envases por el nombre de columna que corresponda. Próximamente lo veremos en Oracle.

La encuesta de la quincena (03/16) ¿Qué opinas de que salga SQL-Server para Linux?

La semana pasada tocó Bandcamp, esta semana toca otra vez encuesta. La anterior era sobre rock and roll, esta toca sobre informática y sobre una de las noticias de la semana que acaba de terminar: En 2017 Microsoft se plantea sacar una versión de SQL-Server para sistemas Linux. Así que ¿qué os parece esto?

SQL Server: Calcular una edad a partir de una fecha.

¿Cuántas veces no tenemos que sacar, en una consulta, la edad de una persona a partir de su fecha de nacimiento? Es una consulta que me toca hacer habitualmente (y que, mira por dónde, nunca había comentado aquí). ¿Cómo lo hacemos en SQL-Server?

Pensemos que tenemos una tabla clientes, donde hay un campo de tipo DATE llamado FechaNac en el que almacenamos sus fechas de nacimiento. Podrías creer que lo más simple es hacer lo siguiente:

Select DATEDIFF(YEAR,FechaNac,GETDATE()) as Edad from clientes

Pero hay un problema: Esa solución sólo resta los años, no tiene en cuenta el mes y el día ¿qué pasaría entonces? Que nos pondría la edad que ese cliente va a cumplir durante este año, no su edad real. ¿Cómo lo solucionamos? Hay muchas opciones, yo he optado por esta:

Select DATEDIFF(YEAR,FechaNac,GETDATE())
-(CASE
   WHEN DATEADD(YY,DATEDIFF(YEAR,clientes.FechaNac,GETDATE()),clientesFechaNac)>GETDATE() THEN 
      1
   ELSE 
      0 
   END) as Edad
 from clientes

Lo que hacemos es sumar la diferencia de años a la fecha de nacimiento y, en caso de que fuera posterior a hoy (es decir, todavía no ha cumpliado años) restamos 1 a la diferencia, si no restamos 0 y nos quedamos como estamos.

Otra opción, más elegante, podría ser esta:

Select floor(
(cast(convert(varchar(8),getdate(),112) as int)-
cast(convert(varchar(8),clientes.FechaNac,112) as int) ) / 10000
) as edad from clientes

En este caso convertimos la fecha al formato clásico de base de datos como una cadena que aglutina pegados Fecha, mes y día y lo convertimos a un entero (hoy nos quedaría por ejemplo 20151001), se lo restamos a la fecha de nacimiento, dividimos entre diez mil para obtener el año y redondeamos por defecto con la función floor().

SQL-Server ¿es mejor usar MAX() o TOP 1?

Una duda me asaltaba el otro día preparando una serie de consultas, en una de las cuales tenía que sacar la fecha más alta de una serie de registros. ¿Consumía menos recursos un MAX() o acaso era mejor recurrir a la conjunción de order by y Top 1?

Rebuscando por duckduckgo me encontré con este blog donde disipan la duda:

  1. Si el campo a buscar forma parte de un índice cluster, entonces da igual porque ambas se ejecutan a una velocidad extremadamente rápida
  2. Si el campo a buscar no es un índice cluster entonces MAX() da una respuesta más rápida porque la función está mejor optimizada y tiene menos carga que realizar un order by después del select

En el artículo original están los bancos de pruebas con resultados concretos.