Joining tables but needs 0 for empty rows - mysql

I don't know how to explain the scenario using words. So am writing the examples:
I have a table named tblType:
type_id | type_name
---------------------
1 | abb
2 | cda
3 | edg
4 | hij
5 | klm
And I have another table named tblRequest:
req_id | type_id | user_id | duration
-------------------------------------------
1 | 4 | 1002 | 20
2 | 1 | 1002 | 60
3 | 5 | 1008 | 60
....
So what am trying to do is, fetch the SUM() of duration for each type, for a particular user.
This is what I tried:
SELECT
SUM(r.`duration`) AS `duration`,
t.`type_id`,
t.`type_name`
FROM `tblRequest` AS r
LEFT JOIN `tblType` AS t ON r.`type_id` = t.`type_id`
WHERE r.`user_id` = '1002'
GROUP BY r.`type_id`
It might return something like this:
type_id | type_name | duration
-------------------------------
1 | abb | 60
4 | hij | 20
It works. But the issue is, I want to get 0 as value for other types that doesn't have a row in tblRequest. I mean I want the output to be like this:
type_id | type_name | duration
-------------------------------
1 | abb | 60
2 | cda | 0
3 | edg | 0
4 | hij | 20
5 | klm | 0
I mean it should get the rows of all types, but 0 as value for those type that doesn't have a row in tblRequest

You could perform the aggregation on tblRequest and only then join it, using a left join to handle missing rows and coalesce to convert the nulls to 0s:
SELECT t.type_id, type_name, COALESCE(sum_duration, 0) AS duration
FROM tblType t
LEFT JOIN (SELECT type_id, SUM(duration) AS sum_duration
FROM tblRequest
WHERE user_id = '1002'
GROUP BY type_id) r ON t.type_id = r.type_id

Select a.type_id, isnull(sum(b.duration), 0)
From tblType a Left Outer Join tblRequest b
ON a.type_id = b.type_id and b.user_id = 1002
Group by a.type_id

Related

How to add default value on sum to have equal number of lines sum

I'm in front of a problem that regarding MySQL.
I have three tables in my Database :
Table gp
____________
id | name |
____________
1 | Le Mans|
2 | Toulon |
3 | Rennes |
Table player
____________
id | name |
____________
1 | Thibaut|
2 | Fred |
3 | Samir |
Table Records
_____________________________
id | gp_id | player_id | time
_____________________________
1 | 1 | 1 | 22
2 | 2 | 1 | 33
3 | 3 | 1 | 44
4 | 3 | 2 | 40
5 | 2 | 2 | 35
6 | 1 | 2 | 20
7 | 1 | 3 | 25
8 | 3 | 3 | 38
I want to get a sum of time for players that have at least one record on some specifics GP and to set a default time for the gp where they don't have a time
I have no idea how I can get that. Actually my SQL query get the values but I don't know how to set a default time for GP not finished by some players.
SELECT p.name, sum(time) as total_time
from records r
join gp g on r.gp_id = g.id
join player p on r.player_id = p.id
where gp.id in ( 1, 2, 3)
having count(distinct g.id) > 0
group by r.player_id
For example for this query I get these values :
name | total_time
_________________
Thibaut | 99
Fred | 95
Samir | 63
But I want a default time to 99 if there is no time for a player in one GP, so in my case Samir should have 63+99 => 162.
But I have no idea how to do that, and I don't know if it's possible
Thanks in advance guys !
Use LEFT JOIN to get a null value if there's no match, and IFNULL() to supply a default value in place of NULL.
Use a CROSS JOIN with gp to specify all the games that should be considered.
SELECT p.name, sum(IFNULL(time, 99)) as total_time
from player p
cross join gp
left join records r on r.player_id = p.id AND r.gp_id = gp.id
WHERE gp.id IN (1, 2, 3)
group by p.id
having count(distinct r.gp_id) > 0
DEMO

MySQL count when value is not equal

I have few tables.
1.region
2.restaurant
3.restaurant_itmes
region
id | name
................
1 | NY
2 | Paris
3 | London
restaurant
id | name | region_id
.........................
1 | KFC | 1
2 | McDonals'| 1
3 | La res | 2
4 | Queen's | 3
restaurant_items
id | name | restaurant_id | pro_pic | featured_pic
...................................................
1 |Pizza |3 | null | defaut.jpg
2 |Pizza |4 | pizza.jpg | defaut.jpg
3 |Burger|1 | burger.jpg| burger.jpg
4 |Burger|2 | burger.jpg| burger.jpg
5 |Burger|3 | null | burger.jpg
6 |Burger|4 | null | default.jpg
7 |Donat |2 | null | default.jpg
8 |Fries |2 | null | default.jpg
I want to generate an query to populate this table
region |Number of restaurants |total items | items_with_pro_pic | items_with_featured_pic
............................................................................................
NY | 2 | 4 | 2 | 2
Paris | 1 | 2 | 0 | 1
London | 1 | 2 | 1 | 0
What I have done upto now is
SELECT region.name, count(restaurant_items.id) as total_items, count(restaurant_items.pro_pic)
INNER JOIN restaurant on restaurant_items.restaurant_id = restaurant.id
INNER JOIN region on restaurant.region_id = region.id
GROUP BY region.name;
Here I can get items_with_pro_pic by count(restaurant_items.pro_pic)
but I can't do that for items_with_featured_pic because featured_pic is not null able if there is no value
default value is default.jpg.
So I tried count(restaurant_items.featured_pic != 'defaut.jpg') but this doesn't work for me.
And how could I get number of restaurants since it is not a part of restaurant_items table?
How do I achieve these two using MySQL?
You can change the COUNT to a SUM and run it over an IF statement:
SUM(IF(restaurant_items.featured_pic != 'default.jpg',1,0))
Or alternatively you can specify it as a COUNT if you want, but the ELSE portion will need to be a NULL rather than a 0 since otherwise it will still count it:
COUNT(IF(restaurant_items.featured_pic != 'default.jpg',1,NULL))
To count number of restaurants, you can simply do a distinct count:
COUNT(DISTINCT restaurant.id)
A few small extra tips:
You may want to change the name of 'restaurant_items' to 'restaurant_item' since that suits the naming convention of the other tables
You should be aliasing your table names in the FROM and JOIN clauses, since it enhances code legibility
You can use a case expression to produce nulls instead of default.jpg in order to use in a count function:
SELECT region.name,
COUNT(restaurant_items.id) as total_items,
COUNT(restaurant_items.pro_pic),
COUNT(CASE WHEN restaurant_items.featured_pic != 'default.jpg' THEN 1 END)
INNER JOIN restaurant ON restaurant_items.restaurant_id = restaurant.id
INNER JOIN region ON restaurant.region_id = region.id
GROUP BY region.name;
use case when
SELECT region.name, count(restaurant_items.id) as total_items, count(restaurant_items.pro_pic),
count(case when restaurant_items.featured_pic != 'defaut.jpg' then 1 end) as
items_with_featured_pic,
count(case when pro_pic is null then 1 end) as
items_with_pro_pic_null
INNER JOIN restaurant on restaurant_items.restaurant_id = restaurant.id
INNER JOIN region on restaurant.region_id = region.id
GROUP BY region.name

selecting only newest row with specific value

Table:
person | borrow_date | is_borrowed | SN | date | id
1 | 2019-01-10...| 1 | 20 |2019-01-10...| 6
3 | 2019-01-09...| 3 | 10 |2019-01-09...| 5
1 | 2019-01-08...| 1 | 10 |2019-01-08...| 4
2 | 2019-01-08...| 1 | 10 |2019-01-08...| 3
1 | NULL | 2 | 20 |2019-01-07...| 2
1 | NULL | 2 | 10 |2019-01-07...| 1
My wanted output is to select newest rows where "is_borrowed" equals 1 and grouped by SN, so that when the query is executed with person=2 or person=3 then it would retrieve empty set. Whereas for person=1 it would give back two rows.
Wanted output (where person=1):
person | borrow_date | is_borrowed | SN | date |id
1 | 2019-01-10...| 1 | 20 | 2019-01-10...|6
1 | 2019-01-08...| 1 | 10 | 2019-01-08...|4
Wanted output (where person=2):
EMPTY SET
Wanted output (where person=3):
EMPTY SET
This is my current query and it sadly doesn't work.
SELECT a.SN, a.is_borrowed,a.max(date) as date, a.person
FROM table a
INNER JOIN (SELECT SN, MAX(date) as date, osoba from table where person like
"2" group by SN) as b
ON a.SN=b.SN and a.date=b.date
WHERE a.person like "2" and a.is_borrowed=1
If I correctly understood you from the question and the comment you made under it, here's one way to do it without specifying the person:
select *
from TableName as p
inner join (select max(borrow_date) as borrow_date,
SN
FROM TableName
where is_borrowed = 1
group by SN) as p2
on p.borrow_date = p2.borrow_date and p.SN = p2.SN
This should give you the result you're looking for. Here's a demo.
Note that I had to change the borrowed_date values in the table since yours contain hours and minutes while I didn't add those.
You can always specify it for each person by adding a where clause after the join.
select p.person,
p.borrow_date,
p.is_borrowed,
p.SN,
p.date,
p.id
from TableName as p
inner join (select max(borrow_date) as borrow_date,
SN
FROM TableName
where is_borrowed = 1
group by SN) as p2
on p.borrow_date = p2.borrow_date and p.SN = p2.SN
where p.person = '1'
Output:
person | borrow_date | is_borrowed | SN | date | id
1 | 2019-01-10 | 1 | 20 | 2019-01-10 | 6
1 | 2019-01-08 | 1 | 10 | 2019-01-08 | 4
While where p.person = '2' and where p.person = '3' will return empty sets.

MySQL - Return Latest Date and Total Sum from two rows in a column for multiple entries

For every ID_Number, there is a bill_date and then two types of bills that happen. I want to return the latest date (max date) for each ID number and then add together the two types of bill amounts. So, based on the table below, it should return:
| 1 | 201604 | 10.00 | |
| 2 | 201701 | 28.00 | |
tbl_charges
+-----------+-----------+-----------+--------+
| ID_Number | Bill_Date | Bill_Type | Amount |
+-----------+-----------+-----------+--------+
| 1 | 201601 | A | 5.00 |
| 1 | 201601 | B | 7.00 |
| 1 | 201604 | A | 4.00 |
| 1 | 201604 | B | 6.00 |
| 2 | 201701 | A | 15.00 |
| 2 | 201701 | B | 13.00 |
+-----------+-----------+-----------+--------+
Then, if possible, I want to be able to do this in a join in another query, using ID_Number as the column for the join. Would that change the query here?
Note: I am initially only wanting to run the query for about 200 distinct ID_Numbers out of about 10 million. I will be adding an 'IN' clause for those IDs. When I do the join for the final product, I will need to know how to get those latest dates out of all the other join possibilities. (ie, how do I get ID_Number 1 to join with 201604 and not 201601?)
I would use NOT EXISTS and GROUP BY
select, t1.id_number, max(t1.bill_date), sum(t1.amount)
from tbl_charges t1
where not exists (
select 1
from tbl_charges t2
where t1.id_number = t2.id_number and
t1.bill_date < t2.bill_date
)
group by t1.id_number
the NOT EXISTS filter out the irrelevant rows and GROUP BY do the sum.
I would be inclined to filter in the where:
select id_number, sum(c.amount)
from tbl_charges c
where c.date = (select max(c2.date)
from tbl_charges c2
where c2.id_number = c.id_number and c2.bill_type = c.bill_type
)
group by id_number;
Or, another fun way is to use in with tuples:
select id_number, sum(c.amount)
from tbl_charges c
where (c.id_number, c.bill_type, c.date) in
(select c2.id_number, c2.bill_type, max(c2.date)
from tbl_charges c2
group by c2.id_number, c2.bill_type
)
group by id_number;

MySQL select unique rows in two columns with the highest value in one column

I have a basic table:
+-----+--------+------+------+
| id, | name, | cat, | time |
+-----+--------+------+------+
| 1 | jamie | 1 | 100 |
| 2 | jamie | 2 | 100 |
| 3 | jamie | 1 | 50 |
| 4 | jamie | 2 | 150 |
| 5 | bob | 1 | 100 |
| 6 | tim | 1 | 300 |
| 7 | alice | 4 | 100 |
+-----+--------+------+------+
I tried using the "Left Joining with self, tweaking join conditions and filters" part of this answer: SQL Select only rows with Max Value on a Column but some reason when there are records with a value of 0 it breaks, and it also doesn't return every unique answer for some reason.
When doing the query on this table I'd like to receive the following values:
+-----+--------+------+------+
| id, | name, | cat, | time |
+-----+--------+------+------+
| 1 | jamie | 1 | 100 |
| 4 | jamie | 2 | 150 |
| 5 | bob | 1 | 100 |
| 6 | tim | 1 | 300 |
| 7 | alice | 4 | 100 |
+-----+--------+------+------+
Because they are unique on name and cat and have the highest time value.
The query I adapted from the answer above is:
SELECT a.name, a.cat, a.id, a.time
FROM data A
INNER JOIN (
SELECT name, cat, id, MAX(time) as time
FROM data
WHERE extra_column = 1
GROUP BY name, cat
) b ON a.id = b.id AND a.time = b.time
The issue here is that ID is unique per row you can't get the unique value when getting the max; you have to join on the grouped values instead.
SELECT a.name, a.cat, a.id, a.time
FROM data A
INNER JOIN (
SELECT name, cat, MAX(time) as time
FROM data
WHERE extra_column = 1
GROUP BY name, cat
) b ON A.Cat = B.cat and A.Name = B.Name AND a.time = b.time
Think about it... So what ID is mySQL returning form the Inline view? It could be 1 or 3 and 2 or 4 for jamie. Hows does the engine know to pick the one with the max ID? it is "free to choose any value from each group, so unless they are the same, the values chosen are indeterminate. " it could pick the wrong one resulting in incorrect results. So you can't use it to join on.
https://dev.mysql.com/doc/refman/5.0/en/group-by-handling.html
If you want to use a self join, you could use this query:
SELECT
d1.*
FROM
date d1 LEFT JOIN date d2
ON d1.name=d2.name
AND d1.cat=d2.cat
AND d1.time<d2.time
WHERE
d2.time IS NULL
It is very simple
SELECT MAX(TIME),name,cat FROM table name group by cat