left outer join on the same table with condition - mysql

I have a table in a mysql 5.7 DB called "mytable" that is made like so:
--------------------
| title | flag |
|--------------------|
| first | 0 |
| first | 1 |
| second | 0 |
--------------------
From this table I need to select only the rows with flag = 0, and from those remove all the ones that has the same title of one with flag = 1. Ending up with a result like this:
--------------------
| title | flag |
|--------------------|
| second | 0 |
--------------------
How can i write my query? Thanks.

Just use a sub-query and combine it with MySQL's IN() function. The following will work:
SELECT title,
flag
FROM mytable
WHERE flag = 0
AND title NOT IN( SELECT title FROM mytable WHERE flag = 1 );
SQL Fiddle

Doing a LEFT OUTER JOIN
SELECT a.*
FROM mytable a
LEFT OUTER JOIN mytable b
ON a.title = b.title
AND b.flag = 1
WHERE a.flag = 0
AND b.flag IS NULL
Do the LEFT OUTER JOIN, then check for NULL in one of the joined columns to ensure that there is no matching record found.

Related

Add information from another table to a mysql-query-result by an id with a JOIN - MYSQL

My tablestructure looks like this:
job
| jobuniqueid | jobtypeunique | jobdateadded | jobtitle | jobuserkey | jobdeleted
| 1 | 3 | 2019-04-04 | Dentist | 69 | 0
jobtype
| jobtypeunique | jobtypename | jobtypecolor |
| 3 | fulltime | #336699 |
So, I want to do a query which shows me the following information per row:
jobuniqueid, jobdateadded, jobtitle, jobtypename, jobtypecolor
I try to realize this with a leftjoin, like this:
SELECT jobunique, jobdateadded, jobtitle FROM job
WHERE (jobuserkey = 69 AND jobdeleted = 0)
LEFT JOIN jobtype ON job.jobtypeunique = jobtype.jobtypeunique
ORDER BY jobdateadded DESC
This query results in a bunch of errors. Can you explain to me where my syntax is false - and, where do I declare which colums I would like to show from the jobtype-table.
You can use the following solution:
SELECT j.jobuniqueid, j.jobdateadded, j.jobtitle, jt.jobtypename, jt.jobtypecolor
FROM job j LEFT JOIN jobtype jt ON j.jobtypeunique = jt.jobtypeunique
WHERE j.jobuserkey = 69 AND j.jobdeleted = 0
ORDER BY j.jobdateadded DESC
demo on dbfiddle.uk
You have to use the WHERE part after the FROM ... LEFT JOIN ... ON .... The FROM and LEFT JOIN part is defining a new (joined) table. After the FROM and LEFT JOIN part you can use WHERE to filter the rows of the joined tables. Also have a look at the documentation of SELECT.

Select all items and count in related table by criteria

I have tables Match and Reaction as following:
REACTION
+----------+----------+----------+----------+
| user_id | game_id | item_id | reaction |
+----------+----------+----------+----------+
| 1 | 1 | 1 | 1 |
| 1 | 1 | 2 | 1 |
| 2 | 1 | 1 | 1 |
| 2 | 1 | 2 | 0 |
+----------+----------+----------+----------+
MATCH:
+----------+----------+
| game_id | item_id |
+----------+----------+
| 1 | 1 |
| 1 | 2 |
+----------+----------+
Now I want (if possible without subqueries) to select ALL item_ids from MATCH table AND count of rows where field reaction in table Reaction is equal to 1 for user with id = 2. For example, for defined tables I want to get following results:
+----------+----------+
| item_id | count |
+----------+----------+
| 1 |  1 |
| 2 | 0 |
+----------+----------+
I've tried something like
SELECT match.item_id, COUNT(reaction.user_id) as c
FROM match
LEFT JOIN reaction ON reaction.item_id = match.item_id
WHERE reaction.reaction = 1 AND match.game_id = 2
GROUP BY match.item_id
HAVING c > 0
but it didn't work as expected. I cannot get count for particular user.
I think you are close. I think you just need to move conditions on the second table to the ON clause:
SELECT m.item_id, COUNT(r.user_id) as c
FROM match m LEFT JOIN
reaction r
ON r.item_id = m.item_id AND
r.reaction = 1 AND
r.user_id = 2
WHERE m.game_id = 2
GROUP BY m.item_id;
I'm not sure what the HAVING clause is for, because you seem to want counts of 0.
Note that this also introduces table aliases so the query is easier to write and to read.
SELECT match.item_id, COUNT(reaction.user_id) as c
FROM match JOIN reaction ON (reaction.item_id = match.item_id and reaction.reaction = 1 AND match.game_id = 2)
GROUP BY match.item_id
HAVING COUNT(reaction.user_id)
I think you need to filter 'before' join -> so use the 'on' clause.
Filters in where are applied after the join is made while filter applied on on clause are applied before the join is made
You have not game_id = 2 so this should return no value
and you should not use left joined table columns in where condition otherwise these wprk as inner join ... in these cases you shou move the related condition in ON clause
SELECT match.item_id, COUNT(reaction.user_id) as c
FROM match
LEFT JOIN reaction ON reaction.item_id = match.item_id
AND reaction.reaction = 1
WHERE match.game_id = 2
GROUP BY match.item_id
HAVING c > 0
but try also
SELECT match.item_id, COUNT(reaction.user_id) as c
FROM match
LEFT JOIN reaction ON reaction.item_id = match.item_id
AND reaction.reaction = 1
GROUP BY match.item_id

MySQL fetch rows if every columns equals the same thing

I have a table like this :
---------------------------------------
| Actions |
---------------------------------------
| action_id | user_id | action_active |
---------------------------------------
| 1 | 1 | 0 |
---------------------------------------
| 2 | 2 | 1 |
---------------------------------------
| 3 | 1 | 0 |
---------------------------------------
| 4 | 2 | 0 |
---------------------------------------
I want to retrieve all rows where a user has all of his rows as action_active = 0. If he has just one action_active as 1, don't retrieve it.
In this example, it should only retrieve the row 1 and 3, since the user 1 has all of his rows at action_active = 0.
I thought about something like this, but I'm not sure about how right it is :
SELECT *
FROM Actions AS a
WHERE action_active = ALL (SELECT action_active FROM actions as s WHERE action_active = 0 where a.idx_user = s.idx_user)
I'm not sure my query is right.
Thanks !
Calculate sum in a sub-query to find users with all zero values and join that with main select
SELECT a.*
FROM actions a
JOIN (SELECT user_id, SUM(action_active) AS sum
FROM actions
GROUP BY user_id) AS sum_a ON sum_a.user_id = a.user_id
WHERE sum = 0
Use NOT EXISTS:
SELECT a.*
FROM actions a
WHERE NOT EXISTS (SELECT 1
FROM actions a2
WHERE a2.user_id = a.user_id AND
a2.action_active <> 0
);
This should have better performance than a solution using group by -- and this makes direct use use of an index on actions(user_id, action_active).
You can also phrase this using a LEFT JOIN:
SELECT a.*
FROM actions a LEFT JOIN
actions a2
ON a2.user_id = a.user_id AND a2.action_active <> 0
WHERE a2.user_id IS NULL;

Weird SQL Issue with Multiple Joins

I am currently in the process of building an API for an application and am having some trouble with a particular query.
This is my original query:
SELECT `users`.`id`,
unclaimed_users.id AS unclaimed_id,
`users`.`firstname`,
`users`.`lastname`,
`unclaimed_users`.`mobile_number`,
`group_members`.`status`,
`user_images`.`profile_image_main`
FROM `group_members`
LEFT JOIN `users`
ON `users`.`id` = `group_members`.`user_id`
AND `group_members`.`unclaimed_user` = 0
LEFT JOIN `user_images`
ON `user_images`.`id` = `group_members`.`user_id`
AND `group_members`.`unclaimed_user` = 0
LEFT JOIN `unclaimed_users`
ON `unclaimed_users`.`id` = `group_members`.`user_id`
AND `group_members`.`unclaimed_user` = 1
WHERE `group_members`.`group_id` = 1
ORDER BY `group_members`.`created_at` ASC
When I execute the above query, I am receiving the two records I expect, however, any field extracted from the unclaimed_user table is returning an empty string (not null), instead of returning the value.
See here:
+------+--------------+-----------+----------+---------------+---------+--------------------+
| id | unclaimed_id | firstname | lastname | mobile_number | status | profile_image_main |
+------+--------------+-----------+----------+---------------+---------+--------------------+
| 1 | NULL | Ben | Carey | NULL | active | NULL |
| NULL | 0 | NULL | NULL | | pending | NULL |
+------+--------------+-----------+----------+---------------+---------+--------------------+
However, when I replace the SELECT with SELECT *, it returns all the fields expected, with the expected values. For instance mobile_number returns the mobile number associated with the relevant unclaimed user.
What am I doing wrong? I cannot work out is happening and why the values are not being returned.
I have tried removing all the fields and only including one field from the unclaimed users table e.g.
SELECT unclaimed_users.id FROM etc...
The above returned 0, but it should return 1...
It is important to note that both id and mobile_number are present on the users table and the unclaimed_users table.
Update
I am currently in the process of putting a fiddle together but it is proving harder than I thought as I am yet to successfully recreate the issue...
In the meantime, I have simplified the query which is still not returning what it is meant to:
SELECT `unclaimed_users`.`mobile_number`
FROM `group_members`
LEFT JOIN `unclaimed_users`
ON `unclaimed_users`.`id` = `group_members`.`user_id`
AND `group_members`.`unclaimed_user` = 1
WHERE `group_members`.`group_id` = 1
The above returns the following
+---------------+
| mobile_number |
+---------------+
| NULL |
| |
+---------------+
When it should return:
+---------------+
| mobile_number |
+---------------+
| NULL |
| +447777779999 |
+---------------+
If I replace the SELECT unclaimed_users.mobile_number with SELECT *, the correct data is returned so the join is working. I cannot see why I am unable to reference the field mobile_number
Update 2
I have noticed that it works when I execute the following:
SELECT `unclaimed_users`.`mobile_number`
FROM `group_members`
LEFT JOIN `unclaimed_users`
ON `unclaimed_users`.`id` = `group_members`.`user_id`
AND `group_members`.`unclaimed_user` = 1
WHERE `group_members`.`unclaimed_user` = 1 -- This is the bit I changed
Because the above is ignoring the first record in the table (the one where unclaimed_user=0, it seems to work...
As you didn't provide too much to test...
SELECT `users`.`id`,
unclaimed_users.id AS unclaimed_id,
`users`.`firstname`,
`users`.`lastname`,
`unclaimed_users`.`mobile_number`,
`group_members`.`status`,
`group_members`.`user_id`,
`group_members`.`unclaimed_user`,
`user_images`.`profile_image_main`
FROM `group_members`
LEFT JOIN `users`
ON `users`.`id` = `group_members`.`user_id`
AND `group_members`.`unclaimed_user` = 0
LEFT JOIN `user_images`
ON `user_images`.`id` = `group_members`.`user_id`
AND `group_members`.`unclaimed_user` = 0
LEFT JOIN `unclaimed_users`
ON `unclaimed_users`.`id` = `group_members`.`user_id`
AND `group_members`.`unclaimed_user` = 1
WHERE `group_members`.`group_id` = 1
ORDER BY `group_members`.`created_at` ASC
I'm not sure if a good answer, I just can't add a comment.

How to select all from SQL query including subquery

I want to perform a mySQL query that returns all the results in a table but has an additional column that has values "selected" if they correspond to a separate query. For example
| name |
|------|
| a |
| b |
| c |
I want to be able to return the following when I do a query "select * from table where name = 'a';
| name | selected |
|------|----------|
| a | yes |
| b | |
| c | |
That is, I also want to know which ones where left out.
select *
, selected = case
when exists (
select *
from table2 t2
where t2.field = t1.field
and ...etc.
) then 1
else 0
end
from table t1
Where clause restricts your row set so you get only rows with name 'a'.
To get all of them either join the limited table back to itself or use IF without where:
SELECT *, IF( name='a', 'yes', '') AS selected
FROM table
The one that worked for me was:
select a.*
, name = case
when
a.name = 'a'
then 0
else 1
end
as selected
from points as a