Anti Patrones SQL
Los Anti Patrones son una técnica que pretende resolver un problema, pero a menudo genera otros problemas. Los Anti-Patrones SQL describen los errores más frecuentes realizados por las personas en SQL. Con la descripción de estos Anti-Patrones y las posibles soluciones, se pretende mejorar el desarrollo “evitando” y generando una solución más adecuada.
En este artículo trataremos los siguientes puntos:
Anti Patrón del uso del NULL
Grupos Ambiguos
Anti Patrón SELECT*
Uso de OR en vez de UNION
1- Anti Patrón del uso del NULL: uso de valor nulo, tanto para almacenamiento como para consulta.
Para el uso del NULL como valor ordinario:
El NULL en BD, se interpreta como “Unknown”, sin embargo, se cometen errores tales como:
Asumir que puede representar 0
SELECT NULL + 10 FROM DUAL;
Asumir que representa un String vacío:
SELECT ‘DIRECCION:’ || NULL
El Anti Patrón más frecuente por los programadores es el uso del NULL como valor ordinario:
a- Para obtener un resultado, si el campo es null:
SELECT CAMPO FROM TABLA WHERE CAMPO=NULL;
b- Para obtener un resultado, si el campo no es null:
SELECT CAMPO FROM TABLA WHERE CAMPO!=NULL;
Otro Anti Patrón es : el uso de valor ordinario como NULL:
a- Para actualizar un campo, con clave foránea:
UPDATE TABLE SET CAMPO_REF=-1 WHERE CAMPO_REF IS NULL;
b- Para actualizar un valor numérico:
UPDATE TABLE SET CAMPO_NUM=-1 WHERE CAMPO_NUM IS NULL;
c- Para entender el funcionamiento del NULL:
Al usarse como valor ordinario se debe usar la función que trate el NULL, como COALESCE o NVL:
SELECT NVL (COL, 0) + 10 FROM TABLA;
a- Para obtener un resultado, si el campo es null
SELECT COL FROM TABLA WHERE COL IS NULL;
b- Para obtener un resultado, si el campo no es null
SELECT COL FROM TABLA WHERE COL IS NOT NULL;
2- Grupos Ambiguos:
Error más común al usar la cláusula Group by
Dado la siguiente consulta:
SELECT PRODUCT_NAME, BUG_ID, MAX (DATE_REPORTED) FROM BUGS GROUP BY PRODUCT_NAME;
La consulta quedaría de la siguiente manera:
Se debe tomar en cuenta la regla: Single-Value, donde cada columna en el SELECT debe ser :
Parte de una función de agregación.
Estar en la cláusula GROUP BY.
Una dependencia funcional de la columna que se encuentra en GROUP BY.
Solución N° 1:
SELECT PRODUCT_NAME, MAX(DATE_REPORTED) FROM BUGS GROUP BY PRODUCT_NAME
Solución N° 2:
SELECT B.PRODUCT_NAME, B.BUG_ID, AUX.D FROM BUGS B JOIN (SELECT PRODUCT_NAME, MAX(DATE_REPORTED) D FROM BUGS GROUP BY PRODUCT_NAME) AUX ON(AUX.PRODUCT_NAME=B.PODUCT_NAME AND B.DATE_REPORTED = AUX.D )
Solución N° 3:
SELECT PRODUCT_NAME, MAX(BUG_ID), MAX(DATE_REPORTED) FROM BUGS GROUP BY PRODUCT_NAME
3- Anti Patrón SELECT * ¿ Por qué no se debe usar?
La sentencia es más lenta, debido que no todas las columnas están indexadas y se debe realizar un full scan.
Data innecesaria se estaría pasando por la conexión entre la aplicación/BD.
Si en la tabla se elimina/agrega un atributo la aplicación puede fallar.
Solución :
Obtener la definición de la tabla y luego seleccionar lo deseado:
MYSQL : DESCRIBE TABLE; Oracle: SELECT COLUMN_NAME FROM USER_TAB_COLUMN WHERE TABLE_NAME=’?’;
4- Uso de OR en vez de UNION
Dadas las siguientes consultas:
Consulta A
SELECT EMPLOYEE_ID FROM EMPLOYEES WHERE JOB_ID=’IT_PROG’ OR DEPARTMEMT_ID=10;
Consulta B
SELECT EMPLOYEE_ID FROM EMPLOYEES WHERE JOB_ID=’IT_PROG’ UNION SELECT EMPLOYEE_ID FROM EMPLOYEES WHERE DEPARTMENT_ID=10;
La consulta B es más rápida que la A. Ya que en la A, el optimizador del SMBD abandona el uso de índices en la cláusula OR (así dicha columna esté indexada) y procede a realizar full scan.
En la B, el optimizador ejecuta las consultas por separado y luego procede a unir los resultados.
Solución:
Si la consulta lo permite, convertir todos los OR en UNION, para una ejecución mas rápida.
Usar UNION ALL en vez de UNION, si no interesa tener repetidos y asi ahorrar al SMBD la eliminación de los mismos.