Introducción a PostgreSQL, uno de los motores de …
En esta serie hemos explorado diversos motores de bases de datos, como SQLite, y los conceptos …
leer másA medida que las bases de datos crecen y se vuelven más complejas, administrar y acceder a los datos de manera eficiente puede convertirse en un desafío. Aquí es donde entran en juego las vistas. Una vista es una especie de “ventana” a los datos almacenados en una base de datos. Se puede pensar en una vista como una consulta predefinida que muestra un subconjunto de los datos almacenados en una o varias tablas. Sin embargo, a diferencia de una tabla tradicional, una vista no almacena físicamente los datos; simplemente actúa como una capa de abstracción sobre ellos.
Imagina que tienes una base de datos de una tienda en línea con múltiples tablas: clientes, pedidos, productos, pagos, etc. Si un analista de negocio solo necesita ver un resumen de las compras de los clientes sin preocuparse por la compleja estructura de la base de datos, una vista puede simplificar esta tarea al presentar la información de forma clara y accesible.
Las vistas son herramientas extremadamente útiles en el mundo de las bases de datos y se pueden utilizar en múltiples escenarios:
A pesar de sus ventajas, las vistas no son la solución perfecta para todo. En algunos casos, pueden afectar el rendimiento si se usan de manera incorrecta, especialmente cuando se ejecutan sobre consultas muy pesadas o en bases de datos con grandes volúmenes de datos. Además, algunas vistas no permiten modificaciones directas sobre los datos, lo que puede ser una limitación en ciertos escenarios.
Para llevar estos conceptos a la práctica, trabajaremos directamente con un conjunto de datos que refleje un caso de uso realista. Crearemos una base de datos con la estructura típica de una tienda en línea y utilizaremos vistas para extraer información útil de forma eficiente.
El objetivo es demostrar cómo las vistas pueden simplificar consultas, optimizar el acceso a los datos y facilitar la gestión de información clave, como el estado de los pedidos, los productos más vendidos o los pagos recibidos. Así quedaría la estructura de la base de datos:
Como siempre, virtualizamos con Docker una base de datos PostgreSQL para trabajar con ella. Para ello, ejecutamos el siguiente comando:
docker run -d \
--name ecommerce_db \
-e POSTGRES_USER=admin \
-e POSTGRES_PASSWORD=admin \
-e POSTGRES_DB=ecommerce \
-p 5432:5432 \
-v pgdata:/var/lib/postgresql/data \
postgres:latest``
Podemos conectar a ella de la forma más cómoda disponible para nosotros, ya sea con una herramienta gráfica como DBeaver
o con la línea de comandos. Personalmente, prefiero interfaces gráficas siempre y cuándo permitan utilizar las funcionalidades
más importantes y habituales, por lo que usaremos DBeaver. Para ello, simplemente debemos añadir una
nueva conexión PostgreSQL
con los siguientes datos:
Una vez conectados a la base de datos tendremos que crear las tablas necesarias para nuestro ejemplo:
-- Crear la tabla de clientes
CREATE TABLE customers (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL
);
-- Crear la tabla de pedidos
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
customer_id INT REFERENCES customers(id) ON DELETE CASCADE,
total DECIMAL(10,2) NOT NULL DEFAULT 0.00,
order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Crear la tabla de productos
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(150) NOT NULL,
price DECIMAL(10,2) NOT NULL,
stock INT NOT NULL DEFAULT 0
);
-- Crear la tabla de items de pedido
CREATE TABLE order_items (
id SERIAL PRIMARY KEY,
order_id INT REFERENCES orders(id) ON DELETE CASCADE,
product_id INT REFERENCES products(id) ON DELETE CASCADE,
quantity INT NOT NULL CHECK (quantity > 0)
);
-- Crear la tabla de pagos
CREATE TABLE payments (
id SERIAL PRIMARY KEY,
order_id INT REFERENCES orders(id) ON DELETE CASCADE,
amount DECIMAL(10,2) NOT NULL,
payment_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Después, utilizamos cualquier algoritmo de IA para que nos “proporcione las cláusulas INSERT
para rellenar las tablas con
datos ficticios”. Para esto, entre otras cosas, son muy buenos, yo he utilizado ChatGTP y esto es lo que me ha recomendado:
-- Insertar clientes (50 registros)
INSERT INTO customers (name, email)
SELECT 'Cliente ' || i,
'cliente' || i || '@correo.com'
FROM generate_series(1, 50) AS i;
-- Insertar productos (30 registros)
INSERT INTO products (name, price, stock)
SELECT 'Producto ' || i,
ROUND((RANDOM() * 90 + 10):: NUMERIC, 2), -- Precio entre 10 y 100
(RANDOM() * 100)::INT -- Stock entre 0 y 100
FROM generate_series(1, 30) AS i;
-- Insertar pedidos (100 registros, distribuidos entre los clientes)
INSERT INTO orders (customer_id, total, order_date)
SELECT (RANDOM() * 49 + 1)::INT, -- Cliente aleatorio entre 1 y 50 0.00, -- Se actualizará luego con el total real NOW() - INTERVAL '1 day' * (RANDOM() * 60)::INT -- Fechas en los últimos 60 días
FROM generate_series(1, 100);
-- Insertar items en los pedidos (200 registros, aleatorios entre productos y pedidos)
INSERT INTO order_items (order_id, product_id, quantity)
SELECT (RANDOM() * 99 + 1)::INT, -- Pedido aleatorio entre 1 y 100 (RANDOM() * 29 + 1)::INT, -- Producto aleatorio entre 1 y 30 (RANDOM() * 5 + 1)::INT -- Cantidad entre 1 y 5
FROM generate_series(1, 200);
-- Calcular el total de cada pedido y actualizarlo
UPDATE orders
SET total = (SELECT COALESCE(SUM(p.price * oi.quantity), 0)
FROM order_items oi
JOIN products p ON oi.product_id = p.id
WHERE oi.order_id = orders.id);
-- Insertar pagos (80 pagos aleatorios, algunos pedidos quedarán sin pagar)
INSERT INTO payments (order_id, amount, payment_date)
SELECT (RANDOM() * 99 + 1)::INT, -- Pedido aleatorio entre 1 y 100 (RANDOM() * 200 + 20)::NUMERIC(10,2), -- Monto entre 20 y 220 NOW() - INTERVAL '1 day' * (RANDOM() * 60)::INT -- Fechas en los últimos 60 días
FROM generate_series(1, 80);
Ahora que ya tenemos nuestra base de datos rellena con datos ficticios, podemos crear algunas vistas que nos ayuden a extraer información útil de manera más sencilla y eficiente. Uno de los casos más comunes en un ecommerce es obtener un listado de pedidos con información de los clientes, sin necesidad de hacer múltiples JOINs en cada consulta.
CREATE VIEW customer_orders AS
SELECT o.id AS order_id,
c.name AS customer_name,
c.email AS customer_email,
o.total,
o.order_date
FROM orders o
JOIN customers c ON o.customer_id = c.id;
En este caso, hemos creado una vista llamada customer_orders
que muestra información relevante de los pedidos junto
con los datos de los clientes asociados. Ahora, en lugar de tener que escribir una consulta semi compleja cada vez que
necesitemos esta información, podemos consultar customer_orders
para obtener los resultados:
SELECT * FROM customer_orders;
Podríamos también querer analizar los productos más populares en la tienda, creando por ejemplo, una vista que nos muestre la cantidad total de unidades vendidas por producto.
CREATE VIEW top_selling_products AS
SELECT
p.id AS product_id,
p.name AS product_name,
SUM(oi.quantity) AS total_sold
FROM order_items oi
JOIN products p ON oi.product_id = p.id
GROUP BY p.id, p.name
ORDER BY total_sold DESC;
SELECT * FROM top_selling_products;
O quizá necesitemos los ingresos mensuales de nuestra tienda. Para ello creamos una vista que nos proporcione esta información de manera clara y concisa.
CREATE VIEW monthly_revenue AS
SELECT
TO_CHAR(payment_date, 'YYYY-MM') AS month,
SUM(amount) AS total_revenue
FROM payments
GROUP BY month
ORDER BY month DESC;
SELECT * FROM monthly_revenue;
Algunas órdenes pueden haber sido generadas sin un pago asociado. Esta vista nos ayudará a identificar pedidos que aún no han sido pagados.
CREATE VIEW unpaid_orders AS
SELECT
o.id AS order_id,
c.name AS customer_name,
o.total,
o.order_date
FROM orders o
JOIN customers c ON o.customer_id = c.id
LEFT JOIN payments p ON o.id = p.order_id
WHERE p.id IS NULL;
SELECT * FROM unpaid_orders;
Para terminar, crearemos una vista que además de mostrar los pedidos que aun no han sido pagados nos proporcioe a mayores los datos del cliente entre otros:
CREATE VIEW detailed_unpaid_orders AS
SELECT
o.id AS order_id,
c.name AS customer_name,
c.email AS customer_email,
o.order_date,
CURRENT_DATE - o.order_date AS days_since_order,
o.total AS order_total,
COALESCE(SUM(p.amount), 0) AS total_paid,
o.total - COALESCE(SUM(p.amount), 0) AS amount_due,
MAX(p.payment_date) AS last_payment_date,
CASE
WHEN SUM(p.amount) IS NULL THEN 'Pendiente'
WHEN SUM(p.amount) < o.total THEN 'Parcialmente Pagado'
ELSE 'Pagado'
END AS order_status
FROM orders o
JOIN customers c ON o.customer_id = c.id
LEFT JOIN payments p ON o.id = p.order_id
GROUP BY o.id, c.name, c.email, o.order_date;
Bastará la simple consulta de la vista detailed_unpaid_orders
para obtener toda la información necesaria sobre los pedidos que aun no han sido pagados:
SELECT * FROM detailed_unpaid_orders;
De esta forma, podemos, además de facilitar la consulta de información en la propia base de datos, utilizarla como “plantilla” para generar informes en nuestra aplicación, hay ocasiones en las que no es necesario complicar la lógica de la aplicación y es mucho más sencillo y eficiente traer los datos ya procesados desde la base de datos.
Como decíamos al principio, no todo deberían ser vistas ni todo debería ser procesado en la base de datos, dependiendo de la situación y de las necesidades de la aplicación, deberemos elegir una u otra opción teniendo en cuenta todos los factores implicados en cada uno de los procesos.
Espero haberte ayudado a entender un poco mejor qué son las vistas en bases de datos si es que aun no las conocías y si ya las conocías, espero haberte ayudado a refrescar un poco la memoria. Te invito a seguir investigando sobre este tema y a que me cuentes tus experiencias pero sobre todo ¡Happy Coding!
Quizá te puedan interesar
En esta serie hemos explorado diversos motores de bases de datos, como SQLite, y los conceptos …
leer másEn el capítulo anterior, repasamos los orígenes y fundamentos de las bases de datos. Hicimos un …
leer másHoy hablaremos de bases de datos, son, muchas veces, la columna vertebral de sistemas informáticos …
leer másDe concepto a realidad