Mejores Prácticas
## Objetivos de Aprendizaje
- Escribir SQL mantenible
- Optimizar rendimiento de consultas
- Asegurar seguridad de datos
- Seguir convenciones de nomenclatura
## Estilo de Código
### Formato
```sql
-- Bueno: legible e indentado
SELECT
e.first_name,
e.last_name,
d.department_name
FROM employees e
INNER JOIN departments d
ON e.department_id = d.id
WHERE e.salary > 50000
ORDER BY e.last_name;
-- Malo: todo en una línea
SELECT e.first_name,e.last_name,d.department_name FROM employees e INNER JOIN departments d ON e.department_id=d.id WHERE e.salary>50000 ORDER BY e.last_name;
```
### Capitalización
```sql
-- Palabras clave en mayúsculas, nombres en minúsculas
SELECT first_name, last_name
FROM employees
WHERE department = 'Sales';
-- También aceptable: todo en minúsculas
select first_name, last_name
from employees
where department = 'Sales';
```
## Convenciones de Nomenclatura
### Tablas
```sql
-- Singulares, minúsculas con guiones bajos
CREATE TABLE employee; -- Malo
CREATE TABLE employees; -- Aceptable
CREATE TABLE employee_record; -- Bueno
CREATE TABLE person; -- Bueno (singular)
```
### Columnas
```sql
-- Descriptivas, minúsculas con guiones bajos
first_name -- Bueno
last_name -- Bueno
date_of_birth -- Bueno
emp_id -- Aceptable
```
### Restricciones
```sql
-- Nomenclatura consistente
pk_employees -- Primary key
fk_employees_department -- Foreign key
uq_employees_email -- Unique constraint
chk_employees_salary -- Check constraint
```
### Alias
```sql
-- Significativos o estándar
SELECT
e.first_name,
d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.id;
-- Usar 'as' para claridad
SELECT first_name AS "First Name"
```
## Optimización de Consultas
### Usar Índices Apropiados
```sql
-- Índice en columnas frecuentemente filtradas
CREATE INDEX idx_employees_department ON employees(department);
CREATE INDEX idx_employees_salary ON employees(salary);
```
### Evitar SELECT *
```sql
-- Malo: recupera columnas innecesarias
SELECT * FROM employees;
-- Bueno: solo columnas necesarias
SELECT first_name, last_name, email FROM employees;
```
### Usar EXISTS sobre COUNT
```sql
-- Más rápido: se detiene en la primera coincidencia
WHERE EXISTS (SELECT 1 FROM orders WHERE employee_id = e.id)
-- Más lento: cuenta todos
WHERE (SELECT COUNT(*) FROM orders WHERE employee_id = e.id) > 0
```
### Evitar Funciones en Columnas Indexadas
```sql
-- Malo: no puede usar índice
WHERE YEAR(hire_date) = 2020
-- Bueno: puede usar índice
WHERE hire_date >= '2020-01-01' AND hire_date < '2021-01-01'
```
### Usar JOINs Eficientemente
```sql
-- Preferir JOIN sobre subconsulta (usualmente)
SELECT e.first_name, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.id;
-- A veces la subconsulta es más clara
SELECT *
FROM employees
WHERE department_id IN (SELECT id FROM departments WHERE location = 'NYC');
```
## Seguridad
### Prevenir Inyección SQL
```sql
-- Malo: vulnerable a inyección
"SELECT * FROM users WHERE name = '" + user_input + "'"
-- Bueno: consultas parametrizadas
"SELECT * FROM users WHERE name = ?"
-- Parámetros: [user_input]
```
### Limitar Privilegios
```sql
-- Usuario de aplicación con acceso limitado
GRANT SELECT, INSERT, UPDATE ON employees TO app_user;
REVOKE DELETE ON employees FROM app_user;
```
### Nunca Almacenar Contraseñas en Texto Plano
```sql
-- Malo
INSERT INTO users (password) VALUES ('secret123');
-- Bueno: almacenar solo hash
INSERT INTO users (password_hash)
VALUES ('sha256$abc123...');
```
## Mantenimiento
### Usar Transacciones
```sql
BEGIN;
UPDATE accounts SET balance = balance - 1000 WHERE id = 1;
UPDATE accounts SET balance = balance + 1000 WHERE id = 2;
COMMIT; -- O ROLLBACK si hay error
```
### Documentar Consultas Complejas
```sql
-- Propósito: Encontrar el mejor pagado en cada departamento
SELECT
department,
first_name,
salary
FROM employees e1
WHERE salary = (
SELECT MAX(salary)
FROM employees e2
WHERE e2.department = e1.department
);
```
### Probar Consultas Destructivas
```sql
-- 1. Primero, SELECT para previsualizar
SELECT * FROM employees WHERE department = 'Temp';
-- 2. Luego DELETE
DELETE FROM employees WHERE department = 'Temp';
```
## Diseño de Esquema
### Normalización
1. **1NF**: Valores atómicos, sin grupos repetitivos
2. **2NF**: Sin dependencias parciales (claves compuestas)
3. **3NF**: Sin dependencias transitivas
### Desnormalización
A veces intencional por rendimiento:
```sql
-- Agregar columna calculada
ALTER TABLE orders
ADD COLUMN total_amount DECIMAL(10,2)
GENERATED ALWAYS AS (quantity * unit_price);
```
## Manejo de Errores
### Verificar Filas Afectadas
```sql
UPDATE employees SET salary = 75000 WHERE employee_id = 999;
-- Si 0 filas afectadas, el empleado no existe
```
### Usar Tipos de Datos Apropiados
```sql
-- Para precisión fija (dinero)
DECIMAL(10,2) -- Bueno para moneda
-- Para valores aproximados
FLOAT -- Bueno para datos científicos
```
## Documentación
### Comentar Tu Código
```sql
-- Calcular bonificación basada en antigüedad
SELECT
first_name,
hire_date,
CASE
WHEN hire_date < '2015-01-01' THEN salary * 0.15
WHEN hire_date < '2020-01-01' THEN salary * 0.10
ELSE salary * 0.05
END AS bonus
FROM employees;
```
## Resumen
- Usar formato y nomenclatura consistentes
- Escribir SQL legible y bien indentado
- Optimizar con índices apropiados
- Usar consultas parametrizadas para seguridad
- Probar consultas destructivas primero con SELECT
- Documentar consultas complejas
- Usar transacciones para cambios relacionados
- Elegir tipos de datos apropiados
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →