Reputation: 79
I want to select a userid from a single table based on multiple and condition.
UserID FieldID Value
-----------------------------------
1 51 Yes
1 6 Dog
2 6 Cat
1 68 TX
1 69 78701
2 68 LA
What I'm trying to get in simple words:
if user search for texas or 78701, Select userId where (68 = TX OR 69=78701) AND (51=yes) AND (6=Dog)
This should return user id 1.
This is what I tried, but returns null.
SELECT user_id FROM `metadata`
WHERE ( (`field_id` = '68' AND value LIKE '%TX%')
OR (`field_id` = '69' AND value LIKE '%78701%') )
AND `field_id` = '51' AND value = 'Yes'
AND `field_id` = '6' AND value = 'Dog'
Upvotes: 1
Views: 1262
Reputation: 433
SELECT user_id FROM metadata
WHERE
(field_id
= '68' AND value LIKE '%TX%')
OR (field_id
= '69' AND value LIKE '%78701%')
AND (field_id
= '51' AND value = 'Yes')
AND (field_id
= '6' AND value = 'Dog');
I have little bit changed your query and tried with the same,it gives output as, user_id is 1
Upvotes: 0
Reputation: 33945
This approach is typically slower than simply LEFT JOINing that table on each criterion, but it can make the problem simpler to comprehend...
SELECT userid
, MAX(CASE WHEN fieldid = 51 THEN value END) smoker
, MAX(CASE WHEN fieldid = 6 THEN value END) favourite_pet
, MAX(CASE WHEN fieldid = 68 THEN value END) state
, MAX(CASE WHEN fieldid = 69 THEN value END) zip
FROM eav
GROUP
BY userid;
You can use HAVING, or bundle this into a subquery to get the desired results.
Upvotes: 0
Reputation: 107267
Your table structure resembles attribute+value modelling, which essentially splits up the columns of a row into individual pairs, and has the side effect of very weak typing.
As you've noted, this can also make things tricky to query, since you have to reason over multiple rows in order to make sense of the original data model.
One approach could be to take an opinion of a 'primary' criterion, and then apply additional criteria by reasoning over the shredded data, joined back by user id:
SELECT DISTINCT m.user_id
FROM `metadata` m
WHERE ((`field_id` = '68' AND value LIKE '%TX%')
OR (`field_id` = '69' AND value LIKE '%78701%'))
AND EXISTS
(SELECT 1
FROM `metadata` m2
WHERE m2.user_id = m.user_id AND m2.field_id = '51' AND m2.value = 'Yes')
AND EXISTS
(SELECT 1
FROM `metadata` m3
WHERE m3.user_id = m.user_id AND m3.field_id = '6' AND m3.value = 'Dog');
However, IMO, it may be better to attempt to remodel the table like so (and ideally choose better descriptions for the attributes as columns):
UserID Field51 Field6 Field68 Field69
----------------------------------------
1 Yes Dog TX 78701
2 No Cat LA NULL
This will make things much easier to query.
Upvotes: 0
Reputation: 72175
You can use GROUP BY
with a HAVING
clause that makes use of multiple conditional aggregates:
SELECT UserID
FROM metadata
GROUP BY UserID
HAVING SUM(field_id = '68' AND value LIKE '%TX%' OR
field_id = '69' AND value LIKE '%78701%') >= 1
AND
SUM(field_id = '51' AND value = 'Yes') >= 1
AND
SUM(field_id = '6' AND value = 'Dog') >= 1
Explanation: In MysQL a boolean expression, like
field_id = '51' AND value = 'Yes'
returns 1 when true, 0 when false.
Also, each predicate of HAVING
clause is applied to the whole group of records, as defined by GROUP BY
.
Hence, predicate:
SUM(field_id = '51' AND value = 'Yes') >= 1
is like saying: return only those UserID
groups having at least one (>=1) record with
field_id = '51' AND value = 'Yes' -> true
Upvotes: 3