I have some SQL to tell me the superheroes who don't ever wield a sword.
Creating the table
First, here is the (MySQL) script to create and populate the table.
CREATE TABLE IF NOT EXISTS `mydb`.`weaponry` (
`hero` VARCHAR(20) NULL,
`weapon` VARCHAR(20) NULL)
ENGINE = InnoDB
insert into weaponry (hero, weapon) values ('Wonder Woman', 'Lasso of truth');
insert into weaponry (hero, weapon) values ('Wonder Woman', 'Sword');
insert into weaponry (hero, weapon) values ('Wonder Woman', 'Shield');
insert into weaponry (hero, weapon) values ('Spider Man', 'Web');
insert into weaponry (hero, weapon) values ('Gamora', 'Sword');
insert into weaponry (hero, weapon) values ('Gamora', 'Daggar');
insert into weaponry (hero, weapon) values ('Batman', 'Batarang');
insert into weaponry (hero, weapon) values ('Batman', 'Batgrapple');
insert into weaponry (hero, weapon) values ('Batman', 'Tranquilizer gun');
Working code using a NOT IN
and a subselect
select distinct hero from weaponry
where hero not in
(select hero from weaponry where weapon = 'Sword')
This correctly returns Spider Man and Batman, who don't wield a sword.
Alternative 1: Using NOT EXISTS
I would like to implement the same query using NOT EXISTS
.
I looked at https://stackoverflow.com/a/5231731/509840.
I tried this:
select distinct hero from weaponry
where not exists
(select * from weaponry where weapon = 'Sword')
Unfortunately, this returns no rows.
Alternative 2: Using LEFT JOIN
I also took a look at this answer: https://stackoverflow.com/a/17413996/509840 .
I was curious to try replacing the NOT IN
with a LEFT JOIN
.
select * from weaponry a
left join weaponry b on a.hero=b.hero and a.weapon=b.weapon
where a.weapon <> 'Sword'
But this just returns all non-sword heroes (including Wonder Woman and Gamora), since all wield weapons other than swords.
Would you please help me rewrite the NOT IN query with either the NOT EXISTS
or the LEFT JOIN
?
EDIT: Left Join and the correlated subquery
@JeffUK suggested in his comments that one could understand the correlated subquery better by removing the WHERE
clause. Here's a small modification to the select
with the where
removed.
select distinct a.hero, weaponryInner.hero from weaponry a
left join
(Select * from weaponry b where weapon = 'Sword') as weaponryInner
on a.hero=weaponryInner.hero
The results are two columns of heroes, some of which are NULL
.
-------------------------------
Hero | Hero
-------------------------------
Wonder Woman | Wonder Woman
Gamora | Gamora
Spider Man | <null>
Batman | <null>
-------------------------------
So adding the final where weaponryInner.Weapon is null
will return just Spider Man and Batman.