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.

Diferencias entre clave primaria y clave foránea

Vamos con una entrada de SQL básico, uno de los fundamentos del modelo relacional ¿Qué diferencia existe entre una clave primaria y una foránea?

En el diseño de una base de datos relacional la clave primaria es el campo, o conjunto de campos, que nos permite identificar de forma única un registro. Por así decirlo es como el DNI de esa tabla. Se trata de un valor, o grupo de valores, único que nos permitirá diferenciar un registro concreto. Podemos definir la clave primaria en el momento de la creación de la tabla, dentro de la sintaxis del CREATE TABLE, o a posteriori añadiendo la regla en un ALTER TABLE.

Por su parte la clave foránea es un campo, o conjunto de campos, que nos permite relacionar un registro de una tabla con otro, generalmente de una tabla distnta . Como ejemplo piensa en la clásica base de datos de una tienda, en la que tienes una tabla con productos, cada uno de los cuales tiene un código (que será la clave primaria), una descripción y un precio. Por otra parte tienes una tabla en la que registras las ventas, y podrías tener un código para cada una, la fecha, la hora y el producto. Para identificar el producto en esa tabla lo más práctico, para no repetir información, sería utilizar el código de la tabla de productos (la clave primaria de la primera tabla, que en esta se convierte en clave externa).

Una tabla puede tener relaciones con varias a través de distintas claves foráneas, e incluso referenciarse a si misma (clave foránea recursiva). Al igual que con la clave primaria podemos definir las claves foráneas dentro de la sintaxis de creación de la tabla o a posteriori con un ALTER TABLE.

Por tanto la diferencia es sencilla: la clave primaria identifica un registro único de una tabla. La clave foránea relaciona los datos de un registro de una tabla con los de otra, o con un registro distinto de la misma tabla.

Diferencias de comandos entre T-SQL y PL/SQL

Esta tarde estuve mirando un poco de PL/SQL porque estoy apuntado en un par de ofertas de trabajo donde piden conocimientos básicos. En principio esta versión de SQL de Oracle  tiene la misma lógica y funciona más o menos igual que Transac SQL, pero hay alguna pequeña diferencia. Por lo que he indagado, algunos comandos cambian de uno a otro, ahí os inserto una lista.

 

CONCATENAR COLUMNAS

T-SQL = +

PL/SQL = ||

COMPROBAR VALOR NULO

T-SQL = ISNULL

PL/SQL = NVL

CONVERSIÓN DE TIPO DE DATO

T-SQL = CONVERT ()

PL/SQL = TO_DATE, TO_CHAR, TO_NUMBER (dependiendo del tipo)

COMPARACIÓN CON EL VALOR NULL

T-SQL = =NULL

PL/SQL = IS NULL

EXTRAER UNA PARTE DE UNA FECHA (DÍA, MES O AÑO) el ejemplo será con año

T-SQL = DATEPART (yy, DATE)

PL/SQL = EXTRACT (Year from DATE)

CREAR UN EQUAL JOIN

T-SQL = SELECT t1.field, t2.field FROM table1 t1 LEFT JOIN table2 ON t2.field = t1.field

PL/SQL = SELECT t1.field, t2.field FROM table1 t1, table2 t2 WHERE t1.field(+) = t2.field

SELECCIONAR UN NÚMERO CONCRETO DE FILAS

T-SQL = SELECT TOP seguido del número de columnas

PL/SQL = SELECT * FROM tabla1 WHERE rownum <= seguido del número de columnas

INCLUIR UN TEXO O UN NÚMERO EN LA SELECT:

T-SQL = SELECT ‘texto’

PL/SQL = SELECT ‘Texto’ FROM dual

ASIGNAR VALOR A UNA VARIABLE

T-SQL = SET var = val

PL/SQL = var := val

IMPRIMIR EN PANTALLA

T-SQL = PRINT ‘texto’

PL/SQL = dbms_output.put_line(“cadena de texto”)

CONDICIONAL IF

T-SQL = IF condicion THEN BEGIN bloque instrucciones END IF

PL/SQL = IF Condicion THEN — END IF;

Luego además, existen algunos comandos propios de TS/SQL y algunas funciones que existen en MySQL o SQL-Server no existen aquí por ser propios de esos gestores, pero grosso modo esto es lo que hay. Espero que os sea de ayuda.

Instalar MongoDB en Linux

MongoDB es un sistema de bases de datos «no SQL» (aunque lo haya metido en la sección SQL del blog), documental, que destaca por su velocidad y rendimiento. No en vano, muchas páginas con requisitos de gran escalabilidad están recurriendo a ete tipo de soluciones, como Facebook y Twitter, que han migrado parte de su base de datos (en un principio MySQL) a CassandraDB.

Aparte de un nombre horrible, MongoDB es bastante fácil de instalar, sólo hay que seguir estos pasitos tras descargar desde aquí la versión que se adapte a vuestra arquitectura (32bits o 64bits):

La idea es descomprimir el archivo (en el ejemplo será el de 64 bits, si bajáis el de 32 poned el nombre que corresponde), crear la carpeta donde MongoDB guarda los datos (/data/db) y darte permisos de usuario en ella. Tal que así:

tar -xzvf mongodb-linux-x86_64-1.8.1.tgz
sudo mkdir -p /data/db
sudo chown TU_USUARIO /data/db

Una vez cambiado el usuario a la carpeta /data/db, se debe ir a los archivos extraídos de MongoDB, entrar a la carpeta bin y ejecutar (con tu usuario) la aplicación: ./mongod

Si no aparece el mensaje «really exiting now» se está ejecutando el demonio de mongoDB, listo para conectar por defecto en el puerto 27017, aunque también tiene una interfaz web muy básica que pone a la escucha el puerto 28017 (puede consultarse en http://localhost:28017). Si quieres finalizar el demonio presiona ctrl + c.

Para hacer consultas desde consola de debe usar otro terminal, el el cual debes ir a la misma carpeta donde descomprimiste mongoDB, entrar a la carpeta bin y ejecutar la aplicación ./mongo que nos debe devolve un prompt vacío (símbolo >)

La shell de MongoDB es un intérprete de JavaScript por lo que podemos escribir scripts que pueden ser interpretados por la propia shell, utilizar las librerías estándares de JavaScript o escribir funciones en varias líneas.

> "Hello, World!".replace("World", "usuario molón");
Hello, usuario molón!

Cuando iniciamos la shell, el cliente conecta con la base de datos por defecto del servidor MongoDB y guarda la conexión en la variable db. Desde esta shell podemos utilizar las operaciones CRUD: crear, modificar, leer y borrar, para manipular y ver los datos en el shell.

Tras esto ya tienes instalado tu servidor de bases de datos MongoDB, si quieres documentación en español puedes tirar de su wiki oficial, para ir introduciéndote en este mundillo NoSQL

EDITO para comentar que en Ubuntu 12.04 han metido MongoDB en los repositorios, por lo que la instalación se simplifica con un básico

sudo apt-get install mongodb

En Debian que yo sepa de momento no, así que tenéis que seguir con el primer ejemplo, de momento.

Usando librerías .dll de .NET en SQL-Server2008

Aunque normalmente no se recomiende tener la lógica de negocio en el lado del servidor de la base de datos, en algunos casos puede resultar interesante el hacerlo. Microsoft, en su empeño (por otra parte loable) de lograr una integración total entre sus servicios, nos permite utilizar nuestras .dll creadas en .NET (sea en VB.NET o en C#.NET o en C++) dentro de nuestra base de datos SQL-Server como «código gestionado».

Iré construyendo poco a poco un ejemplo para que veáis, paso a paso como se realiza esto. Lo primero es activar clr para que nos permita la integración con .NET tal que así:

USE [basedatosdeejemplo]
go
sp_configure 'clr enabled', 1
go
reconfigure
go

Seleccionamos la base de datos a utilizar, configuramos clr como ‘enabled’ (armado, activado) con el parámetro 1 y ejecutamos reconfigure para que el cambio tenga efecto. Acuérdate de poner el parámetro (en este caso 1) y de ejecutar reconfigure.

El primer paso está realizado. Lo siguiente es crear el ensamblado para la librería, definiendo el esquema y el tipo de permisos.


CREATE ASSEMBLY [Utilidades]
AUTHORIZATION [dbo]
FROM 'C:\LibreriaEjemplo.dll'
WITH PERMISSION_SET = SAFE
GO

Creamos el ensamblado con el nombre «Utilidades» (podemos darle el que queramos mientras no sea una palabra reservada), con el esquema dbo (podríamos haber utilizado cualquiera de los disponibles), en FROM le especificamos la dirección del archivo en disco mediante una cadena de texto con la ruta y finalmente los permisos, en este caso SAFE.

SAFE es el permiso más restrictivo que hay, el más «seguro» para nuestro equipo pues limita mucho lo que pueda hacer la librería. En caso de que uses liberías de terceros es el permiso que te reportará más seguridad. Existe también el permiso EXTERNAL_ACCESS, que permite que el código acceda a ciertos recursos externos (registro, archivos, red) y el permiso UNSAFE, que da control sin restricciones a la librería sobre los recursos de la máquina. Si usas una librería propia puedes usar UNSAFE, pero si usas una de un tercero piensa que pueden entrañar riesgos de seguridad.

Con lo puesto ya tenemos la librería disponible en nuestra base datos. ¿Y ahora qué? Ahora simplemente puedes usar las clases y métodos de dicha librería en tu base. Puedes crear tus tipos de datos propios usando sus objetos, incluir sus métodos en procedimientos o triggers. En este ejemplo vamos a suponer que la librería importada tiene una clase Point que guarda las coordenadas de un eje X y un eje Y junto a un valor booleano, y tiene también una función GetCoordsAsText que muestra un mensaje largo con las coordendas. Vamos a crear un tipo de datos Point, usarlo en una tabla, acceder a los valores y usar el método GetCoordsAsText en una función. Pondré comentarios para ir explicando el proceso

--Creamos el tipo de datos Point como un objeto de la clase Point.
--Para acceder a la clase point tenemos que usar el método external name de SQL-Server
--accediendo a la clase mediante el nombre del assembly que creamos antes y el namespace de la clase (en 
--este caso point)
create type Point
external name Utilidades.Point 
go

--Creamos la tabla

create table dbo.Points
(
	id int identity primary key,
	valor Point
);
go

--Para acceder a los datos de la tabla debemos usar un método que nos devuelva
--los valores almacenados dentro del objeto en forma de texto (en este caso .X para el valor X, .Y para 
--el valor Y o .ToString() para sacar ambos en una columna como texto).

select id,
			valor.X as X,
			valor.Y as Y,
			valor.ToString() as Completo
from dbo.Points

--Si intentáramos acceder al objeto a pelo, como voy a poner debajo, nos devolvería el valor del objeto
--sin convertir (un churrazo con la dirección de memoria del puntero)

select valor from dbo.Points --así nos saldría un churo tipo 0x0000000100020000303000000

--También podríamos usar los métodos de la librería en una función, trigger, función de agregado,
--cursor... En este caso haremos una función que acceda a GetCoordsAsText (que está en la clase 
--UserFunctions)

create function dbo.CoordenadasComoTexto
returns nvarchar(50)
as
    external name Utilidades.UserFunctions.GetCoordsAsText

Y con esto tendríais la función que simplemente ejecuta el método definido en la dll y un tipo de datos igual al objeto. Las posibilidades de esto son muy grandes, así que podéis ir profundizando.