49 - Subconsultas correlacionadas

Un almacén almacena la información de sus ventas en una tabla llamada "facturas" en la cual guarda el número de factura y el nombre del cliente y una tabla denominada "detalles" en la cual se almacenan los distintos items correspondientes a cada factura: el nombre del artículo, el precio (unitario) y la cantidad.
Se necesita una lista de todas las facturas que incluya el número, el cliente, la cantidad de artículos comprados y el total:

select f.*,
  (select count(d.numeroitem)
    from Detalles as d
    where f.numero=d.numerofactura) as cantidad,
  (select sum(d.preciounitario*cantidad)
    from Detalles as d
    where f.numero=d.numerofactura) as total
from facturas as f;

El segundo "select" retorna una lista de valores de una sola columna con la cantidad de items por factura (el número de factura lo toma del "select" exterior); el tercer "select" retorna una lista de valores de una sola columna con el total por factura (el número de factura lo toma del "select" exterior); el primer "select" (externo) devuelve todos los datos de cada factura.

A este tipo de subconsulta se la denomina consulta correlacionada. La consulta interna se evalúa tantas veces como registros tiene la consulta externa, se realiza la subconsulta para cada registro de la consulta externa. El campo de la tabla dentro de la subconsulta (f.numero) se compara con el campo de la tabla externa.

En este caso, específicamente, la consulta externa pasa un valor de "numero" a la consulta interna. La consulta interna toma ese valor y determina si existe en "detalles", si existe, la consulta interna devuelve la suma. El proceso se repite para el registro de la consulta externa, la consulta externa pasa otro "numero" a la consulta interna y SQLite repite la evaluación.

Problema resuelto

Un almacén almacena la información de sus ventas en una tabla llamada "facturas" en la cual guarda el número de factura y el nombre del cliente y una tabla denominada "detalles" en la cual se almacenan los distintos items correspondientes a cada factura: el nombre del artículo, el precio (unitario) y la cantidad.

Borrar las dos tablas si existen:

 drop table if exists facturas;
 drop table if exists detalles;

Creamos las dos tablas con las siguientes estructuras:

 create table facturas(
  numero integer primary key,
  cliente text
 );

 create table detalles(
  numerofactura integer not null,
  numeroitem integer not null, 
  articulo text,
  precio real,
  cantidad integer,
  primary key(numerofactura,numeroitem)
 );

Almacenamos algunas filas en cada tabla:

 insert into facturas(numero,cliente) values(1200,'Juan Lopez');
 insert into facturas(numero,cliente) values(1201,'Luis Torres');
 insert into facturas(numero,cliente) values(1202,'Ana Garcia');
 insert into facturas(numero,cliente) values(1300,'Juan Lopez');

 insert into detalles(numerofactura,numeroitem,articulo,precio,cantidad) values(1200,1,'lapiz',1,100);
 insert into detalles(numerofactura,numeroitem,articulo,precio,cantidad)  values(1200,2,'goma',0.5,150);
 insert into detalles(numerofactura,numeroitem,articulo,precio,cantidad)  values(1201,1,'regla',1.5,80);
 insert into detalles(numerofactura,numeroitem,articulo,precio,cantidad)  values(1201,2,'goma',0.5,200);
 insert into detalles(numerofactura,numeroitem,articulo,precio,cantidad)  values(1201,3,'cuaderno',4,90);
 insert into detalles(numerofactura,numeroitem,articulo,precio,cantidad)  values(1202,1,'lapiz',1,200);
 insert into detalles(numerofactura,numeroitem,articulo,precio,cantidad)  values(1202,2,'escuadra',2,100);
 insert into detalles(numerofactura,numeroitem,articulo,precio,cantidad)  values(1300,1,'lapiz',1,300); 

Se necesita una lista de todas las facturas que incluya el número, el cliente, la cantidad de artículos comprados y el total:

 select f.*,
  (select count(d.numeroitem)
    from detalles as d
    where f.numero=d.numerofactura) as cantidad,
  (select sum(d.precio*cantidad)
    from detalles as d
    where f.numero=d.numerofactura) as total
 from facturas as f; 

Problema propuesto

Un club dicta clases de distintos deportes a sus socios. El club tiene una tabla llamada "inscriptos" en la cual almacena el número de "socio", el código del deporte en el cual se inscribe y la cantidad de cuotas pagas (desde 0 hasta 10 que es el total por todo el año), y una tabla denominada "socios" en la que guarda los datos personales de cada socio.

  1. Borrar las tablas "socios" e "inscriptos".

  2. Crear las tablas con las siguientes estructuras:

     create table socios(
      numero integer primary key,
      documento text,
      nombre text,
      domicilio text
     );
     
     create table inscriptos (
      numerosocio integer not null,
      deporte text not null,
      cuotas integer,
      primary key(numerosocio,deporte)
     );
    
  3. Almacenar algunas filas de ejemplo:

     insert into socios(documento,nombre,domicilio) values('23333333','Alberto Paredes','Colon 111');
     insert into socios(documento,nombre,domicilio) values('24444444','Carlos Conte','Sarmiento 755');
     insert into socios(documento,nombre,domicilio) values('25555555','Fabian Fuentes','Caseros 987');
     insert into socios(documento,nombre,domicilio) values('26666666','Hector Lopez','Sucre 344');
    
     insert into inscriptos values(1,'tenis',1);
     insert into inscriptos values(1,'basquet',2);
     insert into inscriptos values(1,'natacion',1);
     insert into inscriptos values(2,'tenis',9);
     insert into inscriptos values(2,'natacion',1);
     insert into inscriptos values(2,'basquet',0);
     insert into inscriptos values(2,'futbol',2);
     insert into inscriptos values(3,'tenis',8);
     insert into inscriptos values(3,'basquet',9);
     insert into inscriptos values(3,'natacion',0);
     insert into inscriptos values(4,'basquet',10);
    
  4. Se necesita un listado de todos los socios que incluya nombre y domicilio, la cantidad de deportes a los cuales se ha inscripto, empleando subconsulta.

  5. Se necesita el nombre de todos los socios, el total de cuotas que debe pagar (10 por cada deporte) y el total de cuotas pagas, empleando subconsulta.

  6. Obtenga la misma salida anterior empleando join.

Solución
drop table if exists socios;
drop table if exists inscriptos;

 create table socios(
  numero integer primary key,
  documento text,
  nombre text,
  domicilio text
 );
 
 create table inscriptos (
  numerosocio integer not null,
  deporte text not null,
  cuotas integer,
  primary key(numerosocio,deporte)
 );
 
 insert into socios(documento,nombre,domicilio) values('23333333','Alberto Paredes','Colon 111');
 insert into socios(documento,nombre,domicilio) values('24444444','Carlos Conte','Sarmiento 755');
 insert into socios(documento,nombre,domicilio) values('25555555','Fabian Fuentes','Caseros 987');
 insert into socios(documento,nombre,domicilio) values('26666666','Hector Lopez','Sucre 344');

 insert into inscriptos values(1,'tenis',1);
 insert into inscriptos values(1,'basquet',2);
 insert into inscriptos values(1,'natacion',1);
 insert into inscriptos values(2,'tenis',9);
 insert into inscriptos values(2,'natacion',1);
 insert into inscriptos values(2,'basquet',0);
 insert into inscriptos values(2,'futbol',2);
 insert into inscriptos values(3,'tenis',8);
 insert into inscriptos values(3,'basquet',9);
 insert into inscriptos values(3,'natacion',0);
 insert into inscriptos values(4,'basquet',10);
 

 select nombre,domicilio,
  (select count(*)
    from inscriptos as i
    where s.numero=i.numerosocio) as deportes
 from socios as s;

 select nombre,
  (select (count(*)*10)
    from inscriptos as i
    where s.numero=i.numerosocio) as total,
  (select sum(i.cuotas)
    from inscriptos as i
    where s.numero=i.numerosocio) as pagas
 from socios as s; 
 
 select nombre,
  count(i.deporte)*10 as total,
  sum(i.cuotas) as pagas
  from socios as s
  join inscriptos as i
  on numero=numerosocio
  group by nombre; 

Ejecución de ejercicios online

Puede ejecutar comandos de SQLite directamente en el sitio sin tener que instalar nada en su computadora.

Resultado.....