问题
I have a table like the following:
user | item
-------------
X | Apple
X | Orange
X | Pear
Y | Orange
Y | Pear
Z | Apple
Z | Orange
My goal is to have 3 search options: ANY
, ALL
(At Least), EXACT
Where
ANY returns a list of users who have at least one item searched for, so searching for "Apple" - ANY would return X,Z, searching for "Apple, Orange" - ANY would return X,Y,Z
ALL returns a list of users who have all items searched for, so searching for "Apple" - ALL would return X,Z, searching for "Apple, Orange" - ALL would return X,Z
EXACT returns a list of users who have all items searched for and no other items, , so searching for "Apple" - EXACT would return no results, searching for "Apple, Orange" - EXACT would return Z
I have the first two worked out:
ANY
SELECT user FROM users_items WHERE item IN ($item_array);
ALL
SELECT user FROM users_items WHERE item IN ($item_array)
GROUP BY user HAVING COUNT(DISTINCT item) = $item_search_count;
But I can't figure out how to do the exact search. The best I can come up with is:
SELECT user FROM users_items WHERE item IN ($item_array) AND
user NOT IN (
SELECT user FROM users_items WHERE item NOT IN ($item_array)
)
GROUP BY user HAVING COUNT(DISTINCT item) = $item_search_count;
This didn't work at first because in the real data set, either user or item could be null but when I updated it to:
SELECT user FROM users_items WHERE item IN ($item_array) AND
user NOT IN (
SELECT user FROM users_items WHERE item NOT IN ($item_array)
AND user IS NOT NULL AND item IS NOT NULL
)
GROUP BY user HAVING COUNT(DISTINCT item) = $item_search_count;
This worked, but the query is really slow.
Is there a better way to query for "all items that match set, exclude items that don't match set"?
回答1:
Use the magic group_concat function:
SELECT user
FROM (
SELECT user, group_concat(item) as items
from user_items
group by 1) x
WHERE items = 'Apple,Orange';
Note: The list of items you're searching for (eg 'Apple,Orange'
) must be in alphabetic order
回答2:
The following works, and should solve your size problem. But it could be slow, just try it with your real data.
Select
User
FROM
users_items
WHERE
User NOT IN
(
SELECT
users.User
FROM
users_items AS users
JOIN
users_items AS users2 ON users.User = users2.User
WHERE
users2.Item NOT IN ('Apple','Orange')
GROUP BY
users.User
)
GROUP BY
User
来源:https://stackoverflow.com/questions/8468117/how-to-limit-query-results-to-exact-group-match