1.
Variables y Asignación
-- Declarar una variable y asignarle un valor
DECLARE @Precio DECIMAL(10,2);
SET @Precio = 99.99;
-- Usar SELECT ... INTO para crear una nueva tabla a partir de una
consulta
SELECT EmployeeID, Name, Salary
INTO EmpleadosTemp
FROM Employees
WHERE Salary > @Precio;
[Link], EDITAR COLUMNAS
I
NSERT INTO empleados (nombre, edad, departamento)
VALUES ('Juan Pérez', 30, 'Ventas');
2.1 ALTER TABLE
ALTER TABLE nombre_de_la_tabla
ADD COLUMN nombre_columna tipo_de_dato;
ALTER TABLE empleados
ADD COLUMN edad INTEGER;
2.11 🔢 Numéricos
Tipo Descripción Alias
comunes
SMALLINT Entero pequeño (2 bytes, -32,768 a int2
32,767)
INTEGER Entero estándar (4 bytes, -2B a 2B) int, int4
BIGINT Entero grande (8 bytes) int8
DECIMAL(p,s) Precisión fija, p: total dígitos, s: decimales NUMERIC(p,s
)
NUMERIC Precisión arbitraria (ideal para finanzas)
REAL Punto flotante (4 bytes) float4
DOUBLE Punto flotante doble (8 bytes) float8
PRECISION
SERIAL Entero autoincremental (4 bytes)
BIGSERIAL Autoincremental grande (8 bytes)
2.12 🔡 Texto / Caracteres
Tipo Descripción
CHAR(n) Texto de longitud fija
VARCHAR( Texto de longitud variable (hasta n
n) chars)
TEXT Texto de longitud ilimitada
2.13📅 Fechas y Tiempos
Tipo Descripción
DATE Solo fecha (YYYY-MM-DD)
TIME Solo hora (sin zona horaria)
TIMESTAMP Fecha y hora (sin zona horaria)
TIMESTAMP WITH TIME Fecha y hora con zona
ZONE
INTERVAL Intervalos de tiempo (e.g. '2
days')
2.14✅ Booleanos
Tipo Descripción
BOOLE Verdadero o falso (true/false, t/f,
AN 1/0)
2.15🧱 Otros útiles
Tipo Descripción
UUID Identificador único universal
BYTEA Datos binarios (archivos, imágenes,
etc.)
JSON / JSONB JSON puro / binario optimizado
ARRAY Arreglos de cualquier tipo (e.g. int[])
ENUM Enumeraciones definidas por el usuario
GEOMETRY, Tipos espaciales (usando PostGIS)
GEOGRAPHY
[Link] TABLE
Basica
UPDATE nombre_tabla
SET columna1 = valor1,
columna2 = valor2,
...
WHERE condición;
Para subconsultas y joins
UPDATE tabla_destino
SET columna_destino = [Link]
FROM (
SELECT ... -- subconsulta o tabla
) AS fuente
WHERE tabla_destino.clave = [Link];
ejemplo
UPDATE ventas v
SET total_por_cliente = [Link]
FROM (
SELECT cliente_id, SUM(monto) AS total
FROM ventas
GROUP BY cliente_id
) t
WHERE v.cliente_id = t.cliente_id;
2.3 CASE
-- Write your PostgreSQL query statement below
SELECT id,
SUM(CASE WHEN month = 'Jan' THEN revenue ELSE NULL END) AS
Jan_Revenue,
SUM(CASE WHEN month = 'Feb' THEN revenue ELSE NULL END) AS
Feb_Revenue,
SUM(CASE WHEN month = 'Mar' THEN revenue ELSE NULL END) AS
Mar_Revenue,
SUM(CASE WHEN month = 'Apr' THEN revenue ELSE NULL END) AS
Apr_Revenue,
SUM(CASE WHEN month = 'May' THEN revenue ELSE NULL END) AS
May_Revenue,
SUM(CASE WHEN month = 'Jun' THEN revenue ELSE NULL END) AS
Jun_Revenue,
SUM(CASE WHEN month = 'Jul' THEN revenue ELSE NULL END) AS
Jul_Revenue,
SUM(CASE WHEN month = 'Aug' THEN revenue ELSE NULL END) AS
Aug_Revenue,
SUM(CASE WHEN month = 'Sep' THEN revenue ELSE NULL END) AS
Sep_Revenue,
SUM(CASE WHEN month = 'Oct' THEN revenue ELSE NULL END) AS
Oct_Revenue,
SUM(CASE WHEN month = 'Nov' THEN revenue ELSE NULL END) AS
Nov_Revenue,
SUM(CASE WHEN month = 'Dec' THEN revenue ELSE NULL END) AS
Dec_Revenue
FROM Department
GROUP BY id;
[Link]
2.6 FULL OUTER JOIN (Unión externa completa)
SELECT e.employee_id, [Link], d.department_name
FROM employees e
FULL OUTER JOIN departments d ON e.department_id =
d.department_id;
✅ Explicación: Muestra todos los empleados y todos los departamentos, incluso si no
coinciden.
2.7 SELF JOIN
SELECT [Link] AS Employee, [Link] AS Manager
FROM employees e1
INNER JOIN employees e2 ON e1.manager_id = e2.employee_id;
✅ Explicación: Se busca el jefe de cada empleado dentro de la misma tabla.
SELECT [Link] AS Employee, [Link] AS Manager, [Link] AS
Director
FROM employees e1
LEFT JOIN employees e2 ON e1.manager_id = e2.employee_id
LEFT JOIN employees e3 ON e2.manager_id = e3.employee_id;
✅ Explicación: Encuentra la jerarquía de empleado → gerente → director.
2.8 UNION (Elimina duplicados)
SELECT employee_id, name FROM employees_sales
UNION
SELECT employee_id, name FROM employees_marketing;
✅ Explicación:
● Une las listas de empleados de ventas y marketing.
● Si un empleado está en ambas tablas, solo aparecerá una vez.
2.9 UNION ALL (Conserva duplicados)
UNION ALL combina los resultados de dos consultas pero conserva los
duplicados.
SELECT employee_id, name FROM employees_sales
UNION ALL
SELECT employee_id, name FROM employees_marketing;
Si un empleado está en ambas tablas, aparecerá dos veces.
Ejemplo Comparativo
SELECT name FROM employees_sales
UNION
SELECT name FROM employees_marketing;
🔹 Salida:
John
Alice
Mark
Sarah
SELECT name FROM employees_sales
UNION ALL
SELECT name FROM employees_marketing;
🔹 Salida:
John
Alice
Mark
Sarah
Mark
Alice
✅ Explicación:
● UNION eliminó los duplicados.
● UNION ALL mantuvo todos los valores.
[Link] de Manipulación de Texto
-- SUBSTR: Extrae una subcadena (ejemplo: primeros 4 caracteres)
SELECT SUBSTRING(Name, 1, 4) AS NombreCorto
FROM Employees;
-- CONCAT: Une dos o más cadenas
SELECT CONCAT(Name, ' - ', DepartmentID) AS InfoEmpleado
FROM Employees;
-- LENGTH: Devuelve la longitud de una cadena
SELECT Name, LENGTH(Name) AS LongitudNombre
FROM Employees;
SELECT Name, RIGHT(Name,3) AS ETC1
SELECT Name, LEFT(Name,3) as ETC2
3.5 TRIM,REPLACE Y PATRONES DE TEXTO CON
COMODINES
SELECT TRIM(' Hola SQL ') AS SinEspacios;
SELECT TRIM(LEADING FROM ' Hola') AS Izquierda;
SELECT TRIM(TRAILING FROM 'SQL ') AS Derecha;
SELECT TRIM(BOTH FROM ' Hola SQL ') AS SinEspacios;
✅ TRIM elimina los espacios a ambos lados, mientras que
LTRIM y RTRIM lo hacen en un solo lado.
SELECT REPLACE('SQL Server es increíble', 'increíble', 'potente')
AS FraseModificada;
✅ Reemplaza "increíble" por "potente".
✅3.6 REPLACE:
REPLACE(expresión_original, texto_a_buscar, texto_de_reemplazo)
2. ¿Por qué REPLACE es tan útil?
✅ Limpieza de datos
● Quitar caracteres no deseados (*, #, -, etc.).
● Normalizar datos, como nombres o formatos de fecha.
✅ Corrección de datos
● Corregir errores tipográficos en texto almacenado.
✅ Migración de datos
● Cambiar nombres de dominio en correos electrónicos.
● Actualizar formatos de códigos de productos.
✅ Anonimización de datos sensibles
● Enmascarar números de tarjetas, teléfonos, correos, etc.
[Link] guiones
SELECT telefono AS Original,
REPLACE(telefono, '-', '') AS Telefono_Limpio
FROM clientes;
[Link] formatos de fecha
SELECT fecha AS FechaOriginal,
REPLACE(REPLACE(fecha, '-', '/'), ' ', '') AS
FechaFormateada
FROM pedidos;
[Link]
SELECT nombre AS NombreOriginal,
REPLACE(REPLACE(nombre, '#', ''), '*', '') AS NombreLimpio
FROM clientes;
Ejemplo de salida:
NombreOriginal NombreLimpio
#Juan Pérez Juan Pérez
María*Gómez María Gómez
4. Correos
SELECT email AS EmailOriginal,
REPLACE(email, '@[Link]', '@[Link]')
AS EmailActualizado
FROM empleados;
EmailOriginal EmailActualizado
juan@[Link] juan@[Link]
m m
maria@[Link] maria@[Link]
m m
3.65 POSITION,SIMILAR TO, ~ (expresiones regulares) o
regexp_matches.
SELECT POSITION('SQL' IN 'Aprender SQL Server') AS Posicion;
CONTIENE NUMERO O NO? (BOOLEANS)
SELECT name,
regexp_match(name, '[0-9]') IS NOT NULL AS ContieneNumero
FROM employees;
Detectar filas con dos espacios:
SELECT area
FROM info_empresarial
WHERE area ~ ' ';
🔁 Usar SIMILAR TO o ~ en lugar de LIKE con corchetes
SIMILAR TO
-- Usar SIMILAR TO
SELECT Name FROM Employees WHERE Name SIMILAR TO '[AEIOU]%';
-- O usando expresiones regulares
SELECT Name FROM Employees WHERE Name ~* '^[AEIOU]';
___________________________________________________________________
3.7 ✂️ESPACIOS-PALABRAS
-- Limpia espacios al inicio y al final
-- Reemplaza múltiples espacios seguidos dentro del texto por un
solo espacio
UPDATE Info_Empresarial
SET area = regexp_replace(trim(area), '\s+', ' ', 'g');
🔁 Comparación con SQL Server
Objetivo SQL Server PostgreSQL
Quitar espacios al LTRIM(RTRIM(area)) trim(area)
principio y final
Reemplazar STUFF(...) + regexp_replace(trim(area),
espacios múltiples STRING_SPLIT + FOR '\s+', ' ', 'g') ✅
por uno
XML PATH 🙃
Ver resultado antes SELECT DISTINCT SELECT DISTINCT area,
de actualizar area, regexp_replace(...) AS
area_limpia ... area_limpia
[Link]ÓN DE TIPOS DE DATOS
SELECT
precio AS PrecioOriginal,
CAST(precio AS NUMERIC(10,2)) AS PrecioConvertido,
CAST(precio AS INTEGER) AS PrecioEntero
FROM ventas;
🕒 [Link] en PostgreSQL con TO_CHAR()
SELECT
CURRENT_DATE AS FechaActual, -- Fecha del sistema en formato
DATE
TO_CHAR(CURRENT_DATE, 'DD/MM/YYYY') AS Fecha_DD_MM_AAAA, --
Formato europeo
TO_CHAR(CURRENT_DATE, 'MM/DD/YYYY') AS Fecha_MM_DD_AAAA, --
Formato estadounidense
TO_CHAR(CURRENT_DATE, 'YYYYMMDD') AS Fecha_YYYYMMDD -- Fecha
sin símbolos (ideal para exportar o comparar)
3 FACILMENTE:
3️⃣
'texto'::tipo = CAST('texto' AS tipo)
SELECT
'2025-04-18'::DATE AS FechaDesdeTexto, -- Convierte texto en tipo DATE
'123.45'::NUMERIC AS NumeroDesdeTexto -- Convierte string a decimal
[Link] Matemáticas
-- ROUND: Redondea un número a la cantidad de decimales indicada
SELECT Salary, ROUND(Salary, 0) AS SalarioRedondeado
FROM Employees;
-- CEILING: Redondea hacia arriba
SELECT Salary, CEILING(Salary) AS SalarioTecho
FROM Employees;
-- FLOOR: Redondea hacia abajo
SELECT Salary, FLOOR(Salary) AS SalarioPiso
FROM Employees;
[Link]áusulas y Consultas Básicas
-- SELECT con WHERE, DISTINCT, LIKE, WHERE NOT, WHERE IN,
condiciones con AND y BETWEEN
-- Seleccionar empleados con salario entre dos valores y cuyo
nombre empiece con 'A'
SELECT DISTINCT Name, Salary
FROM Employees
WHERE Name LIKE 'A%'
AND Salary BETWEEN 50000 AND 100000;
-- Excluir departamentos específicos
SELECT Name, DepartmentID
FROM Employees
WHERE DepartmentID NOT IN (2, 5);
-- Combinar condiciones usando AND
SELECT Name, Salary
FROM Employees
WHERE Salary > 60000 AND DepartmentID = 3;
[Link]ón y Ordenación
-- Agrupar por DepartmentID y usar HAVING para filtrar grupos
SELECT DepartmentID, COUNT(*) AS TotalEmpleados, AVG(Salary) AS
PromedioSalario
FROM Employees
GROUP BY DepartmentID
HAVING COUNT(*) > 5
ORDER BY PromedioSalario DESC;
[Link] y Funciones de Agregación
-- Uso de funciones de agregación y la cláusula CASE para
clasificar
SELECT
DepartmentID,
COUNT(*) AS TotalEmpleados,
SUM(Salary) AS TotalSalarios,
AVG(Salary) AS PromedioSalario,
CASE
WHEN AVG(Salary) >= 80000 THEN 'Alto'
WHEN AVG(Salary) BETWEEN 50000 AND 79999 THEN 'Medio'
ELSE 'Bajo'
END AS CategoriaSalario
FROM Employees
GROUP BY DepartmentID;
[Link] Avanzadas: Subconsultas
-- Subconsulta para obtener empleados del departamento con el
salario promedio más alto
SELECT Name, Salary
FROM Employees
WHERE DepartmentID = (
SELECT DepartmentID
FROM Employees
GROUP BY DepartmentID
ORDER BY AVG(Salary) DESC
LIMIT 1
);
10.📌 TIPOS DE SUBCONSULTAS EN
SQL SERVER
Las subconsultas en SQL Server son consultas dentro de otra consulta. Se usan en
SELECT, FROM, WHERE, HAVING y permiten extraer datos de manera más flexible.
o como obtener valores adicionales, realizar comparaciones o filtrar los datos.
En términos simples, la subconsulta realiza una operación o cálculo, y el resultado de esa
operación se utiliza en la consulta principal.
Dónde se usan las subconsultas:
● En el SELECT: Para calcular valores agregados o devolver valores que serán
usados por la consulta principal.
● En el WHERE: Para filtrar resultados basados en el resultado de la subconsulta.
● En el FROM: Para tratar una subconsulta como una tabla temporal que se usa en la
consulta principal.
● En el HAVING: Para filtrar los resultados de las funciones de agregación.
📍0. De Extracción
Es una subconsulta no comparativa que sirve para Extraer siempre 1 columna, o mas
columnas para hacer otras de extracción, para luego poder filtrar o comparar. Ej
SELECT
[Link] AS Título_de_la_Serie,
Series.año_lanzamiento AS Año_de_Lanzamiento,
[Link] AS Género,
AVG(Episodios.rating_imdb) AS Rating_Promedio_IMDb
FROM
Series
JOIN
Episodios ON Series.serie_id = Episodios.serie_id
WHERE
[Link] IN ( -- La subconsulta extrae los géneros para
poder compararlos con la columna [Link]
SELECT genero -- La subconsulta extrae la columna
'genero' de la tabla 'Series'
FROM (
SELECT genero, COUNT(*) AS cantidad_de_series --
Extrae los géneros y cuenta las series por cada uno
FROM Series
GROUP BY genero -- Agrupa los géneros para contar las
series
ORDER BY cantidad_de_series DESC -- Ordena los
géneros por la cantidad de series en orden descendente
LIMIT 3 -- Ajuste para limitar la cantidad de géneros
(PostgreSQL no tiene TOP)
) AS Subconsulta -- Alias de la subconsulta
)
GROUP BY
Series.serie_id
ORDER BY
Rating_Promedio_IMDb DESC;
📍 1. Escalar
📌 Devuelve un solo valor (una fila y una columna).
💡 Se usa en SELECT, WHERE o HAVING.
Ejemplo: Mostrar a cada empleado junto con el salario más alto de la empresa
SELECT nombre,
(SELECT MAX(salario) FROM empleados) AS salario_maximo
FROM empleados;
✅ Resultado esperado:
nombre | salario_maximo
--------|--------------
Juan | 5000
Maria | 5000
Carlos | 5000
🔹 La subconsulta obtiene el salario más alto (5000), y se muestra en cada fila.
📍 2. Una Sola Fila
📌 Devuelve una sola fila con una o más columnas.
💡 Se usa con operadores como =, <, >, >=, <=.
Ejemplo: Encontrar el nombre del empleado con el salario más alto
SELECT nombre
FROM empleados
WHERE salario = (SELECT MAX(salario) FROM empleados);
✅ Resultado esperado:
nombre
------
Maria
🔹 La subconsulta obtiene MAX(salario), y la consulta principal busca el empleado con
ese salario.
📍 3. de Varias Filas
📌 Devuelve múltiples filas (pero solo una columna).
💡 Se usa con IN, ANY, ALL, EXISTS.
Ejemplo: Listar empleados que trabajan en departamentos ubicados en Madrid
SELECT nombre
FROM empleados
WHERE id_departamento IN (SELECT id FROM departamentos WHERE
ubicacion = 'Madrid');
🔹 La subconsulta obtiene los id_departamento de Madrid, y la consulta principal lista los
empleados en esos departamentos.
📍 4. Correlacionada
📌 Se ejecuta una vez por cada fila de la consulta principal.
💡 Depende de la tabla externa.
Ejemplo: Mostrar empleados que pertenecen a un departamento en Madrid
SELECT [Link]
FROM empleados e
WHERE EXISTS (SELECT 1 FROM departamentos d WHERE
e.id_departamento = [Link] AND [Link] = 'Madrid');
🔹 La subconsulta verifica si el id_departamento del empleado existe en la tabla
departamentos con ubicación en Madrid.
📍 5. En FROM (Subconsulta en Línea o Tabla Derivada)
📌 Actúa como una tabla temporal dentro del FROM.
Ejemplo: Contar empleados por departamento usando una subconsulta en FROM
SELECT depto, COUNT(*) AS total
FROM (SELECT id_departamento AS depto FROM empleados) AS sub
GROUP BY depto;
🔹 La subconsulta obtiene los id_departamento, y la consulta principal agrupa y cuenta
empleados por departamento.
📍 6. Subconsulta en HAVING
Se usa para filtrar grupos de GROUP BY con valores calculados.
Ejemplo: Mostrar departamentos con salario promedio superior al salario promedio
general
SELECT id_departamento, AVG(salario)
FROM empleados
GROUP BY id_departamento
HAVING AVG(salario) > (SELECT AVG(salario) FROM empleados);
🔹 La subconsulta obtiene el salario promedio general y la consulta principal filtra
departamentos con salarios mayores.
📌 RESUMEN RÁPIDO
Tipo de Descripción
Subconsulta
Escalar Devuelve un solo valor.
Una Sola Fila Devuelve una sola fila con una o más
columnas.
Varias Filas Devuelve varias filas (pero solo una columna).
Correlacionada Se ejecuta por cada fila de la consulta principal.
En FROM (derivada) Actúa como tabla temporal.
En HAVING Filtra grupos de GROUP BY.
[Link] Avanzadas: CTE (Common Table
Expressions)
-- CTE para calcular estadísticas por departamento y luego filtrar
WITH DeptStats AS (
SELECT DepartmentID, COUNT(*) AS TotalEmpleados, AVG(Salary)
AS PromedioSalario
FROM Employees
GROUP BY DepartmentID
)
SELECT DepartmentID, TotalEmpleados, PromedioSalario
FROM DeptStats
WHERE TotalEmpleados > 5;
[Link] de Ventana y Agregación
EN TODO CASO 🎉
--
==============================================================
====
SELECT
CONCAT(Name, ' ', Last_Name) AS full_name,Salary,area,
RANK() OVER (PARTITION BY area ORDER BY Salary desc) AS
RangoSalario
FROM Info_Empresarial ie;
==============================================================
====
SIN PARTITION BY 😡 : Ordenaría el ranking por Salario general
CON PARTITION BY 😀 : Ordena pues el ranking por Salario dentro de un grupo partición,
en este caso, área.
🧩 ¿Qué es PARTITION BY en una window function?
Es como crear mini-grupos dentro de la tabla donde se aplica la función. Te doy unos
ejemplos rápidos:
📊 Sin PARTITION BY:
RANK() OVER (ORDER BY total DESC)
➡ Ranking global: todos los clientes están compitiendo entre sí.
📊 Con PARTITION BY:
RANK() OVER (PARTITION BY country ORDER BY total DESC)
➡ Crea rankings dentro de cada país, reiniciando el conteo de 1 para cada country.
En tu caso, si haces PARTITION BY customer_id, solo hay un cliente por grupo, así que
todos tienen RANK() = 1. Inútil 😅
-- Seleccionamos los datos de los empleados con rankings de
salario dentro de cada departamento
SELECT
EmployeeID, -- ID del empleado
Name, -- Nombre del empleado
DepartmentID, -- ID del departamento al que pertenece
Salary, -- Salario del empleado
-- ROW_NUMBER genera un número único de fila dentro de cada
departamento,
-- ordenando de mayor a menor salario.
ROW_NUMBER() OVER (PARTITION BY DepartmentID ORDER BY Salary
DESC) AS NumFila,
-- RANK genera un ranking basado en el salario,
-- pero si hay empleados con el mismo salario, reciben el
mismo número.
-- El siguiente número en la secuencia se salta valores (ej:
1, 2, 2, 4).
RANK() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC)
AS RangoSalario,
-- DENSE_RANK también genera un ranking donde empleados con el
mismo salario
-- reciben el mismo número, pero SIN saltos en la numeración
(ej: 1, 2, 2, 3).
DENSE_RANK() OVER (PARTITION BY DepartmentID ORDER BY Salary
DESC) AS RangoDenso
FROM Employees;
-- Sintaxis de ROW_NUMBER()
ROW_NUMBER() OVER (PARTITION BY <columnas de partición> ORDER BY
<columnas de orden>)
-- Sintaxis de RANK()
RANK() OVER (PARTITION BY <columnas de partición> ORDER BY
<columnas de orden>)
-- Sintaxis de DENSE_RANK()
DENSE_RANK() OVER (PARTITION BY <columnas de partición> ORDER BY
<columnas de orden>)
-- Ejemplo de OVER con función de agregación (SUM) para calcular
el total salarial por departamento
SELECT
EmployeeID, -- ID del empleado
Name, -- Nombre del empleado
Salary, -- Salario del empleado
-- SUM calcula la suma total de salario para cada departamento
(PARTITION BY DepartmentID),
-- pero no agrupa las filas. Así se puede ver el total de cada
departamento junto con el salario individual.
SUM(Salary) OVER (PARTITION BY DepartmentID) AS TotalDepto
FROM Employees; -- De la tabla Employees
SELECT
CONCAT([Link], [Link]) AS NombreEmpleado,
a.horas_asignadas,
RANK() OVER (PARTITION BY e.depto_id ORDER BY
a.horas_asignadas DESC) AS Mejores_Rendimientos,
-- Ranking por departamento basado en horas asignadas
SUM(a.horas_asignadas) OVER (PARTITION BY e.empleado_id) AS
MasHorasTrabajadas
-- Total de horas trabajadas por empleado
FROM
Empleados e
INNER JOIN
AsignacionesDeProyectos a ON a.empleado_id = e.empleado_id
INNER JOIN
Departamentos d ON e.depto_id = d.depto_id
Nota: Algunos motores (como MySQL) usan REGEXP para expresiones
regulares, por ejemplo:
SELECT Name
FROM Employees
WHERE Name REGEXP '^[A-Z]';
[Link] Temporales
Las tablas temporales son útiles para almacenar datos temporales dentro de una sesión.
TABLA TEMPORAL LOCAL
CREATE TEMP TABLE EmpleadosTemp (
EmployeeID INT PRIMARY KEY,
Name VARCHAR(100),
Salary DECIMAL(10,2)
);
INSERT INTO EmpleadosTemp (EmployeeID, Name, Salary)
SELECT EmployeeID, Name, Salary FROM Employees WHERE Salary >
50000;
SELECT * FROM EmpleadosTemp; -- Consulta la tabla temporal
DROP TABLE EmpleadosTemp; -- Eliminar la tabla cuando ya no se
necesita
TABLA GLOBAL (## puedes ser accedidas por otras sesiones)
CREATE GLOBAL TEMPORARY TABLE EmpleadosGlobal (
EmployeeID INT PRIMARY KEY,
Name VARCHAR(100),
Salary DECIMAL(10,2)
) ON COMMIT DROP; -- Las tablas temporales globales se eliminan
al finalizar la sesión
INSERT INTO EmpleadosGlobal (EmployeeID, Name, Salary)
SELECT EmployeeID, Name, Salary FROM Employees WHERE Salary >
50000;
SELECT * FROM EmpleadosGlobal; -- Consulta la tabla global
-- PostgreSQL elimina la tabla global al finalizar la sesión, si
no se especifica lo contrario
SELECT Name
FROM Employees
WHERE Name ~ '^[A-Z]'; -- Coincide con los nombres que empiezan
con una letra mayúscula
[Link] para Manipulación de Texto
[Link] Conceptos Clave
15.2 Normalización de Bases de Datos (Conceptual)
-- 1NF: Cada columna contiene un valor atómico.
-- 2NF: La tabla está en 1NF y cada columna depende de la clave
primaria completa.
-- 3NF: La tabla está en 2NF y no existen dependencias
transitivas.
15.3 Procedimientos Almacenados
-- Obtener un empleado por ID
CREATE OR REPLACE FUNCTION GetEmployeeByID(emp_id INT)
RETURNS TABLE (employee_id INT, name TEXT, salary NUMERIC,
department_id INT)
AS $$
BEGIN
RETURN QUERY
SELECT employee_id, name, salary, department_id
FROM employees
WHERE employee_id = emp_id;
END;
$$ LANGUAGE plpgsql;
-- Buscar libros por autor
CREATE OR REPLACE FUNCTION BuscarLibrosPorAutor(nombre_autor TEXT)
RETURNS TABLE (titulo TEXT, autor TEXT)
AS $$
BEGIN
RETURN QUERY
SELECT titulo, autor
FROM libros
WHERE autor ILIKE '%' || nombre_autor || '%';
END;
$$ LANGUAGE plpgsql;
15.4Índices
-- Crear un índice para acelerar la búsqueda por nombre
CREATE INDEX idx_EmployeeName ON Employees(Name);
15.5Transacciones
-- Transacción con SAVEPOINT
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
SAVEPOINT sp1;
UPDATE accounts SET balance = balance + 100 WHERE account_id = 2;
-- Para retroceder si hay error:
-- ROLLBACK TO SAVEPOINT sp1;
COMMIT;
15.6Manejo de Errores
DO $$
DECLARE
stock_actual INT;
BEGIN
SELECT stock INTO stock_actual FROM products WHERE product_id
= 1;
IF stock_actual < 10 THEN
RAISE EXCEPTION 'No hay suficiente stock';
END IF;
END;
$$ LANGUAGE plpgsql;
[Link] + TRANSACCIONES
CREATE OR REPLACE FUNCTION RealizarPedido(producto_id INT,
cantidad INT, cliente_id INT)
RETURNS VOID
AS $$
DECLARE
stock_disponible INT;
BEGIN
-- Inicia transacción implícita
SELECT stock INTO stock_disponible FROM productos WHERE
producto_id = producto_id;
IF stock_disponible >= cantidad THEN
INSERT INTO pedidos (cliente_id, producto_id, cantidad)
VALUES (cliente_id, producto_id, cantidad);
UPDATE productos
SET stock = stock - cantidad
WHERE producto_id = producto_id;
ELSE
RAISE EXCEPTION 'No hay suficiente stock para completar el
pedido.';
END IF;
EXCEPTION
WHEN OTHERS THEN
RAISE NOTICE 'Error: %', SQLERRM;
-- La transacción se revierte automáticamente si la
función falla
RAISE;
END;
$$ LANGUAGE plpgsql;
Verifique si hay suficiente stock de un producto para poder realizar un pedido.
Si hay suficiente stock, se crea el pedido.
Si no hay suficiente stock, se lanza un error.
-- Esta función realiza un pedido, actualiza el stock y maneja
errores
CREATE OR REPLACE FUNCTION RealizarPedido(
p_ProductoID INT, -- El ID del producto que el cliente
quiere comprar
p_Cantidad INT, -- La cantidad que se quiere comprar
p_ClienteID INT -- El ID del cliente que hace el
pedido
) RETURNS VOID
AS $$
DECLARE
v_StockDisponible INT; -- Variable para almacenar el stock
actual del producto
BEGIN
-- Obtenemos el stock disponible del producto
SELECT stock INTO v_StockDisponible
FROM productos
WHERE productoid = p_ProductoID;
-- Comprobamos si hay suficiente stock
IF v_StockDisponible >= p_Cantidad THEN
-- Insertamos el nuevo pedido
INSERT INTO pedidos (clienteid, productoid, cantidad)
VALUES (p_ClienteID, p_ProductoID, p_Cantidad);
-- Actualizamos el stock del producto
UPDATE productos
SET stock = stock - p_Cantidad
WHERE productoid = p_ProductoID;
ELSE
-- Si no hay suficiente stock, lanzamos una excepción
RAISE EXCEPTION 'No hay suficiente stock para completar el
pedido.';
END IF;
-- Manejo de errores
EXCEPTION
WHEN OTHERS THEN
-- Muestra el mensaje de error (como lo haría RAISERROR en
SQL Server)
RAISE NOTICE 'Error al procesar el pedido: %', SQLERRM;
-- Re-lanzamos el error para forzar rollback
RAISE;
END;
$$ LANGUAGE plpgsql;
17. Vistas
CREATE VIEW vw_EmpleadosBasicos AS
SELECT EmployeeID, Name, DepartmentID
FROM Employees;
[Link]
CREATE TRIGGER trg_AfterInsertEmployee
ON Employees
AFTER INSERT
AS
BEGIN
PRINT 'Se ha insertado un nuevo empleado.';
END;
[Link] de Fechas (SQL Server)
DATEPART(YEAR, finish_date) AS Año,
DATEPART(MONTH, finish_date) AS Mes,
DATEPART(DAY, finish_date) AS Día,
DATEPART(HOUR, finish_date) AS Hora,
DATEPART(MINUTE, finish_date) AS Minuto,
DATEPART(SECOND, finish_date) AS Segundo
-- Obtener la fecha y hora actual
SELECT GETDATE() AS FechaActual;
-- Extraer partes de la fecha: Año, Mes, Día
SELECT YEAR(GETDATE()) AS AñoActual,
MONTH(GETDATE()) AS MesActual,
DAY(GETDATE()) AS DiaActual;
-- Convertir una fecha a un formato específico
SELECT CONVERT(DATE, GETDATE(), 1) AS FechaFormateada;
-- Sumar 5 días a la fecha actual
SELECT DATEADD(DAY, 5, GETDATE()) AS FechaMas5Dias;
-- Calcular la diferencia en días entre dos fechas
SELECT DATEDIFF(DAY, '2025-01-01', GETDATE()) AS DiferenciaDias;
-- Obtener el nombre del mes y del día de la semana
SELECT DATENAME(MONTH, GETDATE()) AS NombreMes,
DATENAME(WEEKDAY, GETDATE()) AS NombreDiaSemana;
-- Extraer una parte específica de la fecha (por ejemplo, la hora)
SELECT DATEPART(HOUR, GETDATE()) AS HoraActual;
CONVERT(tipo_de_dato, expresión, estilo)
SELECT
GETDATE() AS FechaActual,
CONVERT(VARCHAR, GETDATE(), 103) AS Fecha_DD_MM_AAAA,
CONVERT(VARCHAR, GETDATE(), 101) AS Fecha_MM_DD_AAAA,
CONVERT(VARCHAR, GETDATE(), 112) AS Fecha_YYYYMMDD
📌 Claves:
● 103 → DD/MM/YYYY (formato europeo).
● 101 → MM/DD/YYYY (formato USA).
● 112 → YYYYMMDD (ideal para exportar datos sin caracteres especiales).
CONVERT(tipo_de_dato, expresión, estilo)
–Convierte una columna en date, con el estilo 101.
SELECT birth_date, CONVERT(DATE, birth_date, 101) AS
FechaConvertida
FROM Info_Empresarial;
SYNTAXIS
CONVERT(TIPO_DE_DATO, EXPRESIÓN, ESTILO)
ESTILOS
🎯¿SQL Server evita borrar la hora en DATETIME
automáticamente?
Sí. SQL Server nunca borra la hora de un DATETIME solo porque le pongas un estilo
de fecha. Si el campo es DATETIME, la hora sigue allí a menos que tú específicamente lo
conviertas a DATE primero.
🎯 Tabla de Estilos de CONVERT() en SQL Server (Salida
Formateada)
SELECT CONVERT(VARCHAR, GETDATE(), <estilo>)
Código Formato de Salida Ejemplo (2025- ¿Funcion ¿Funciona
(<estilo> (VARCHAR) 03-16 a con con
) 14:30:45.123) DATE? DATETIME?
100 Mon DD YYYY Mar 16 2025 ❌ ✅
HH:MIAM/PM 2:30PM
101 MM/DD/YYYY (USA) 03/16/2025 ✅ ✅ (Pero no
borra la
hora)
102 [Link] (ISO) 2025.03.16 ✅ ✅ (Pero no
borra la
hora)
103 DD/MM/YYYY (Europa) 16/03/2025 ✅ ✅ (Pero no
borra la
hora)
104 [Link] 16.03.2025 ✅ ✅ (Pero no
(Alemania) borra la
hora)
105 DD-MM-YYYY (Formato 16/03/2025 ✅ ✅ (Pero no
LATAM) borra la
hora)
106 DD Mon YYYY 16-mar-25 ✅ ✅ (Pero no
borra la
hora)
107 Mon DD, YYYY Mar 16, 2025 ✅ ❌ (Muestra
la hora si
no
conviertes
a DATE)
108 HH:MI:SS (24H) 14:30:45 ❌ ✅
109 Mon DD YYYY Mar 16 2025 ❌ ✅
HH:MI:SS:mmmAM/PM 2:30:45:123PM
110 MM-DD-YYYY 03-16-2025 ✅ ✅ (Pero no
borra la
hora)
111 YYYY/MM/DD 16/03/2025 ✅ ✅ (Pero no
borra la
hora)
112 YYYYMMDD (Sin 20250316 ✅ ✅ (Pero no
separadores) borra la
hora)
113 DD Mon YYYY 16 Mar 2025 ❌ ✅
HH:MI:SS:mmm 14:30:45:123
114 HH:MI:SS:mmm (24H) 14:30:45:123 ❌ ✅
120 YYYY-MM-DD HH:MI:SS 16/03/2025 ❌ ✅
(ISO) 2:30:45 p. m.
121 YYYY-MM-DD 2025-03-16 ❌ ✅
HH:MI:[Link] 14:30:45.123
126 YYYY-MM- 2025-03- ❌ ✅
DDTHH:MI:[Link] 16T14:30:45.123
(ISO 8601)
SELECT
GETDATE() AS Original,
CONVERT(VARCHAR, GETDATE(), 107) AS Estilo107,
CONVERT(VARCHAR, CAST(GETDATE() AS DATE), 107) AS
Estilo107_Corregido
Original | Estilo107 | Estilo107_Corregido
----------------------|----------------------|--------------------
2025-03-16 14:30:45 | Mar 16, 2025 14:30:45 | Mar 16, 2025
❌ 107 NO borró la hora en DATETIME.
✅ Al castear a DATE primero, ahora sí funciona bien.
📌DE ENTRADA A DATE
Estil Formato de Entrada Ejemplo Entrada Ejemplo Convertido
(DATE)
0 Mon DD YYYY HH:MI:SS Mar 16 2025 2025-03-16
AM/PM 02:30:45 PM
1 MM/DD/YY (USA) 03/16/25 2025-03-16
101 MM/DD/YYYY (USA) 03/16/2025 2025-03-16
2 [Link] 25.03.16 2025-03-16
102 [Link] 2025.03.16 2025-03-16
3 DD/MM/YY (Europa) 16/03/25 2025-03-16
103 DD/MM/YYYY (Europa) 16/03/2025 2025-03-16
4 [Link] 16.03.25 2025-03-16
104 [Link] 16.03.2025 2025-03-16
5 DD-MM-YY 16-03-25 2025-03-16
105 DD-MM-YYYY 16-03-2025 2025-03-16
6 DD MMM YY 16 Mar 25 2025-03-16
106 DD MMM YYYY 16 Mar 2025 2025-03-16
7 MMM DD, YY Mar 16, 25 2025-03-16
107 MMM DD, YYYY Mar 16, 2025 2025-03-16
8 HH:MI:SS (24H) 14:30:45 14:30:45
108 HH:MI:SS (24H) 14:30:45 14:30:45
9 Mon DD YYYY Mar 16 2025 2025-03-16
HH:MI:SS:MMM AM/PM 02:30:45:123 PM 14:30:45.123
109 Mon DD YYYY Mar 16 2025 2025-03-16
HH:MI:SS:MMM AM/PM 02:30:45:123 PM 14:30:45.123
10 MM-DD-YY 03-16-25 2025-03-16
110 MM-DD-YYYY 03-16-2025 2025-03-16
11 YY/MM/DD 25/03/16 2025-03-16
111 YYYY/MM/DD 2025/03/16 2025-03-16
12 YYMMDD 250316 2025-03-16
112 YYYYMMDD 20250316 2025-03-16
13 DD Mon YYYY 16 Mar 2025 2025-03-16
HH:MI:SS:MMM (24H) 14:30:45:123 14:30:45.123
113 DD Mon YYYY 16 Mar 2025 2025-03-16
HH:MI:SS:MMM (24H) 14:30:45:123 14:30:45.123
14 HH:MI:SS:MMM AM/PM 02:30:45:123 PM 14:30:45.123
114 HH:MI:SS:MMM (24H) 14:30:45:123 14:30:45.123
20 YYYY-MM-DD HH:MI:SS 2025-03-16 2025-03-16
14:30:45 14:30:45
120 YYYY-MM-DD HH:MI:SS 2025-03-16 2025-03-16
14:30:45 14:30:45
21 YYYY-MM-DD 2025-03-16 2025-03-16
HH:MI:[Link] 14:30:45.123 14:30:45.123
121 YYYY-MM-DD 2025-03-16 2025-03-16
HH:MI:[Link] 14:30:45.123 14:30:45.123
126 YYYY-MM- 2025-03- 2025-03-16
DDTHH:MI:[Link] (ISO 16T14:30:45.123 14:30:45.123
8601)
127 YYYY-MM- 2025-03- 2025-03-16
DDTHH:MI:[Link] (ISO 16T14:30:45.123456 14:30:45.123456
8601)
📌 Diferencias entre CAST() y CONVERT() en SQL Server
Función Uso principal Notas
CAST() Convierte un dato a otro tipo Es más estándar (funciona en otros
(DATE, DATETIME, TIME) motores de bases de datos como
PostgreSQL y Oracle).
CONVERT Convierte y permite formatos con Más flexible para formatear fechas, pero
() un código de estilo (101, 103, menos compatible con otros motores.
120, etc.)
FORMAT( Formatea la salida de una fecha No cambia el tipo de dato, solo la
) como VARCHAR (YYYY-MM-DD apariencia. Más lento que CONVERT().
HH:mm:ss, etc.)