質問

Possible Duplicate:
Mysql: Perform of NOT EXISTS. Is it possible to improve permofance?

Is there a better/optimal way to do it. Should I use exists instead of join? Or two separate queries? And what about temporary tables, as I was reading about those but uncertain.

Getting members email from a group. Checking that they have not received a item yet.

SELECT m.email,g.id 
FROM group g 
  LEFT JOIN  members m 
    ON  g.mid = m.id 
    AND g.gid='1' 
WHERE NOT EXISTS 
      ( SELECT id 
        FROM items AS i 
        WHERE i.mid=m.id 
        AND i.item_id='5'
      ) 
役に立ちましたか?

解決

Here's the same thing written as a JOIN:

SELECT m.email, g.id
From members m
JOIN group g ON g.mid = m.id AND g.gid = '1' 
LEFT JOIN items i ON i.mid = m.id AND i.item_id = '5'
WHERE i.id IS NULL

Use the following compound indexes:

group (mid, gid)
items (mid, item_id)

I reversed the LEFT JOIN on members and group because it seems like you're returning members, not groups, and I changed the LEFT JOIN into an INNER JOIN since you only want members from that group.

I think this one might read better:

SELECT m.email, g.id
From members m
JOIN group g ON g.mid = m.id
LEFT JOIN items i ON i.mid = m.id AND i.item_id = 5
WHERE g.gid = 1
  AND i.id IS NULL

You might be wondering if we can move the i.item_id = 5 part to the WHERE clause also. You can't because there are no rows where i.id IS NULL and i.item_id = 5. You must do the join first and then eliminate the NULL rows in the WHERE clause.

他のヒント

I don't believe a temporary table is necessary. We'd really only go that route if we can't get acceptable performance.

From your query, we gather your schema looks like this:

group (id INT PK, gid INT, mid INT)
items (id INT PK, item_id INT, mid INT)
members (id INT PK, email VARCHAR)

It looks like your group table is really a "membership" table, which resolves/implements a many-to-many relationship between a group and a person. (That is, a person can be a member of zero, one or more groups; a group can have zero, or or more persons as members.)

You are using a LEFT JOIN between group and members. This will return a row for group (returning group.id) when there are no matching members, with a NULL for members.email (which may be what you want). But if you only want to return email addresses, then this can be changed to an INNER JOIN.

The NOT EXISTS predicate can be replaced with an OUTER JOIN and a test for a NULL value returned from the JOINED table. If the group.gid and/or items.item_id columns are numeric datatype, then you can remove the quotes from around the integer literals in the predicates.

Here is an alternative which will return an equivalent resultset, and may perform better:

SELECT m.email
     , g.id 
  FROM members m
  JOIN group g ON g.mid = m.id AND g.gid = 1
  LEFT
  JOIN items i ON i.mid = m.id AND i.item_id = 5
 WHERE i.id IS NULL

ADDENDUM:

TEST CASE (provided in comment on selected answer) demonstrates difference in result set between queries with the predicate items.item_id = 5 in the ON clause and in the WHERE clause. (Moving this predicate to the WHERE clause messes with the anti-join.)

CREATE TABLE `group` (`id` INT PRIMARY KEY, `gid` INT, `mid` INT);
CREATE TABLE `items` (`id` INT PRIMARY KEY, `item_id` INT, `mid` INT);
CREATE TABLE `members` (`id` INT PRIMARY KEY, `email` VARCHAR(40));

INSERT INTO `group` VALUES (1,1,1), (2,1,2);
INSERT INTO `items` VALUES (1,5,1);
INSERT INTO `members` VALUES (1,'one@m.com'),(2,'two@m.com'); 
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top