English version (when available)
SELECT a.cod FROM articulo a WHERE marca = 'Samsung' AND cod IN (SELECT articulo FROM linped); -- sin problema, linped.articulo no admite nulosSolución alternativa:SELECT a.cod FROM articulo a WHERE marca = 'Samsung' AND EXISTS (SELECT 1 FROM linped l WHERE a.cod = l.articulo); -- otra solución SELECT DISTINCT a.cod FROM articulo a JOIN linped l ON a.cod = articulo WHERE marca = 'Samsung';
SELECT nombre FROM articulo WHERE cod IN ( SELECT cod FROM camara WHERE tipo LIKE '%compacta%visor%electrónico%' UNION SELECT cod FROM tv WHERE panel LIKE '%televisor%CRT%');Solución alternativa:SELECT nombre FROM articulo a WHERE EXISTS ( SELECT 1 FROM camara c WHERE c.cod = a.cod AND c.tipo LIKE '%compacta%visor%electrónico%' ) OR EXISTS ( SELECT 1 FROM tv t WHERE t.cod = a.cod AND t.panel LIKE '%televisor%CRT%' );
SELECT u.nombre, p.nombre, l.pueblo FROM usuario u JOIN localidad l ON u.pueblo = l.codm and u.provincia = l.provincia JOIN provincia p ON l.provincia = p.codp WHERE l.pueblo LIKE '%San Vicente%' OR p.nombre LIKE '%Valencia%';No siempre es necesario UNION
SELECT u.nombre, email FROM usuario u join provincia p on u.provincia = p.codp where p.nombre = 'Asturias' AND email NOT IN (SELECT email FROM direnvio) -- sin problema, email no admite nulosSolución alternativa:SELECT u.nombre, email FROM usuario u join provincia p on u.provincia = p.codp where p.nombre = 'Asturias' AND NOT EXISTS (SELECT 1 FROM direnvio d WHERE u.email=d.email)
SELECT a.cod, nombre, marca FROM objetivo o join articulo a on a.cod = o.cod WHERE (focal = '500 mm' OR focal = '600 mm') AND marca IN ( -- marcas de las que no hay líneas de pedido del 11/2010 SELECT marca FROM marca except SELECT marca FROM pedido p JOIN linped l ON l.numpedido=p.numpedido JOIN articulo a ON l.articulo=a.cod WHERE p.fecha BETWEEN '2010-11-01' and '2010-11-30' );Solución alternativa:En este caso, el uso de IN no supone ningún problema porque el operando izquierdo del EXCEPT es MARCA.marca, clave primaria, por lo que la operación no devolverá nulos.SELECT a.cod, nombre, marca FROM objetivo o join articulo a on a.cod = o.cod WHERE (focal = '500 mm' or focal = '600 mm') AND NOT exists ( SELECT 1 FROM pedido p JOIN linped l ON p.numpedido=l.numpedido JOIN articulo a2 ON l.articulo= a2.cod WHERE fecha>='2010-11-01' and p.fecha<='2010-11-30' AND a.marca=a2.marca );
En cualquier caso, la forma de "olvidarse" de los nulos y el operador IN es recurrir a la solucíon alternativa con EXISTS.
SELECT a.cod, pvp FROM articulo a WHERE marca = 'Samsung' AND pvp IS NOT NULL AND a.cod NOT IN (select articulo from linped); -- sin problema, linped.articulo no permite nulosSolución alternativa:SELECT a.cod, pvp FROM articulo a WHERE marca = 'Samsung' AND pvp IS NOT NULL AND NOT EXISTS (SELECT 1 from linped l WHERE a.cod=l.articulo);
SELECT nombre FROM articulo WHERE cod IN (SELECT articulo FROM ptienea);Solución alternativa:SELECT nombre FROM articulo WHERE EXISTS (SELECT 1 FROM ptienea WHERE cod = articulo); -- ------------------- SELECT distinct nombre FROM articulo a,ptienea p where a.cod=p.articulo;
SELECT distinct l.pueblo FROM usuario u1, usuario u2, localidad l WHERE u1.email != u2.email -- que sean usuarios diferentes AND u1.pueblo = u2.pueblo AND u1.provincia = u2.provincia -- de la misma localidad AND u1.pueblo=l.codm AND u1.provincia=l.provincia; -- enlazado con LOCALIDAD para conseguir el nombre, me da igual si esde un usuario o del otroSolución alternativa:Compáralo con la solución usando group by-having. SELECT l.pueblo FROM localidad l JOIN usuario u ON u.pueblo = l.codm AND u.provincia=l.provincia GROUP BY l.codm, l.provincia, l.pueblo HAVING COUNT(*) >= 2;
select articulo from stock INTERSECT select articulo from cesta INTERSECT select articulo from linped order by articulo;Solución alternativa:SELECT DISTINCT s.articulo FROM stock s join cesta c on s.articulo = c.articulo join linped l on s.articulo = l.articulo order by s.articulo;
SELECT p.articulo, a.nombre FROM ptienea p JOIN articulo a ON p.articulo = a.cod UNION ALL SELECT c.articulo, a.nombre FROM cesta c JOIN articulo a ON c.articulo = a.cod;Solución alternativa:SELECT p.articulo, a.nombre FROM ptienea p JOIN articulo a ON (p.articulo = a.cod) UNION ALL SELECT c.articulo, a.nombre FROM cesta c JOIN articulo a ON (c.articulo = a.cod);
select articulo from cesta union select articulo from linped;
select email, nombre from usuario u where NOT EXISTS (select 1 from pedido p where p.usuario=u.email) UNION select email, nombre from usuario, pedido where email=usuario group by email, nombre having count(*)=1;
select distinct u1.apellidos from usuario u1, usuario u2 where u1.email <> u2.email and u1.apellidos= u2.apellidos;Solución alternativa:Compáralo con la solución usando group by-having. select apellidos from usuario group by apellidos having count(*) > 1;
select p1.nombre, p2.nombre, l1.pueblo from provincia p1, provincia p2, localidad l1, localidad l2 where p1.codp < p2.codp and p1.codp=l1.provincia and p2.codp=l2.provincia and l1.pueblo=l2.pueblo;Solución alternativa:Observad lo que tarda en realizar la consulta comparado con otras.SELECT p1.nombre, p2.nombre, l1.pueblo FROM localidad l1 JOIN localidad l2 ON l1.pueblo=l2.pueblo -- nombres iguales AND l1.provincia < l2.provincia -- códigos de provincia diferentes JOIN provincia p1 ON p1.codp=l1.provincia -- necesitamos el nombre JOIN provincia p2 ON p2.codp=l2.provincia -- y el otro nombre
Se han comparado las provincias con "menor estricto": p1.codp < p2.codp. Con esto se consigue filtrar las filas que, en realidad, son información duplicada: Cuenca-Palmas (Las)-Moya y Palmas (Las)-Cuenca-Moya.
SELECT cod, nombre FROM articulo WHERE cod IN ( SELECT cod FROM articulo EXCEPT SELECT articulo FROM linped UNION SELECT articulo FROM stock WHERE entrega = 'Descatalogado' );De la suboconsulta.
Observa que los operadores de conjunto se aplican en orden: primero "artículos no solicitados en ningún pedido" (EXCEPT) y, después, (UNION) "artículo descatalogados en STOCK".
Además, el nombre de la columna resultado es el de la primera consulta (ARTICULO.cod)
select email, nombre, apellidos from usuario u join pedido p ON u.email=p.usuario join linped l ON l.numpedido=p.numpedido join tv ON tv.cod=l.articulo EXCEPT select email, nombre, apellidos from usuario u join pedido p ON u.email=p.usuario join linped l ON l.numpedido=p.numpedido join camara c ON c.cod=l.articulo
select usuario from linped l join pedido p on l.numpedido=p.numpedido group by p.numpedido, usuario having sum(cantidad*importe)>10000 UNION select usuario from linped l join pedido p on l.numpedido=p.numpedido group by usuario having count(distinct articulo)>5;
select email, nombre, apellidos, ' GRAN CLIENTE' tipo from usuario join pedido p on p.usuario=email join linped l on l.numpedido=p.numpedido group by email, nombre, apellidos having sum(cantidad*importe)>10000 UNION ALL -- los conjuntos son disjuntos, no hace falta buscar filas duplicadas select email, nombre, apellidos, ' CLIENTE MEDIO' from usuario join pedido p on p.usuario=email join linped l on l.numpedido=p.numpedido group by email, nombre, apellidos having sum(cantidad*importe) between 6000 and 10000 UNION ALL select email, nombre, apellidos, ' COMPRA POCO' from usuario join pedido p on p.usuario=email join linped l on l.numpedido=p.numpedido group by email, nombre, apellidos having sum(cantidad*importe)<6000 UNION ALL select email, nombre, apellidos, ' ** NO HA COMPRADO NUNCA' from usuario where email not in (select usuario from pedido) -- PERO SÍ HAY PEDIDOS SIN LÍNEAS DE PEDIDOS -- Aquí los estamos eliminando de la respuesta order by 3; -- también order by tipoSolución alternativa:UNION ALL devuelve todas las filas sin eliminar duplicados. Si sabemos que no los va a haber, UNION ALL es menos pesado para el motor de base de datos, hace una cosa menos, buscar y eliminar duplicados.WITH tt AS ( SELECT email, nombre, apellidos, SUM(importe*cantidad) total FROM usuario LEFT join pedido p on p.usuario=email LEFT JOIN linped l ON l.numpedido=p.numpedido GROUP BY email, nombre, apellidos ) SELECT email, nombre, apellidos, ' GRAN CLIENTE' FROM tt WHERE total>10000 UNION ALL -- el group by no genera filas duplicadas, UNION ALL no necesita compararlas SELECT email, nombre, apellidos, ' CLIENTE MEDIO' FROM tt WHERE total BETWEEN 6000 AND 10000 UNION ALL SELECT email, nombre, apellidos, ' COMPRA POCO' FROM tt WHERE total<6000 UNION ALL SELECT email, nombre, apellidos, ' ** NO HA COMPRADO NUNCA' FROM tt WHERE total IS NULL -- puede haber pedidos sin líneas de pedido, asumimos que esos casos entran en esta categoría ORDER BY apellidos; ------------------- SELECT u.email, u.nombre, u.apellidos, CASE WHEN SUM(l.importe * l.cantidad) IS NULL THEN '** NO HA COMPRADO NUNCA' WHEN SUM(l.importe * l.cantidad) < 6000 THEN 'COMPRA POCO' WHEN SUM(l.importe * l.cantidad) <= 10000 THEN 'CLIENTE MEDIO' ELSE 'GRAN CLIENTE' END AS `Tipo de cliente` FROM usuario u LEFT JOIN pedido p ON p.usuario = u.email LEFT JOIN linped l ON l.numPedido = p.numPedido GROUP BY u.email, u.nombre, u.apellidos ORDER BY apellidos
Tal y cómo está diseñada la base de datos puede haber pedidos sin líneas de pedido. El enunciado es ambiguo en este dato, hay que decidir en qué categoría ("compra poco", "no ha comprado")
select exists (select * from marca)
SELECT email, nombre FROM usuario EXCEPT SELECT u.email, u.nombre FROM usuario u JOIN pedido p ON u.email = p.usuario JOIN linped l ON p.numpedido = l.numpedido JOIN camara c ON l.articulo = c.cod;Solución alternativa:SELECT u.email,u.nombre from usuario u WHERE NOT exists ( SELECT 1 FROM pedido p JOIN linped l ON p.numpedido = l.numpedido JOIN camara c ON l.articulo = c.cod WHERE p.usuario=u.email );
select email, nombre from usuario u JOIN pedido p ON u.email=p.usuario EXCEPT select email, nombre from usuario u JOIN pedido p ON u.email=p.usuario JOIN linped l ON l.numPedido=p.numPedido JOIN camara c ON c.cod=l.articulo;Solución alternativa:Obsérvese el uso de usuario.email en la subconsulta del exists.select email, nombre from usuario u where email in (select usuario from pedido) and not exists (select 1 from linped l, pedido p, camara c where u.email=p.usuario and l.numpedido=p.numpedido and c.cod=l.articulo);
select cod,nombre from articulo a where not exists (select 1 from pedido p where not exists (select 1 from linped l where l.numpedido=p.numpedido and l.articulo=a.cod))Solución alternativa:"Artículo tal que NO existe pedido que NO lo incluya".select articulo from linped l group by articulo having count(distinct numpedido) = (select count(*) from pedido);
select 'sí' respuesta from dual where exists (select 1 from marca);"Dual" es una tabla ficticia del sistema que sirve precisamente para esto, para consultas que no utilizan ninguna tabla.
select 'no' respuesta from dual where not exists (select 1 from memoria);Solución alternativa:"Dual" es una tabla ficticia del sistema que sirve precisamente para esto, para consultas que no utilizan ninguna tabla. En realidad, se utiliza por compatibilidad con otros motores de BD, en MariaDB/MySQL no es imprescindible (véase la solución alternativa).select 'sí' respuesta where exists (select 1 from marca);
SELECT * FROM pedido WHERE numpedido IN ( SELECT numpedido FROM linped JOIN camara ON articulo = cod INTERSECT SELECT numpedido FROM linped JOIN tv ON articulo = cod );Solución alternativa:select * from pedido p where EXISTS (select 1 from linped l join tv ON l.articulo=tv.cod where l.numpedido=p.numpedido) and EXISTS (select 1 from linped l join camara c ON l.articulo=c.cod where l.numpedido=p.numpedido);
SELECT * FROM pedido WHERE numpedido IN ( SELECT numpedido FROM linped JOIN camara ON articulo = cod UNION SELECT numpedido FROM linped JOIN tv ON articulo = cod );Solución alternativa:select * from pedido p where exists (select 1 from linped l where l.numpedido=p.numpedido and articulo in (select cod from camara)) OR exists (select 1 from linped l where l.numpedido=p.numpedido and articulo in (select cod from tv));
select * from articulo natural join memoria;
select cod,nombre,pvp,marca,tipo from articulo natural join memoria;
select cod,nombre,pvp,marca,tipo from articulo natural join memoria where tipo='Compact Flash';Solución alternativa:select a.cod,a.nombre,a.pvp,a.marca,m.tipo from articulo a, memoria m where a.cod=m.cod and tipo='Compact Flash';
select * from pedido natural join linped order by fecha;Solución alternativa:select * from pedido natural join linped order by fecha;
select * from cesta natural join pack;Solución alternativa:En CESTA se utiliza el nombre de columna "articulo" mientras que en pack se utiliza "cod": no hay columnas comunes.select * from cesta, pack;
select * from usuario natural join direnvio;Entre USUARIO y DIRENVIO hay varias columnas comunes: "email", "calle", "calle2", "codpos", "pueblo" y "provincia".
Más útil sería concatenar únicamente por "email"; si nos empeñamos en utilizar la concatenación natural la única solución es renombrar las columnas:
select *
from usuario
natural join
(select email,calle dcalle,calle2 dcalle2,codpos dcodpos,pueblo dpueblo,provincia dprovincia from direnvio) as d
SELECT articulo FROM stock INTERSECT SELECT articulo FROM cesta;Solución alternativa:SELECT distinct articulo FROM stock s CROSS JOIN cesta c ON s.articulo=c.articulo;
SELECT articulo FROM cesta EXCEPT SELECT articulo FROM stock;
select cod from articulo EXCEPT select cod from pack EXCEPT select articulo from linped
select cod, nombre from articulo where cod IN ( select cod from articulo EXCEPT select cod from pack EXCEPT select articulo from linped )En este caso es seguro utilizar IN porque trabaja con columnas que sabemos que no almacenan nulos.
SELECT usuario, articulo FROM cesta INTERSECT SELECT p.usuario, l.articulo FROM pedido p JOIN linped l ON p.numPedido = l.numPedido;
SELECT usuario FROM pedido EXCEPT SELECT email FROM direnvio;
select fecha from pedido EXCEPT select nacido from usuario order by fecha;Este es un caso del uso "peligroso" de NOT IN. Esta consulta
select fecha from pedido
WHERE fecha NOT IN (select nacido from usuario)
order by fecha;
Obtiene cero filas porque en la columna usuario.nacido hay valores nulos.
SELECT articulo,'vendido' etq, importe FROM linped UNION SELECT articulo, 'de cesta', pvp FROM cesta c JOIN articulo a ON c.articulo=a.cod
-- Más vendidos SELECT a.cod, a.nombre,'MAS VENDIDO' tipo_destacado FROM articulo a JOIN linped l ON l.articulo = a.cod GROUP BY a.cod, a.nombre HAVING SUM(l.cantidad) >= 30 UNION ALL -- Muy añadidos a cesta SELECT a.cod, a.nombre, 'MUY EN CESTA' FROM articulo a JOIN cesta c ON c.articulo = a.cod GROUP BY a.cod, a.nombre HAVING COUNT(*) >= 3;Usamos UNION ALL porque estamos seguros de que no va a haber filas duplicadas.
SELECT a.cod, a.nombre,'CAMARA' AS tipo, c.resolucion AS detalle1, c.sensor AS detalle2 FROM articulo a JOIN camara c ON c.cod = a.cod WHERE pvp IS NOT NULL UNION ALL SELECT a.cod,a.nombre,'OBJETIVO',o.focal,o.apertura FROM articulo a JOIN objetivo o ON o.cod = a.cod WHERE pvp IS NOT NULLClaramente, los conjuntos de cámaras y objetivos van a ser disjuntos: aunque solo sea por el tipo, no habrá filas duplicadas entre ambos, por lo que podemos usar UNION ALL.
¡Ojo! Eso no quiere decir que no haya artículos con el mismo código en las tablas CAMARA y OBJETIVO, cosa que sí podría darse.