sql query combine two queries into one with empty rows - mysql

This is my sql table structure:
Table1: details
|--id--|--id_user--|--price--|
| 1 | 1 | 10 |
| 2 | 2 | 15 |
| 3 | 1 | 25 |
| 4 | 3 | 30 |
| 5 | 3 | 7 |
------------------------------
Table2: users
|--id--|--id_country--|
| 1 | 1 |
| 2 | 2 |
| 3 | 0 |
-----------------------
Table3: country
|--id--|--country--|
| 1 | France |
| 2 | Italy |
--------------------
What I need is to get the SUM of price by country:
SELECT c.country, SUM(d.price) AS price
FROM details d
INNER JOIN users u ON u.id = d.id_user
INNER JOIN country c ON c.id = u.id_country
GROUP BY c.country
ORDER BY c.country
I get this:
|--country--|--price--|
| France | 35 |
| Italy | 15 |
-----------------------
BUT I'd need to get this:
|--country--|--price--|
| France | 35 |
| Italy | 15 |
| Undefined | 37 |
-----------------------
where undefined would be if id_country=0. (I can't add to country table the id=0 or id=undefined, it will messed up other things). Right now I'm achieving this by two separate queries, the second one is:
SELECT SUM(d.price) as price
FROM details d
INNER JOIN users u ON u.id = d.id_user AND u.id_country=0
GROUP BY u.id_country
I'm thinking if... is it possible to do this in one query?

You need to use left join in this case:
SELECT c.country, SUM(d.price) AS price
FROM details d
LEFT JOIN users u ON u.id = d.id_user
LEFT JOIN country c ON c.id = u.id_country
GROUP BY c.country
ORDER BY c.country
If you use INNER JOIN, you will only get results that exists in both tables.
To replace NULL with Undefined use:
SELECT IFNULL(c.country,'Undefined') AS Country, SUM(d.price) AS price
FROM details d
LEFT JOIN users u ON u.id = d.id_user
LEFT JOIN country c ON c.id = u.id_country
GROUP BY c.country
ORDER BY c.country
One way to sort to get Undefined last is to add a Sortfield
SELECT A.Country,A.Price FROM (
SELECT IFNULL(c.country,'Undefined') AS Country, SUM(d.price) AS price, IFNULL(c.Country,'ZZZZZZZZ') AS Sort
FROM details d
LEFT JOIN users u ON u.id = d.id_user
LEFT JOIN country c ON c.id = u.id_country
GROUP BY c.country
) A
ORDER BY A.Sort
Edit: ORDER BY suggested in comments
SELECT IFNULL(c.country,'Undefined') AS Country, SUM(d.price) AS price
FROM details d
LEFT JOIN users u ON u.id = d.id_user
LEFT JOIN country c ON c.id = u.id_country
GROUP BY c.country
ORDER BY c.country IS NULL, c.country

Try below query.
SELECT
CASE
WHEN c.country is NULL THEN 'Undefined'
ELSE c.country
END as country
, SUM(d.price) AS price
FROM users u
left JOIN details d ON u.id = d.id_user
left JOIN country c ON c.id = u.id_country
GROUP BY c.country
ORDER BY c.country
For Demo :
SqlfiddlE Demo :
Please let us know if you have any que.

Related

MySQL: Join with 4 tables and common field: row_number()

I have 4 tables -
Tab: d
Name | ID
----------
A | 1
B | 2
C | 3
Tab: p
Name | ID
----------
AX | 1
B | 2
X | 3
Y | 4
Z | 5
Tab: s
Name | ID
----------
A | 1
BL | 2
V | 3
M | 4
Tab: a
Name | ID
----------
K | 1
J | 2
H | 3
N | 4
Now I am using MySQL and today I found out that MySQL does not support FULL join. So, I am using left join with all 4tables and then using "union" and right join to merge all the 4 tables' records.
The query I am using is like -
(select d.Name, p.Name, s.Name, a.Name from doc d
left join
prof p
on d.id = p.id
left join
sing s
on d.id = s.id
left join
act a
on d.id = a.id)
union
(select d.Name, p.Name, s.Name, a.Name from doc d
right join
prof p
on d.id = p.id
right join
sing s
on d.id = s.id
right join
act a
on d.id = a.id)
But this is not giving the intended output. It is giving something like -
D | P | S | A
---------------------------------
A | AX | A | K
B | B | BL | J
C | X | V | H
NULL | NULL | NULL | N
Actual output should be -
D | P | S | A
---------------------------------
A | AX | A | K
B | B | BL | J
C | X | V | H
NULL | Y | M | N
NULL | Z | NULL | NULL
Please help me to figure out what I am missing! And also to help me to get the result...
The behavior you really want here is a full outer join, but MySQL does not directly support that (and the workaround is fairly ugly). One approach I can suggest here would be to maintain a fifth table containing all ID values which you expect in the result set. Consider:
SELECT c.ID, d.Name, p.Name, s.Name, a.Name
FROM
(SELECT 1 AS ID UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL
SELECT 4 UNION ALL SELECT 5) c
LEFT JOIN d ON d.ID = c.ID
LEFT JOIN p ON p.ID = c.ID
LEFT JOIN s ON s.ID = c.ID
LEFT JOIN a ON a.ID = c.ID
ORDER BY c.ID;
Assuming that you're using MySQL v8, you might be able to do it this way:
WITH RECURSIVE cte AS (
SELECT 1 AS rn, MAX(cnt) AS mxcnt
FROM
( SELECT COUNT(ID) cnt FROM doc UNION
SELECT COUNT(ID) FROM prof UNION
SELECT COUNT(ID) FROM sing UNION
SELECT COUNT(ID) FROM act ) v UNION ALL
SELECT rn+1, mxcnt FROM cte WHERE rn+1 <= mxcnt)
SELECT c.rn, d.Name, p.Name, s.Name, a.Name
FROM cte c
LEFT JOIN doc d ON d.ID = c.rn
LEFT JOIN prof p ON p.ID = c.rn
LEFT JOIN sing s ON s.ID = c.rn
LEFT JOIN act a ON a.ID = c.rn
ORDER BY c.rn;
Using WITH RECURSIVE to generate numbering sequence based on the largest count result from all related table then use it as the reference in LEFT JOIN. I agree with Tim about having a master table for all of the ids.

MySQL get sum in nested left joins

I've a food listing system. It has five tables.
REGIONS
BANNERS
CUISINE
RESTAURANTS
RESTAURANT_SPONSORED
BANNERS, CUISINE and RESTAURANT_SPONSORED table records the revenue of advertising.
I want to generate this table.
name | banner_revenue | cuisine_revenue | restaurant_promotions
------------------------------------------------------------------
NY | 10,000 | 4,800 | 12,000
Paris | NULL | 8,000 | 8,000
London | NULL | NULL | 2,000
This query will output,
SELECT r.name,
sb.fee,
sc.fee
FROM REGIONS r
LEFT JOIN (SELECT sum(b.fee) fee,
b.region_id
FROM BANNERS b
GROUP BY b.region_id) sb
ON sb.region_id = r.id
LEFT JOIN (SELECT sum(c.fee) fee,
c.region_id
FROM CUISINE c
GROUP BY c.region_id) sc
ON sc.region_id = r.id;
name | banner_revenue | cuisine_revenue |
--------------------------------------------
NY | 10,000 | 4,800 |
Paris | NULL | 8,000 |
London | NULL | NULL |
But how do I get restaurant_promotions part? Which is needed a nested left join.
DB Fiddle demo
modify your query to :
SELECT r.name,
sb.fee,
sc.fee,
sr.fee
FROM REGIONS r
LEFT JOIN (SELECT sum(b.fee) fee,
b.region_id
FROM BANNERS b
GROUP BY b.region_id) sb
ON sb.region_id = r.id
LEFT JOIN (SELECT sum(c.fee) fee,
c.region_id
FROM CUISINE c
GROUP BY c.region_id) sc
ON sc.region_id = r.id
LEFT JOIN(SELECT sum(RESTAURANT_SPONSORED.fee) fee,
R.region_id
FROM RESTAURANTS R
LEFT JOIN RESTAURANT_SPONSORED ON(RESTAURANT_SPONSORED.restaurant_id = R.id)
GROUP BY R.region_id) sr
ON sr.region_id= r.id
this should work

Query to join and count rows in mysql

I have two table portal and login table.How to get count of portal_id in login table and join with portal table. If there is no matching row exists in login table show as null value
$this->db->select("a.name");
$this->db->from("{$this->Portal} a");
$this->db->join("{$this->login} b","a.id = b.portal_id");
$this->db->order_by("a.portal_id asc");
Table portal
id | name
1 | john
2 | steve
3 | ricky
4 | richard
Table Login
portal_id | city
1 | Bangalore
2 | Ludhiana
1 | Chandighara
2 | Delhi
Result Table
id | name | count
1 | john | 2
2 | steve | 2
3 | ricky | null
Simple left join needs to be used, to get the counts as null instead of zero you can use nullif
select p.id,
p.name,
NULLIF(count(l.portal_id), 0) as
portal_logn_count
from portal p left join login l on p.id =
l.portal_id
group by p.id,p.name
order by p.id,p.name
SELECT p.id, p.name, COUNT(l.id) AS `count`
FROM portal p
LEFT JOIN Login l ON l.portal_id = p.id
GROUP BY p.id
$this->db->select("a.id,a.name,count(a.id)");
$this->db->from("{$this->Portal} a");
$this->db->join("{$this->login} b","a.id = b.portal_id", 'left');
$this->db->group_by("a.id");
$this->db->order_by("a.id asc");
Making a query like
Select a.id, a.name, count(a.id) from portal a
left join login b on a.id = b.portal_id
group by a.id
order by a.id asc
Works Perfectly....
Select a.id, a.name, NULLIF(count(b.portal_id ), 0) from portal a
left join Login b on a.id = b.portal_id
group by a.id
order by a.id asc

MYSQL - Group Contact rows with records NOT IN

My case looks simple but i'm messing around with this..
I have 4 tables: User, Macros, Categories, and another one that relate users with categories. One Macro have many Categories.
What i need, is a query that based on the Macro, get the users and the Categories where user is NOT IN.
Example: I have a macro named VEICULES, with categories CAR,TRUCK and Motorcycle. User José is on category CAR and User Julio on category CAR and TRUCK, so my query should return:
José | TRUCK,Motorcycle
Julio | Motorcycle
Tables:
prd_users
id | name | Email
---------------------------
1 | José | jose#email.com
2 | Júlio | julio#email.com
3 | André | andre#email.com
cat_macros
macro_id | macro_name
-----------------------
1 | Veicules |
cat_categories
category_id | category_name | macro_id
---------------------------------------
1 | Cars | 1
2 | Trucks | 1
3 | Motorcycles | 1
prd_tr_rabbit_catg
id | category_id | tasker_user_id
---------------------------------------
1 | 1 | 1
2 | 1 | 2
3 | 2 | 2
I'm stucked on just getting the categories where the user already is ..
SELECT prd_users.id, prd_users.name,
prd_users.email,cat_macros.macro_name as macro,
GROUP_CONCAT(cat_categories.category_name SEPARATOR ', ') as in_categories
FROM prd_users
INNER JOIN prd_tr_rabbit_catg ON prd_tr_rabbit_catg.tasker_user_id = prd_users.id
INNER JOIN cat_categories ON cat_categories.category_id = prd_tr_rabbit_catg.category_id
INNER JOIN cat_macros ON cat_macros.macro_id = cat_categories.macro_id
WHERE cat_macros.macro_id = '45'
GROUP BY prd_users.id;
To solve this problem it's necessary to create a list of all users joined with all categories for the given macro category. This can be done with a CROSS JOIN:
SELECT *
FROM prd_users u
CROSS JOIN (SELECT m.macro_id, m.macro_name, c.category_name, c.category_id
FROM cat_macros m
JOIN cat_categories c ON c.macro_id = m.macro_id) c
This can then be LEFT JOINed to the prd_tr_rabbit_catg table and by selecting those rows where there is no matching entry in the prd_tr_rabbit_catg table, we can find the users who don't have an entry for the given category:
SELECT c.macro_name, u.id AS user_id, u.name, u.Email, GROUP_CONCAT(c.category_name) AS missing_cats
FROM prd_users u
CROSS JOIN (SELECT m.macro_id, m.macro_name, c.category_name, c.category_id
FROM cat_macros m
JOIN cat_categories c ON c.macro_id = m.macro_id) c
LEFT JOIN prd_tr_rabbit_catg x ON x.tasker_user_id = u.id AND x.category_id = c.category_id
WHERE x.id IS NULL
AND c.macro_id = 1
GROUP BY c.macro_name, u.id
For your sample data, this gives:
macro_name user_id name Email missing_cats
Veicules 1 José jose#email.com Motorcycles,Trucks
Veicules 2 Júlio julio#email.com Motorcycles
Veicules 3 André andre#email.com Cars,Motorcycles,Trucks
Update
To exclude users who don't have any of the categories, add a HAVING clause:
HAVING COUNT(*) < (SELECT COUNT(*) FROM cat_categories WHERE macro_id = 1)
Demo on SQLFiddle

MySQL: SELECT from three tables?

I have problems with a MySQL query with three tables. I would like to search for a name and get all (even better only the first one) phonenumber and email. Here are my tables:
Table 1, contact
==========
id | name
==========
1 | stefan
2 | michael
3 | andy
4 | bob
Table 2, phone
==============================
id | contact_id | phonenumber
==============================
1 | 1 | +1 434 434232
2 | 1 | +1 434 24234
3 | 2 | +1 89234
4 | 4 | +1 345345
5 | 4 | +1 434 7567567
Table 3, email
===============================
id | contact_id | emailaddress
===============================
1 | 1 | stefan#home.com
2 | 1 | stefan#work.com
3 | 1 | stefan#mars.com
4 | 4 | bob#anywhere.com
5 | 2 | michael#nothing.com
And this is my query, which seams to send MySQL to nirvana:
SELECT c.name, p.phonenumber, e.emailaddress
FROM contact AS c
JOIN phonenumber AS p ON c.id = p.contact_id
JOIN email AS e ON c.id = e.contact_id
WHERE c.name = 'michael'
When I do only one join this works fine as:
SELECT c.name, p.phonenumber
FROM contact AS c
JOIN phonenumber AS p ON c.id = p.contact_id
WHERE c.name = 'michael'
Any ideas?
Thanks
Mike
Try this:
SELECT c.name, p.phonenumber, e.emailaddress
FROM name_of_your_schema.contact AS c
JOIN name_of_your_schema.phone AS p ON c.id = p.contact_id
JOIN name_of_your_schema.email AS e ON c.id = e.contact_id
WHERE c.name = 'stefan'
LIMIT 1;
Tom L.
Try
SELECT c.name, p.phonenumber, e.emailaddress
FROM contact c
INNER JOIN phone p ON c.id = p.contact_id
INNER JOIN email e ON p.contact_id = e.contact_id
WHERE c.name = 'michael'
To get just one result per contact, you might use aggregation in a bit unorthodox way. I modified #Emanuel Saringan's query:
SELECT c.name, min(p.phonenumber), min(e.emailaddress)
FROM contact c
left JOIN phone p ON c.id = p.contact_id
left JOIN emailaddress e ON c.id = e.contact_id
WHERE c.name = 'michael'
GROUP BY c.id
See it work here:
http://sqlfiddle.com/#!2/6a8700/2