sql select count with group, add null value if no result found - mysql

I have three table which i want to count each row with group from another table column.
The problem is count with group will return nothing when no record found.
So i want to add null value for each group that no record found.
Here is the query:
select monster.monster_name,count(*) as count
from monster right join monster_ability
on monster.monster_id= monster_ability.monster_id where
isnull(monster_ability.used)
group by monster.monster_id
here is the fiddle: fiddle
I want the result should look like this:
| monster_name | count |
|--------------|-------|
| kora | 1 |
| lowdowu | 3 |
| ngjengeh| null|
| lortyu | 1 |
| foh du fy| null|

Use case when to get null when count is 0:
select
m.monster_name,
case when count(a.ability_id) = 0 then null else count(a.ability_id) end as `count`
from monster m
left join monster_ability ma on m.monster_id = ma.monster_id and ma.used is null
left join ability a on ma.ability_id = a.ability_id
group by m.monster_id
SQLFiddle demo here.

Related

Get the last record with INNER JOIN

I'm trying to get the last record, but I ger the first record.
What am I doing wrong?
My Table permission
|id|pid|uid|
| 1| 2 | 2 |
| 2| 5 | 2 |
My Table fruits
|id|pid|number1|number2|
|1 | 1 | 50 | 100 |
|2 | 1 | 10 | 100 |
|3 | 1 | 100 | 100 | <== Try get last record
I want get the last record, but I can't.
I create the query, but not work:
SELECT DISTINCT(fruits.pid), permission.pid, fruits.number1, fruits.number2
FROM permission
LEFT JOIN fruits ON permission.pid = fruits.pid
WHERE permission.uid = '2'
GROUP BY fruits.pid
ORDER BY fruits.id DESC
I need the result:
|pid|pid|number1|number2|
|3 | 1 | 100 | 100 |
Your join doesn't join any rows. The value of fruits.pid is always 1. The values of permissions.pid are (2, 5). Thus, your join of fruits.pid = permission.pid doesn't find any rows that match, so you're not getting the results you expect. If you drop the DISTINCT in the query and remove the GROUP BY (which causes it to fail in MySQL 8) your query produces:
pid pid number1 number2
null 2 null null
null 5 null null
The row you want isn't in the result set, so of course you don't get it.
The other problem you have is that the number 3 is not in the column fruits.pid. It's an id value, so I suspect you're joining on the wrong field. And you've got permission.pid as the second field returned by your query, but that column only contains 2 and 5, as noted earlier, but you want a value of 1 there; thus, it appears you want to return fruits.pid as the second column of the result set. So something like:
SELECT fruits.id, fruits.pid, fruits.number1, fruits.number2
FROM fruits
LEFT JOIN permission
ON permission.id = fruits.pid
WHERE permission.uid = '2'
ORDER BY fruits.id DESC
db<>fiddle here

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.

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.

Add a column to a table according to some query results

I have these two tables, say faq_categories and faq_category_relations. First I do a query in tabel faq_category_relations, like
SELECT category_id
FROM faq_category_relations
WHERE faq_id = 2;
And the result_a is
--------------
|category_id |
--------------
| 2 |
--------------
| 3 |
--------------
Then I want to query table faq_categories and add one column checked, the value of which is set according to result_a(that is, if faq_categories.id is one of the values in result_a, set the checked to be true, else false), to the query results.
Till now I only come up with:
SELECT *, IF((faq_categories.id is one of result_a), 'true', 'false') AS checked
FROM faq_categories
And have no idea how to continue..
I know I can do that trick in PHP after retrieving the query data, but there must be some simple way to directly return the query results I needed.
EIDT:
The result should come out like this:
---------------------------
|category_id | checked |
---------------------------
| 1 | false |
---------------------------
| 2 | true |
---------------------------
| 3 | true |
---------------------------
| 4 | false |
---------------------------
| 5 | false |
---------------------------
Assuming there are no duplicates in faq_category_relations, then you can do this with a left outr join and a case expression:
SELECT c.*,
(case when cr.category_id is not null then 'true' else 'false' end) as checked
FROM faq_categories c left outer join
faq_category_relations cr
on c.category_id = cr.category_id and
cr.faq_id = 2;
I am also assuming that you just need the value in a query and not as a new column in the table.
The simplest way is:
select c.*, cr.category_id is not null checked
from faq_categories c
left join faq_category_relations cr
on c.category_id = cr.category_id
where cr.faq_id = 2
This works because with left joins, the values in the joined columns will be null if there's no join, so testing for not null will tell you if the category was listed in the other table.
If you want you can move the condition on faq_id into the join condition, which can be done by changing where to and in the above query.

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!