MySQL Select/Join Multiple Row in User Meta Table - mysql

I have a table called user_meta and it contains data like:
---------------------------
| user_id | field | value |
---------------------------
| 1 | 1 | Green |
| 1 | 2 | Square |
| 1 | 3 | Big |
| 2 | 1 | Red |
| 2 | 2 | Square |
| 2 | 3 | Small |
----------------------------
The field column is the number of a form field in the user's profile. The value column is the value the user submitted via the form.
How do I write a MySQL query that returns all users who have 'green big squares'?
Thanks!

This will return the result that you want. This uses a WHERE clause to return all records that have the values that you want, then you count the distinct values to make sure there are only 3:
select user_id
from user_meta
where value in ('Green', 'Square', 'Big')
group by user_id
having count(distinct value) = 3
See SQL Fiddle with Demo

Using a subquery would work if you're stuck with that schema. It's not going to be very fast though.
select userid
from user_meta
where user_id in (
select user_id from user_meta
where (field = 1 and value = 'Green')
)
and user_id in (
select user_id from user_meta
where (field = 2 and value = 'Square')
)
and user_id in (
select user_id from user_meta
where (field = 3 and value = 'Big')
)

SELECT user_id
FROM user_meta user_meta1
JOIN user_meta user_meta2 ON user_meta1.UserID = user_meta2.UserID
JOIN user_meta user_meta3 ON user_meta2.UserID = user_meta3.UserID
WHERE user_meta1.value = 'Green' AND user_meta2.value='Square' AND user_meta3.value='big'

Related

How do i add another COUNT statement with different condition to sql query

I have 2 tables.
1st table: duels
| duelId | user1Id | user2Id | gameId | winnerId |
2nd table: usergameprogress
| usergameprogressId | userId | gameId | gameStar |
Given an userId, I would like to get duel count, gameStar, win count for each gameId.
Example return:
| duelCount | duelWinCount | gameStar | gameId |
I have managed to get duelCount, gameStar and gameId given a userId but I couldn't add duelWinCount to my query result. How do I do that ?
My query:
SELECT
COUNT(d1.duelId) AS duelCount,
usergameprogress.gameId, usergameprogress.gameStar
FROM
duels d1
JOIN
usergameprogress ON (usergameprogress.gameId = d1.gameId)
WHERE
d1.user1Id = "gkfurcwsi033qzxg0u2bmj1ekebsklej"
OR d1.user2Id = "gkfurcwsi033qzxg0u2bmj1ekebsklej"
GROUP BY
usergameprogress.gameId
EDIT:
solved thanks to comment use sum instead of count
SELECT sum(case when d1.user1Id = 'gkfurcwsi033qzxg0u2bmj1ekebsklej' OR d1.user2Id="gkfurcwsi033qzxg0u2bmj1ekebsklej" then 1 else 0 end) AS totalDuelCount,sum(case when winnerId="gkfurcwsi033qzxg0u2bmj1ekebsklej" then 1 else 0 end) AS duelWinCount,usergameprogress.gameId,usergameprogress.gameStar FROM duels d1 JOIN usergameprogress ON (usergameprogress.gameId = d1.gameId) GROUP BY usergameprogress.gameId

extended update in mysql

I have a table named 'products' and another table named 'rates' that has one to many relation with 'products' table. For each product i have two rows in 'rates' table that i want update one boolean column named 'index' to 1 for each 'product' in 'rates' table.
i used this query :
UPDATE ( SELECT
products.id AS productId,
products.name ,
X.`index` AS `index`,
x.id AS rateId,
x.price, x.discount
FROM products JOIN ( SELECT rates.*
FROM rates
) AS x
WHERE products.id = x.product_id
GROUP BY products.id
) AS y
SET y.index = 1
but id got this error massage:
SQL Error (1288) the target table y of the update is not updatable
i'm new in mysql and i don't know where is my mistake.Thank you for helping
Products Table
| id | name
| 1 | chair
| 2 | bench
Rates Table
| id | product_id | index | value
| 1 | 1 | 0 | xx ==> index = 1
| 2 | 1 | 0 | yy
| 3 | 2 | 0 | zz ==> index = 1
| 4 | 2 | 0 | tt
i want update index column for each product in rates to 1
It looks like you want to update the "first" row in rates for each product_id. If so, you can self-join the table with an aggregate query that computes the minimum id per product_id:
update rates r
inner join (select product_id, min(id) id from rates group by product_id) r1
on r1.id = r.id
set r.index = 1

How to get all items that has added action but wasn't deleted

I have an Activity table in which user's activity on actions is stored.
There is two type of actions in column type that are required to check:
add_favorite
remove_favorite
So same object can be added or removed from favorites multiple times so table has records like:
| id | type | timestamp | object_id | user |
|----|-----------------|------------|-----------|-------|
| 1 | add_favorite | 1584150783 | 4 | user1 |
| 2 | add_favorite | 1584151341 | 5 | user1 |
| 3 | remove_favorite | 1584161259 | 4 | user1 |
| 4 | add_favorite | 1584168758 | 4 | user1 |
| 5 | remove_favorite | 1584171635 | 10 | user1 |
| 6 | add_favorite | 1584174201 | 11 | user1 |
| 7 | remove_favorite | 1584177194 | 5 | user1 |
First I was constructing two queries that was collecting all object_id's with add_favorite actions for the user in array. Then I was filtering this array with another one set of object_id's which had type of action removed_favorite
SELECT object_id FROM activity WHERE user='user1' AND type='add_favorite';
SELECT object_id FROM activity WHERE user='user1' AND type='remove_favorite';
But this is highly ineffective on large number of activities. Please advise me query with LEFT JOIN which would be more effective and will give the end result
You can simplify your query to:
SELECT object_id
FROM activity
WHERE user = 'user1' AND
type IN ('add_favorite', 'remove_favorite');
Then I would recommend aggregation:
SELECT object_id
FROM activity
WHERE user = 'user1' AND
type IN ('add_favorite', 'remove_favorite')
GROUP BY object_id
HAVING SUM(type = 'add_favorite') > SUM(type = 'remove_favorite');
This gets the objects that have net more adds than removes.
An alternative is to look at the last action and only choose those that are adds:
SELECT a.object_id
FROM activity a
WHERE a.user = 'user1' AND
a.timestamp = (SELECT MAX(a2.timestamp)
FROM activity a2
WHERE a2.user = a.user AND
a2.type IN ('add_favorite', 'remove_favorite')
) AND
a.type = 'add_favorite'
With an index on activity(user, type, timestamp), this probably has better performance.
You could use a subquery like this
SELECT object_id
FROM activity a1
WHERE a1.user = 'user1'
AND a1.type = 'add_favorite'
AND object_id NOT IN (
SELECT object_id
FROM activity
WHERE user = 'user1'
AND type = 'remove_favorite'
)
However, I guess you want a list of effectively active (added) favorites
SELECT a1.object_id
FROM activity a1
WHERE a1.user = 'user1'
AND a1.TIMESTAMP = (
SELECT MAX(a2.TIMESTAMP)
FROM activity a2
WHERE a2.user = a1.user
AND a1.object_id = a2.object_id
AND a2.type IN (
'add_favorite'
,'remove_favorite'
)
)
AND a1.type = 'add_favorite'
Here is a demo.

MySQL left join that shows NULL for missing rows

I have 2 tables
Table:
recip
recipid | recipname
1 | Recip1
2 | Recip2
And table:
recipuser
recipid | userid
1 | 1
2 | 1
1 | 2
So userid 2 has 1 recip
The result I'm trying to achieve is to show all "recip" rows with matching or null for given user id, EG:
SELECT r.recipid, r.recipname, ru.userid
FROM recip r
left JOIN recipuser ru ON r.recipid = ru.recipid
WHERE ru.userid = 2 OR ru.userid IS NULL
Results in:
recipid | recipname | userid
1 | Recip1 | 2
I want to get:
recipid | recipname | userid
1 | Recip1 | 2
2 | Recip2 | NULL
How do I show all rows from recip with the userid or NULL for every row given a user id??
Thanks for your help.
Move the WHERE logic to the ON clause:
SELECT r.recipid, r.recipname, ru.userid
FROM recip r
LEFT JOIN recipuser ru
ON r.recipid = ru.recipid AND ru.userid = 2;
The problem with your current query is that the WHERE clause is filtering off the non matching record which you want to appear.

How to query a table (which has multiple rows pertaining to a single entity) and return GROUPED result but only where all conditionals have been met?

Firstly, pardon the incredibly vague/long question, I'm really not sure how to summarise my query without the full explanation.
Ok, I have a single MySQL table with the format like so
some_table
user_id
some_key
some_value
If you imagine that, for each user, there are multiple rows, for example:
1 | skill | html
1 | skill | php
1 | foo | bar
2 | skill | html
3 | skill | php
4 | foo | bar
If I want to find all the users who have listed HTML as a skill I can simply do:
SELECT user_id
FROM some_table
WHERE some_key = 'skill' AND some_value='html'
GROUP BY user_id
Easy enough. This would give me user ID's 1 and 2.
If I want to find all users who have listed HTML or PHP as a skill then I can do:
SELECT user_id
FROM some_table
WHERE (some_key = 'skill' AND some_value='html') OR (some_key = 'skill' AND some_value='php')
GROUP BY user_id
This would give me use ID's 1, 2 and 3.
Now, what I'm struggling to work out is how I can query the same table but this time say "give me all the users who have listed both HTML and PHP as a skill", i.e: just user ID 1.
Any advice, guidance or links to docs massively appreciated.
Thanks.
Here's one way:
SELECT user_id
FROM some_table
WHERE user_id IN (SELECT user_id FROM some_table where (some_key = 'skill' AND some_value='html'))
AND user_id IN (SELECT user_id FROM some_table where (some_key = 'skill' AND some_value='php'))
you need to use a nested query (or a self join, which is different)
I set up the following table.
+-------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| type | char(10) | YES | | NULL | |
| value | char(10) | YES | | NULL | |
+-------+----------+------+-----+---------+-------+
inserted the following values
+------+-------+-------+
| id | type | value |
+------+-------+-------+
| 1 | skill | html |
| 1 | skill | php |
| 2 | skill | html |
| 3 | skill | php |
| 2 | skill | php |
+------+-------+-------+
ran this query
select id
from test
where type = 'skill'
and value = 'html'
and id in (
select id
from test
where type = 'skill'
and value = 'php');
and got
+------+
| id |
+------+
| 1 |
| 2 |
+------+
a self join would be as follows
select e1.id
from test e1, test e2
where e1.id = e2.id
and e2.type = 'skill'
and e2.value = 'html'
and e1.type = 'skill'
and e1.value = 'php'
;
and produce the same result.
so there you have two ways to try it in your code.
I don't know if this is valid for mysql, but should be (works for other db engines):
SELECT php.user_id
FROM some_table php, some_table html
WHERE php.user_id = html.user_id
AND php.some_key = 'skill'
AND html.some_key = 'skill'
AND php.some_value = 'php'
AND html.some_value = 'html';
And alternative, by using HAVING statement:
SELECT user_id, count(*)
FROM some_table
WHERE some_key = 'skill'
AND some_value in ('php','html')
GROUP BY user_id
HAVING count(*) = 2;
And a third option is to use inner selects. A slight alternative approach to David's approach:
SELECT user_id FROM some_table
WHERE
some_key = 'skill' AND
some_value = 'html' AND
user_id IN (
SELECT user_id FROM some_table
WHERE
some_key = 'skill' AND
some_value = 'php' AND
user_id IN (
SELECT user_id FROM some_table
WHERE
some_key = 'skill' AND
some_value = 'js' -- AND user_id IN ... for next level, etc.
)
);
... idea is that you can "pipe" the inner selects. With each new property you add new inner select to the most inner one.