Search code examples
mysqlsqljoinsql-delete

Delete rows when data missing 2 joins away


I'm used to delete rows using joins in combination with "IS NULL", like this

Example:

Person A has number 1 of type 1. Person B has numer 2 of type 2, but also have number 1 of type 1.

Query:

SELECT people.name,
 GROUP_CONCAT(phone_numbers.phone_number) AS "All number",
 GROUP_CONCAT(IF(phone_numbers.type_id = 2, phone_numbers.phone_number, NULL)) AS "Type 2 Numbers" 
FROM people 
LEFT JOIN people_phones USING (people_id) 
LEFT JOIN phone_numbers USING (phone_number) 
GROUP BY people_id;

Result:

+------+------------+----------------+
| name | All number | Type 2 Numbers |
+------+------------+----------------+
| A    | 1          | NULL           |
| B    | 1,2        | 2              |
+------+------------+----------------+

I want to remove all persons that don't have a type 2 number, in this case Person A.

First i tried the usal way (that works if its one join away)

Tried:

DELETE people 
FROM people 
LEFT JOIN people_phones USING (people_id) 
LEFT JOIN phone_numbers 
  ON (phone_numbers.phone_number = people_phones.phone_number
      AND phone_numbers.type_id = 2) 
WHERE phone_numbers.phone_number IS NULL;

but that removes both Person 'A' and Person 'B' as 'B' has a number that isn't type 2.

How do i remove people that don't have a type 2 number?
(The real exemple have more joins, and more complex where parts, and 5 miljon rows in the tables.)

SQL for recreateing my exemple data:

CREATE TABLE people (people_id INT NOT NULL AUTO_INCREMENT KEY, name TINYTEXT NOT NULL);
CREATE TABLE phone_numbers (phone_number INT NOT NULL KEY, type_id INT NOT NULL);
CREATE TABLE people_phones (people_id INT NOT NULL, phone_number INT NOT NULL, PRIMARY KEY(people_id, phone_number))

INSERT INTO people VALUES (1,'A'),(2,'B');
INSERT INTO phone_numbers VALUES (1, 1), (2,2);
INSERT INTO people_phones VALUES (1,1), (2,1), (2,2);

Solution

  • This is one way:

    DELETE p
    FROM people AS p
    WHERE p.people_id NOT IN (SELECT pp.people_id
                              FROM people_phones as pp
                              INNER JOIN phone_numbers as pn
                                 ON pp.phone_number = pn.phone_number
                              WHERE pn.type_id = 2);
    

    And another way:

    DELETE p
    FROM people AS p
    WHERE NOT EXISTS(SELECT 1 
                     FROM people_phones as pp
                     INNER JOIN phone_numbers as pn
                        ON pp.phone_number = pn.phone_number
                     WHERE pn.type_id = 2
                     AND pp.people_id = p.people_id);
    

    Here is a sqlfiddle with a demo of the second version (which is also the one that I recommend).