Limitar el número de filas en la salida de un consulta en Oracle

En fin, hoy primer contacto con el SGBD de Oracle (sí, ese privativo tan caro y a la vez tan extendido en la administración), del cual no puedo decir mucho porque con una clase, sobre fundamentos de SQL además (que no es por ir de sobrado, pero la primera semana me da que me voy a aburrir repasando lo más básico de siempre), pues no da para haber testado mucho. Lo que sí comprobé, tonteando un poco, es que la forma de forma de limitar el número de filas en la salida de una consulta no tiene nada que ver ni con SQL-Server, PostgreSQL o MySQL.

Porque claro, tú quieres por ejemplo sacar los 10 que más cobran y en MySQL o en PostgreSQL lo haces tal que así

SELECT nombre FROM empleados ORDER BY salario LIMIT 10

Y si eres de los de tito Bill te apañarías con:

SELECT TOP 10 nombre FROM empleados ORDER BY salario 

Total, que andaba trasteando un rato después de hacer los ejercicios, tipo «me ha sobrado un huevo de tiempo, vamos a probar si otras cosas van igual que en algunos de los sistemas gestore que he usado» y ahí me encuentro que esto cambia, que ni es como en Microsoft ni como en los otros dos SGBD libres. Aunque curiosamente la palabra limit aparezca como reservada (todavía no se para qué, una de las cosas que espero aprender en el susodicho curso). Tras un poco de investigación entre la (vasta) documentación de Oracle me ecuentro con que la cosa va así:

SELECT nombre FROM empleados ORDER BY salario WERE rownum<=10

Conclusión/explicación: Oracle nos ofrece una pseudocolumna (iba a llamarlo variable, pero acabo de leer que este es el término más correcto) llamada rownum (número de fila) que nos servirá, mediante una condición where, para limitar la salida de resultados de la consulta (también puede valer para realizar consultas paginadas en una web, pero igual te chupas demasiadas conexiones al motor si lo haces así en vez de bajar todo de vez y manejarlo en el cliente… ahí ya depende del proyecto). Si quieres 10 filas le dices «que el número de fila sea menor o igual que diez», que quieres 20… lo mismo.

Crear una función en MySQL

Aunque se trate de algo muy básico a la hora de programar bases de datos, creo que no está de más hablar de la sintáxis para la creación de funciones definidas por el usuario en MySQL. Dicha sintaxis es la siguiente:

CREATE [AGGREGATE] FUNCTION function_name RETURNS {STRING|INTEGER|REAL}
       SONAME shared_library_name

Explicado rápidamente: function_name es el nombre con el que se va a invocar dicha función. RETURNS nos indica, por otra parte, qué tipo de dato recibiremos como respuesta. Y shared_library_name es el nombre del fichero del objeto compartido que guarda el código de la función. El modificador opcional AGGREGATE permite definir la función como una función de agregado (como SUM() o COUNT()).

Para poder crear una función se necesita tener permiso de INSERT en la base de datos, mientras que para eliminarla se necesitan permisos de DELETE. La sintaxis de borrado de una función es:

DROP FUNCTION function_name

MySQL además del uso de funciones SQL permite también el uso de funciones de C y C++ (como SQL-Server permite el uso de funciones contenidas en una .dll u Oracle el uso de Java). En caso de querer usar una función de C hay que recordar que los tipos de datos en C no se llaman igual que en SQL, para definir el retorno de la función. STRING sería char *, INTEGER sería long long y REAL sería double. También es interesante, aunque no obligatorio, definir una función nombredelafuncion_init() y nombredelafuncion_deinit(), el primero para reservar memoria y el segundo para liberarla. Recuerda que C no reserva memoria automáticamente, así que puede ser indispensable para el uso de la función si esta requiere algo de RAM. Las funciones en C o C++ deben compilarse e instalarse en el equipo donde corre el servidor.

Encriptación AES en MySQL (y MaríaDB)

El uso de la encriptación AES en MySQL (la más segura de la que dispone el famoso SGBD) es sencillo, y se realiza mediante dos funciones: AES_ENCRYPT y AES_DECRYPT. Permite usar AES_128 o AES_256 dependiendo de la fuente que usemos.

Para insertar o modificar los datos de una columna basta con hacer uso de AES_ENCRYPT, que recibe dos parámetros: el dato a insertar (string) y la clave (string también). A diferencia de SQL-Server, en este caso el dato a encriptar siempre es una cadena. Si quieres conocer de antemano la longitud de la cadena resultado (para ajustar el tamaño del campo en la tabla) lo puedes hacer recurriendo a la fórmula 16 * (trunc(logintud_de_la_cadena / 16) + 1). Si recibe un argumento null el resultado será null también.

Para el caso de AES_DECRYPT los parámetros serán la cadena encriptada a descifrar y la contraseña. Si hay algún error en la encriptación (sea por una modificación maliciosa o por datos de entrada incorrectos) esta función devolverá NULL, aunque es posible que en algún caso pueda dar un valor no nulo erróneo si la cadena encriptada o la contraseña son erróneas.

Una inserción de un registro encriptado se ralizaría de la siguiente forma:

INSERT INTO tb_Ejemplo VALUES(AES_ENCRYPT('valor a encriptar como cadena', 'contraseña'));

En este caso si realizamos una select normal nos devolvería los datos encriptados, para poder verlos tendríamos que hacer

SELECT AES_DECRYPT(COLUMNA_ENCRIPTADA, 'contraseña') FROM tb_Ejemplo;

Finalmente, por seguridad podemos guardar la contraseña en una variable del servidor para no enviarla en cada comunicación con la base de datos, tal que así:

SELECT @pss:='password';
INSERT INTO tb_Ejemplo VALUES (AES_ENCRYPT('texto',@pss));

Sobre cifrado tanto MySQL como MariaDB ofrecen varias opciones aparte de AES, que en este caso es la comentada por ser la más segura y robusta. Pero también existe la posibilidad de usar DES (DES_ENCRYPT y DES_DECRYPT), las funciones DECODE y ENCODE (que no se qué algoritmo usan, la verdad, pero que nos obligan a almacenar los datos en una columna tipo BLOB) y para hashes las funciones MD5, SHA1 y ENCRYPT (que llama a la función crypt() de UNIX, por lo que no puede usarse en Windows). También existe la función PASSWORD, pero es la que usa el sistema de autenticación de MySQL, por lo que se recomienda no usarla en aplicaciones propias.

Encriptar una columna en MS-SQL Server con clave simétrica

Siguiendo con la racha de entradas sobre SQL, vamos a ver cómo encriptar una columna de una tabla SQL-Server usando una clave simétrica.

Bueno, lo primero, tras haber seleccionado la base de datos que queremos usar, es crear una clave maestra si no existe ya, con un password seguro (largo, con mayúsculas, minúsculas, números y símbolos), luego crear un certificado y finalmente una clave simétrica eligiendo el algoritmo a usar (en este caso AES256, robusto y seguro):

IF NOT EXISTS 
    (SELECT * FROM sys.symmetric_keys WHERE symmetric_key_id = 101)
    CREATE MASTER KEY ENCRYPTION BY 
    PASSWORD = 'PoNGo@1#pa55w0rd!fuEr73@para½q@no$me()lo_tAngUen'
GO

CREATE CERTIFICATE NombreDelCertificado
   WITH SUBJECT = 'Certificado Para Ejemplo';
GO

CREATE SYMMETRIC KEY SSN_Clave_Simétrica_01
    WITH ALGORITHM = AES_256
    ENCRYPTION BY CERTIFICATE NombreDelCertificado;
GO

Ok, con esto ya tenemos nuestra clave creada, con su contraseña. Un dato, si sois usuarios de Windows XP o de Windows Server 2000 no podéis usar AES, esos sistemas operativos no lo soportan, tenéis que usar DES (menos robusto y seguro, pero es lo que hay por usar sistemas sin actualizar). Ahora para introducir datos cifrados tienes que hacer lo siguiente:

-- Primero abre la clave simétrica.
OPEN SYMMETRIC KEY SSN_Clave_Simétrica_01
   DECRYPTION BY CERTIFICATE NombreDelCertificado;

--Luego introduce los datos
UPDATE BDEjemplos.Ejemplo
SET ColumnaDatosEncriptados = EncryptByKey(Key_GUID('SSN_Clave_Simétrica_01'), 'Una cadena cualquiera');
GO

En este caso usé un UPDATE pero podría haberlo hecho dentro de un INSERT. En todo caso, la función EncryptByKey() que recibe el identificador de la clave como primer parámetro y el dato a encriptar (String, Real, Float…) como segundo, es la clave de la operación.

Y para acceder a los datos cifados la cosa tampoco se torna muy compleja, es má o menos lo mismo, primero abrir y luego usar una función para desencriptar en la SELECT.En el ejemplo sacaríamos en una columna los datos encriptados y al lado el mismo dato desencriptado.

OPEN SYMMETRIC KEY SSN_Clave_Simétrica_01
   DECRYPTION BY CERTIFICATE NombreDelCertificado;
GO

SELECT ColumnaDatosEncriptados 
    AS 'Dato Encriptado',
    CONVERT(nvarchar, DecryptByKey(ColumnaDatosEncriptados)) 
    AS 'Dato Desencriptado'
    FROM BDEjemplos.Ejemplo;
GO

Existe la posibilidad de hacer esto también con un autenticador para fortalecer la seguridad. Si os interesa podéis mirarlo en la web de ayuda de Microsoft o presionarme para que publique aquí como hacerlo.

La función IFNULL de MySQL

Si el otro día hablaba de la función IF() no es menos interesante la función IFNULL() de MySQL.

En muchos casos te encuentras bases de datos donde en una columna, por falta de restricciones, hay gente que ha usado el nulo en lugar del cero y están todos mezclados (eso me ha pasado a mi, y luego no había forma de cuadrar los balances). O simplemente quieres sacar un listado de producto y deseas que en el nulo ponga un mensaje tipo «no aplicable» (para un descuento) o «sin unidades» (para un registro de almacén). La función IFNULL facilita este trabajo.

Vamos a ver el primer ejemplo citado. Tenemos una columna donde se han usado nulos en valores que realmente deberían llevar cero, y ahora necesitamos exportarlo a Excel y nos va a hacer el lío tenerlos así. Cierto es que una opción sería modificar toda la columna cambiando nulos por ceros, pero supongamos que no es posible (por falta de permisos de edición sobre la tabla, mismamente). En este caso con una línea tal que esta:

SELECT producto, IFNULL(precio, 0) from tbProductos

La función evalúa el primer parámetro y si este es nulo devuelve el valor indicado en el segundo, mientras que si no lo es devuelve el primero. Es decir, en el ejemplo devolvería el precio del producto si no es nulo, y si lo es devolvería cero.

Veamos otro ejemplo, donde probaríamos la función IFNULL con el ejemplo del descuento:

SELECT producto, IFNULL(descuento, 'NO APLICABLE') from tbProductos

La función IFNULL puede devolver valores del tipo INTEGER, REAL o STRING.

Comentar que en MaríaDB también existe, como la mayoría de las funciones de MySQL. Y que en Microsoft SQL-Server existe la función equivalente ISNULL.

La función IF de MySQL

En el SGBD MySQL existe una función bastante útil llamada IF. Obviamente no estoy hablando de una sentencia condicional if para el control de flujo como las que que tenemos en cualquier lenguaje de programación (Visual Basic, C, Java, PHP… y que en MySQL también existe), sino de una función que recibe tres argumentos: una condición a evaluar, un valor a devolver si se cumple y un valor a devolver si no.

Veámoslo ilustrado con el siguiente ejemplo. Tenemos una lista de productos con sus respectivos precios, y nos interesa saber simplemente si el precio es mayor de 60 euros, para separarlos así entre «caros» y «baratos». Aunque podríamos hacerlo con una par de consultas unidas imprimiendo una cadena, ahorraremos código haciéndolo de la siguiente forma:

Select IdProducto, NompreProducto, if(Precio>=60, 'caro','barato')
from tbArtículos

Incluso podemos usar la función IF con funciones de agregado, combinando. En el siguiente ejemplo supongamos que, con la misma tabla de productos, queremos saber si los proveedores nos sirven sólo un producto o si nos sirven más de uno, no nos importa la cantidad, sólo discriminaremos entre uno y más de uno.

Select Proveedor, if(count(*)>1, 'Más de uno','Sólo uno')
from Productos group by Proveedor

La idea es que la función evalúa si el primer parámetro recibido es verdadero y distinto de nulo, y en base a eso devuelve uno de los dos valores, el primero para TRUE y el segundo para FALSE.

Por cierto, la función IF también existe en el fork libre de MySQL MaríaDB, como otras muchas funciones del popular gestor de bases de datos. No está mal recordarlo, por si algún día Oracle se encabrona y nos deja tirados, ya que puede que algún día MaríaDB sea la mejor opción.

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.

Convertir fechas de formato MySQL a español en PHP

Uno de los problemas al recuperar una fecha desde MySQL es que te la devuelve en formato aaaa-mm-dd, el cual tiene su lógica a la hora de hacer comparaciones de fechas, pero es un pelín engorroso y raro mostrarlo así en pantalla. Antaño usaríamos una expresión regular para arreglarlo pero en PHP 5 hay una solución mejor, por lo que he actualizado el artículo (por eso y porque en la solución antigua usaba ereg que actualmente es una función obsoleta), ya que con un objeto DateTime se acaba el mamoneo:

/////////////////////////////////////////////////// 
//Convierte fecha de mysql a español 
//////////////////////////////////////////////////// 
function cambiaf_mysql($fechadb){
//vamos a suponer que recibmos el formato MySQL básico de YYYY-MM-DD
//lo primero es separar cada elemento en una variable
   	list($yy,$mm,$dd)=explode("-",$fechadb);
//si viniera en otro formato, adaptad el explode y el orden de las variables a lo que necesitéis
//creamos un objeto DateTime (existe desde PHP 5.2)
   	$fecha = new DateTime();
//definimos la fecha pasándole las variabes antes extraídas
        $fecha->setDate($yy, $mm, $dd);
//y ahora el propio objeto nos permite definir el formato de fecha para imprimir que queramos       
   	echo $fecha->format('d-m-Y');
}

Como podéis ver la cosa es sencilla. Sobre todo para el tema de insertar fechas desde PHP a MySQL. Porque si vuestra cuita es mostrar en pantalla la fecha formateada, también podéis pedir que MySQL os la devuelva con el formato que queráis. Basta con usar la función date_format() en la query de la select. En el ejemplo vamos a pedir que nos devuelva la fecha con el formato español ya:

SELECT DATE_FORMAT(campo_fecha,'%d/%m/%Y') FROM tabla

Ale, artículo actualizado para vuestro gozo y disfrute.

Instalar PostgreSQL en Ubuntu / Debian / Linux Mint

PostgreSQL mola por muchos motivos: es libre (licencia BSD), es estable, puede usarse en aplicaciones comerciales, es muy escalable y tiene un elefante como logo (y todo el mundo que no es Borbón ama a los elefantes).

La instalación de PostgreSQL en tu linux (ya sea Debian, Ubuntu o Mint) es sencillita. Empezaré con Ubuntu/Mint, y tras esto, Debian.

Empezamos con el clásico:

sudo apt-get install postgresql  libpq-dev

En Debian la cosa sería prácticamente igual pero con aptitude en lugar de apt-get.

Luego toca lanzar una instancia del servidor de bases de datos y cambiar la

su -n postgres

psql -d postgres -U 

##ahora estaréis en la una instancia de Postgres, ya.
postgres=#
postgres=# alter user postgres with password 'El que toque' 
postgres=# q

Obviamente en vez de ‘El que toque» ponéis un password decente, uno que creáis seguro (nunca menos de 12 dígitos, con letras, número y símbolos)

Y tras esto sólo os queda instalar el entorno gráfico del servidor de la base de datos. Para esto basta un simple:

sudo apt-get install pgadmin3

PostgresAdmin es un cómodo y liviano entorno gráfico que os facilitará mucho la vida.

Ok ¿y en Debian? Similar y sencillito. Ya no explico los pasos, porque son los mismos, lo meto todo en un mismo bloque de texto:

aptitude install postgresql

##Cambiamos la contraseña

su postgres

psql

alter user postgres with password 'nuevopassword';

##Instalamos PostgresAdmin

aptitude install pgadmin3

Ya lo ves, clavadico en Debian.

En fin, es tarde y mañana tengo un examen de certificación de Oracle así que os dejo con esto. Estos días estoy menos activo porque he estado chapando para estas cosas y administrando y gestionando la página de WP del festival Pardiñas Rock.

Instalar complemento de PHP para MongoDB

Como complento a la entra sobre cómo instalar MongoDB en Linux, toca ver cómo se instala la extensión para PHP.

Algunas distribuciones de MongoDB ya traen de por si la citada extensión, así que antes de hacer nada prueba con lo siguiente:

php --re mongo

Si tras esto nos devuelve Extension mongo does not exist es que tenemos que instalarlo. Lo más fácil es usar los repositorios PECL, aunque recuerda que tienes que tener instalada una versión de PHP compatible, porque si no te dará error (si tienes PHP 5 no hay problema, si no instálalo). El código sería el siguiente:

sudo pecl install mongo

##Si necesitas instalar PHP lo puedes hacer con la siguiente instrucción##
sudo apt-get install php5-dev

Ahora, tras reiniciar el servidor basta con volver a ejecutar php –re mongo para comprobar que sí está instalada la extensión.

Con esto ya tienes la extensión para PHP de MongoDB instalada en tu equipo Linux.