Las vistas en bases de datos, ¿qué son y cómo se usan?

feb. 15, 2025

Este artículo forma parte de una serie:

A 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.

¿Qué son las vistas en bases de datos?

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.

Casos de uso

Las vistas son herramientas extremadamente útiles en el mundo de las bases de datos y se pueden utilizar en múltiples escenarios:

  • Simplificación de consultas complejas: En lugar de escribir largas y complicadas consultas SQL cada vez que se necesiten ciertos datos, se puede crear una vista que encapsule esa lógica y facilitar su reutilización.
  • Seguridad y control de acceso: Las vistas pueden restringir el acceso a ciertos datos sensibles, mostrando solo la información necesaria a los usuarios sin exponer datos internos confidenciales.
  • Compatibilidad y estabilidad en aplicaciones: Si una aplicación depende de una estructura de datos específica, una vista puede servir como un “puente” entre la estructura actual y la nueva sin necesidad de modificar la aplicación.
  • Optimización del rendimiento: Aunque las vistas no almacenan datos, en algunos motores de bases de datos existen las vistas materializadas, que sí guardan los resultados de la consulta para mejorar el rendimiento en casos de uso intensivo.

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:

  • customers – Información de los clientes.
  • orders – Pedidos realizados por los clientes.
  • order_items – Productos incluidos en cada pedido.
  • products – Lista de productos disponibles en la tienda.
  • payments – Pagos asociados a los pedidos.

Creación de la base de datos en PostgreSQL

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:

  • Host: localhost
  • Port: 5432
  • Database: ecommerce
  • User: admin
  • Password: admin

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
  );
    

Inserción de datos

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);
    

Creación de vistas en PostgreSQL

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.

Vista de pedidos


  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.

Vista de productos más vendidos


  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.

Vista de ingresos mensuales


  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.

Vista de pedidos pendientes de pago


  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:

Vista detallada de pedidos impagos


  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.

Conclusión

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!

Artículos relacionados

Quizá te puedan interesar