MySQL left join that shows NULL for missing rows - mysql

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.

Related

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

SELECT not working when using != in WHERE clause (using GROUP BY and HAVING COUNT)

This question is based on: Select row from left join table where multiple conditions are true
I am now trying to select rows from Table 1, which do not have a connection in Table 2 to a certain property ID.
These are the tables:
Table 1
| ID | Name |
| 1 | test |
| 2 | hello |
Table 2
| ID | PropertyID |
| 1 | 3 |
| 1 | 6 |
| 1 | 7 |
| 2 | 6 |
| 2 | 1 |
I am using the following query (which is working with '='):
SELECT tab1ID
FROM table2
WHERE propertyID != 3 OR propertyID = 6
GROUP BY tab1ID
HAVING COUNT(*) = 2;
This query should return ID=2, but it returns zero rows. What I am doing wrong?
Any help is greatly appreciated!
Edit: I had given a MWE but this is my actual query:
SELECT transactionline.total FROM transactionline
LEFT JOIN product_variant ON product_variant.SKU = transactionline.SKU
LEFT JOIN product ON product_variant.productID = product.productID
LEFT JOIN connect_option_product ON connect_option_product.productID = product.productID
LEFT JOIN productattribute_option ON productattribute_option.optionID = connect_option_product.optionID
WHERE productattribute_option.optionID = 4 OR productattribute_option.optionID = 9
GROUP BY transactionline.lineID
HAVING COUNT(*) = 1
AND SUM(productattribute_option.optionID = 4) = 0
AND SUM(productattribute_option.optionID = 9) > 0
A product can have multiple connections to the optionID's. The goal of this query is to select the total amount where some filters are true or false.
Your grouping is correct. But you need to count how many times the value you do not want is in your group. That count must be zero.
SELECT tab1ID
FROM table2
GROUP BY tab1ID
HAVING sum(propertyID = 6) > 0
AND sum(propertyID = 3) = 0

query which creates missing rows based on anther table

I have many forms that users fill out. Each form contains a list of questions. In this first table is the form id and the id's of the questions.
form_id | question_id
1 | 1
1 | 2
1 | 3
2 | 4
2 | 5
This table has two forms one which has 3 questions and the other 2. I have a second table which has the answers that the users have given for the questions.
user_id | form_id | question_id | answer
476 | 1 | 1 | "answer1"
476 | 1 | 3 | "answer2"
693 | 1 | 1 | "answer3"
693 | 1 | 2 | "answer4"
235 | 2 | 5 | "answer5"
In this example, 2 users have filled out form 1 and 1 user has filled in form 2. But none have filled in all the questions. Is it possible to write a query which combines the two tables and will give me the answers that the user have given including the questions that they didn't answer? I'd like the results to look like this.
user_id | form_id | question_id | answer
476 | 1 | 1 | "answer1"
476 | 1 | 2 | NULL
476 | 1 | 3 | "answer2"
693 | 1 | 1 | "answer3"
693 | 1 | 2 | "answer4"
693 | 1 | 3 | NULL
235 | 2 | 4 | NULL
235 | 2 | 5 | "answer5"
The problem that I have when I use a left join like this
select * from template t
left join answers a on a.template_id = t.template_id
AND a.question_id = t.question_id
AND t.template_id = t.template_id;
is that the row that results is missing user_id.
Yes, the specified result can be returned by a query.
One way to achieve this is a join to an inline view, and an "outer join" operation to the second table.
The "trick" is getting a distinct list of user_id and form_id from the second table, using a query, for example:
SELECT user_id, form_id
FROM second_table
GROUP BY user_id, form_id
And then using that query as an inline view (wrapping it in parens, assigning a table alias, and referencing it like it was a table in an outer query.
All that's required after that is an "outer join" to the second table.
For example:
SELECT r.user_id
, q.form_id
, q.question_id
, a.answer
FROM first_table q
JOIN ( SELECT p.user_id, p.form_id
FROM second_table p
GROUP BY p.user_id, p.form_id
) r
ON r.form_id = q.form_id
LEFT
JOIN second_table a
ON a.user_id = r.user_id
AND a.form_id = r.form_id
AND a.question_id = q.question_id
ORDER
BY r.user_id
, q.form_id
, q.question_id
Note that the keyword "LEFT" specifies an outer join operation, returning all rows from the left side, along with matching rows from the right side. A typical "inner" join would exclude rows that didn't find a matching row from the table on the right side.
use
left join
something like:
select * from table1 left join table2 on table1.form_id= table2.form_id

MySQL Select/Join Multiple Row in User Meta Table

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'

Joining 3 tables with n:m relationship, want to see nonmatching rows

For this problem, consider the following 3 tables:
Event
id (pk)
title
Event_Category
event_id (pk, fk)
category_id (pk, fk)
Category
id (pk)
description
Pretty trivial I guess... :) Each event can fall into zero or more categories, in total there are 4 categories.
In my application, I want to view and edit the categories for a specific event. Graphically, the event will be shown together with ALL categories and a checkbox indicating whether the event falls into the category. Changing and saving the choice will result in modifocation of the intermediate table Event_Category.
But first: how to select this for a specific event? The query I need will in fact always return 4 rows, the number of categories present.
Following returns only the entries for the categories the event with id=11 falls into. Experimenting with outer joins did not give more rows in the result.
SELECT e.id, c.omschrijving
FROM Event e
INNER JOIN Event_Categorie ec ON e.id = ec.event_id
INNER JOIN Categorie c ON c.id = ec.categorie_id
WHERE e.id = 11
Or should I start with the Category table in the query? Hope for some hints :)
TIA, Klaas
UPDATE:
Yes I did but still have not found the answer. But I have simplified the issue by omitting the Event table from the query because this table is only used to view the Event descriptions.
SELECT * from Categorie c LEFT JOIN Event_Categorie ec ON c.id = ec.categorie_id WHERE ec.event_id = 11;
The simplified 2-table query only uses the lookup table and the link table but still returns only 2 rows instead of the total of 4 rows in the Categorie table.
My guess would be that the WHERE clause is applied after the joining, so the rows not joined to the link table are excluded. In my application I solved the issues by using a subquery but I still would like to know what is the best solution.
What you want is the list of all categories, plus information about whether that category is in the list of categories of your event.
So, you can do:
SELECT
*
FROM
Category
LEFT JOIN Event_Category ON category_id = id
WHERE
event_id = 11
and event_id column will be NULL on the categories that are not part of your event.
You can also create a column (named has_category below) that you will use to see if the event has this category instead of comparing with NULL:
SELECT
*,
event_id IS NOT NULL AS has_category
FROM
Category
LEFT JOIN Event_Category ON category_id = id
WHERE
event_id = 11
EDIT: This seems exactly what you say you are doing on your edit. I tested it and it seems correct. Are you sure you are running this query, and that rows with NULL are not somehow ignored?
The query
SELECT * FROM Categorie;
returns 4 rows:
+----+--------------+-------------------------------------+--------------------------------------+
| id | omschrijving | afbeelding | afbeelding_klein |
+----+--------------+-------------------------------------+--------------------------------------+
| 1 | Creatief | images/categorieen/creatief420k.jpg | images/categorieen/creatief190k.jpg |
| 2 | Sportief | images/categorieen/sportief420k.jpg | images/categorieen/sportief190kr.jpg |
| 4 | Culinair | images/categorieen/culinair420k.jpg | images/categorieen/culinair190k.jpg |
| 5 | Spirit | images/categorieen/spirit420k.jpg | images/categorieen/spirit190k.jpg |
+----+--------------+-------------------------------------+--------------------------------------+
4 rows in set (0.00 sec)
BUT:
The query
SELECT *
FROM Categorie
LEFT JOIN Event_Categorie ON categorie_id = id
WHERE event_id = 11;
returns 2 rows:
+----+--------------+-------------------------------------+-------------------------------------+----------+--------------+
| id | omschrijving | afbeelding | afbeelding_klein | event_id | categorie_id |
+----+--------------+-------------------------------------+-------------------------------------+----------+--------------+
| 1 | Creatief | images/categorieen/creatief420k.jpg | images/categorieen/creatief190k.jpg | 11 | 1 |
| 4 | Culinair | images/categorieen/culinair420k.jpg | images/categorieen/culinair190k.jpg | 11 | 4 |
+----+--------------+-------------------------------------+-------------------------------------+----------+--------------+
2 rows in set (0.00 sec)
So I still need the subquery... and the LEFT JOIN is not effective in showing all rows of the CAtegorie table, regardless whether there is a match with the link table.
This query, however, does what I want it to do:
SELECT *
FROM Categorie c
LEFT JOIN (SELECT * FROM Event_Categorie ec WHERE ec.event_id = 11 ) AS subselect ON subselect.categorie_id = c.id;
Result:
+----+--------------+-------------------------------------+--------------------------------------+----------+--------------+
| id | omschrijving | afbeelding | afbeelding_klein | event_id | categorie_id |
+----+--------------+-------------------------------------+--------------------------------------+----------+--------------+
| 1 | Creatief | images/categorieen/creatief420k.jpg | images/categorieen/creatief190k.jpg | 11 | 1 |
| 2 | Sportief | images/categorieen/sportief420k.jpg | images/categorieen/sportief190kr.jpg | NULL | NULL |
| 4 | Culinair | images/categorieen/culinair420k.jpg | images/categorieen/culinair190k.jpg | 11 | 4 |
| 5 | Spirit | images/categorieen/spirit420k.jpg | images/categorieen/spirit190k.jpg | NULL | NULL |
+----+--------------+-------------------------------------+--------------------------------------+----------+--------------+
4 rows in set (0.00 sec)
The issue is that you have filtered the results by the eventid. As you can see in your results, two of the categories (Sportief and Spirit) do not have events. So the correct SQL statement (using SQL Server syntax; some translation may be required) is:
SELECT *
FROM Categorie
LEFT JOIN Event_Categorie ON categorie_id = id
WHERE (event_id IS NULL) OR (event_id = 11);
Finally I found the right query, no subselect is necessary. But the WHERE clause works after the joining and therefore is no part of the join anymore. THe solution is extending the ON clause with an extra condition. Now all 4 rows are returned with NULL for the non-matching Categories!
SELECT *
FROM Categorie
LEFT JOIN Event_Categorie ON categorie_id = id AND event_id = 11;
So the bottom line is that putting an extra condition in the ON clause has different effect than filtering out rows by the same condition in the WHERE clause!