SQL Engineering: Guía Definitiva
Estrategias avanzadas, diseño de esquemas y optimización extrema de consultas para Bases de Datos Relacionales (SQL Server y PostgreSQL) en Devlinks.
1. RDBMS e Introducción
SQL es el estándar mundial de gestión de datos. El objetivo principal como Data Engineer en Devlinks es escribir consultas que eviten bloqueos innecesarios (Table Scans), respetando las estructuras estructuradas y relacionales de los BD.
Diseño Normalizado: Siempre procura cumplir la 3ra Forma Normal (3FN) para evitar redundancias de datos. No dupliques información a menos que estés desarrollando un Data Warehouse donde uses esquemas Desnormalizados (Estrella).
2. Tipos de Datos y Comandos DDL
El Data Definition Language rige la estructura física de los datos.
Tipos Fundamentales
-- INT: Usado para IDs. (Identidades de Autoincremento)
-- VARCHAR / NVARCHAR: Usa NVARCHAR para soportar codificación Unicode (Emoticones/Kanji)
-- DECIMAL(18,2): OBLIGATORIO para cálculos monetarios. Nunca uses FLOAT.
-- UUID / UNIQUEIDENTIFIER: Global Unique Identifiers, geniales para sistemas distribuidos, pero lentos como Primary Keys Clustered.
-- DATETIME2: En SQL Server, se prefiere sobre DATETIME por mayor precisión y rango.
Creación de Tablas (DDL)
CREATE TABLE Users (
Id INT IDENTITY(1,1) PRIMARY KEY,
GlobalId UNIQUEIDENTIFIER DEFAULT NEWID(),
Email NVARCHAR(100) NOT NULL UNIQUE,
Balance DECIMAL(18,2) DEFAULT 0.00,
CreatedAt DATETIME2 DEFAULT GETUTCDATE()
);
3. Filtrado y DML Moderno
Proyección y Paginación (OFFSET/FETCH)
-- Anti-Patrón: SELECT * FROM Users; (Trae cargas pesadas de red)
-- Mejor Práctica (Proyección Estricta con Paginación Segura):
SELECT Id, Email, Balance
FROM Users
WHERE Balance > 0
ORDER BY CreatedAt DESC
OFFSET 0 ROWS
FETCH NEXT 20 ROWS ONLY;
Actualizaciones Condicionadas Seguras (DML)
-- Uso avanzado de UPDATE con EXISTS y JOIN implícito
UPDATE a
SET a.Status = 'Suspended'
FROM Accounts a
WHERE EXISTS (
SELECT 1 FROM FraudLogs f
WHERE f.AccountId = a.Id
AND f.RiskLevel >= 90
);
4. Uniones (Joins)
Los Joins cruzan los conjuntos matemáticos de manera optimizada. Evita cadenas de subqueries dentro del SELECT evaluadas fila por fila cuando un JOIN resolvería todo en una exploración simple.
Inner vs Left Join
-- INNER JOIN: Exige que el registro exista en AMBAS tablas
SELECT O.Id, C.Name, O.Total
FROM Orders O
INNER JOIN Customers C ON O.CustomerId = C.Id;
-- LEFT JOIN: Trae TODOS los Customers, incluso si no tienen Orders (retorna NULL)
SELECT C.Name, O.Id
FROM Customers C
LEFT JOIN Orders O ON C.Id = O.CustomerId
WHERE O.Id IS NULL; -- Excelente forma de encontrar Usuarios inactivos
5. Agrupación y Agregación
GROUP BY vs HAVING
-- WHERE filtra antes de agrupar. HAVING filtra DESPUÉS de agrupar.
SELECT CategoryId, SUM(Price * Quantity) AS TotalSales
FROM OrderDetails
WHERE Discount = 0 -- Quita detalles con descuento antes de agrupar
GROUP BY CategoryId
HAVING SUM(Price * Quantity) > 10000; -- Filtra los grupos matemáticos sumados
6. Common Table Expressions y Windows Functions
SQL Moderno permite generar tablas virtuales nombradas al vuelo (CTEs) y manipular datos en el aire con Window Functions sin arruinar la cardinalidad.
Over, Partition By, y Row_Number()
WITH RankingCTE AS (
SELECT
DepartmentId,
EmployeeName,
Salary,
ROW_NUMBER() OVER(
PARTITION BY DepartmentId
ORDER BY Salary DESC
) AS SalaryRank
FROM Employees
)
-- Este CTE permite filtrar fácilmente al Mejor pagado de cada departamento
SELECT * FROM RankingCTE WHERE SalaryRank = 1;
7. Índices y Rendimiento B-Tree
Clustered vs Non-Clustered
-- ÍNDICE CLUSTERIZADO: Determina el orden físico en el Disco Duro.
-- Solo puede haber UNO por tabla (Casi siempre el ID Autoincremental).
-- ÍNDICES NO CLUSTERIZADOS: Crea una copia referencial tipo Directorio Telefónico.
CREATE NONCLUSTERED INDEX IX_Users_LastName
ON Users (LastName ASC);
-- Covering Index: Incluye las columnas del SELECT evitando buscar en el disco la fila completa.
CREATE NONCLUSTERED INDEX IX_Orders_Performance
ON Orders (Status)
INCLUDE (TotalAmount, CreatedAt);
8. Transacciones ACID
Atomicidad (Rollbacks Automáticos)
Siempre encierra cualquier movimiento bancario o lógica de transferencia en una transacción Try-Catch.
BEGIN TRY
BEGIN TRAN;
UPDATE Accounts SET Balance = Balance - 100 WHERE Id = 1;
UPDATE Accounts SET Balance = Balance + 100 WHERE Id = 2;
COMMIT TRAN; -- Si ambos updates pasaron, se materializa.
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRAN; -- Si un update falló (Constraint), revierte ambas operaciones.
-- Levanta el error original
THROW;
END CATCH;