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

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.

Related

MySQL select all left entries from a table which is joined from another table

I have the following MySQL-Statement:
SELECT norm.NormID, norm.NormName
FROM (assignment
INNER JOIN norm
ON assignment.NID = norm.NormID )
INNER JOIN wire
ON assignment.LID = wire.WireID
WHERE wire.WireID= 109
ORDER BY norm.NormName;
Now what I got are the entries from the table assignment with the NormID and NormName for that WireID.
What I want to get are the entries from the table norm, which are not setted for this WireID.
E.g.:
WireID has the norm assignment A, B, D, G.
The table norm has the entries A, B, C, D, E, F, G, H.
What I want to get from the MySQL-Statment are the entries C, E, F, H.
How can I select those left norm entries for this WireID?
With the above statement I would get:
-----------------------
| NormID | NormName |
-----------------------
| 1 | A |
| 2 | B |
| 4 | D |
| 7 | G |
-----------------------
I want to have this Table:
-----------------------
| NormID | NormName |
-----------------------
| 3 | C |
| 5 | E |
| 6 | F |
| 8 | H |
-----------------------
I think (if I understood what you asked) you can try this :
SELECT norm.NormID, norm.NormName
FROM assignment
INNER JOIN norm ON assignment.NID = norm.NormID
LEFT JOIN wire ON assignment.LID = wire.WireID
WHERE assignment.LID= 109
AND wire.wireID IS NULL
ORDER BY norm.NormName;
Edit after your comments.
I think you could use:
SELECT A.NormID, A.NormName
FROM norm A
LEFT JOIN (SELECT NID FROM assignment WHERE LID = 109) B ON B.NID = A.NormID
WHERE B.NID IS NULL
ORDER BY A.NormName;
OR
SELECT A.NormID, A.NormName
FROM norm A
WHERE NOT EXISTS (SELECT 1 FROM assignment WHERE LID = 109 AND ASSIGNMENT.NID = A.NormID)
ORDER BY A.NormName;
Try this:
select norm.NormID,norm.NormName from norm
Inner JOIN
assignment on assignment.NID = norm.NormID
where assignment.LID in(select wireID from Wire where WireID = 109)
Im not so sure coz i dont have your data
after you added a sample data the entries that are not setted with 109 wireid are these:
SELECT norm.NormID, norm.NormName
FROM assignment
inner JOIN norm
ON assignment.NID = norm.NormID
INNER JOIN wire
ON assignment.LID = wire.WireID
WHERE wire.WireID <> 109
ORDER BY norm.NormName;

sql query combine two queries into one with empty rows

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.

Select * as well as count/sum from another table

I am trying to write a MySQL query to select all rows from table a, as well as information from table b, while also querying the count and sum of values in another table c, for each row of a.
I will try to break that down a bit better, here is a simplified version of my tables:
Table A
+---------+----------+-----------+
| id | name | bid |
+---------+----------+-----------+
| 1 | abc | 1 |
| 2 | def | 1 |
| 3 | ghi | 2 |
+--------------------------------+
Table B
+---------+----------+
| id | name |
+---------+----------+
| 1 | STAN |
| 2 | UCLA |
+--------------------+
Table C
+---------+----------+-----------+
| id | aid | cnumber |
+---------+----------+-----------+
| 1 | 1 | 40 |
| 2 | 1 | 20 |
| 3 | 2 | 10 |
| 4 | 3 | 40 |
| 5 | 3 | 20 |
| 6 | 3 | 10 |
+--------------------------------+
What I need is a query that will return rows containing
a.id | a.name | b.id | b.name | SUM(c.cnumber) | COUNT(c.cnumber)
I am not sure if such a thing is even possible in MySQL.
My current solution is trying to query A + B Left Join and then UNION with A + C Right Join. It's not giving me the results I'm looking for however.
Thanks.
edit:
current query re-written for this problem:
SELECT
a.id,
a.name,
b.id,
b.name
"somecolumn" as dummy_column
"somecolumn1" as dummy_column1
FROM a
LEFT OUTER JOIN b ON a.b.id = b.id
UNION
SELECT
"somecolumn" as dummy_column
"somecolumn1" as dummy_column1
"somecolumn2" as dummy_column2
"somecolumn3" as dummy_column3
COUNT(c.cnumber) AS ccount
SUM(c.cnumber) AS sum
FROM a
RIGHT OUTER JOIN c ON a.id = c.a.id;
Unfortunately MySQL doesn't have FULL OUTER JOIN, and this is my temporary work around, although I don't think it's even the proper idea.
My desired output is all the rows from table A with some info from table B, as well as their totaled inputs in table C.
edit2:
SELECT
a.id,
a.name,
b.id,
SUM(c.cnumber) as totalSum,
(SELECT count(*) FROM c as cc WHERE cc.aid = a.id) as totalCount
FROM
a
LEFT JOIN b ON a.bid = b.id
LEFT JOIN c ON c.aid = a.id;
For future similar questions, solution:
SELECT
a.id AS aid,
a.name,
b.id,
(SELECT SUM(c.rating) FROM c WHERE c.aid = aid) AS totalSum,
(SELECT COUNT(c.rating) FROM c WHERE c.aid = aid) AS totalCount
FROM
a
LEFT JOIN b ON a.bid = b.id;
Your query should be :-
SELECT
a.id,
a.name,
b.id,
(SELECT SUM(c.cnumber) FROM c as cd WHERE cd.aid = a.id) as totalSum,
(SELECT count(*) FROM c as cc WHERE cc.aid = a.id) as totalCount
FROM
a
LEFT JOIN b ON a.bid = b.id
LEFT JOIN c ON c.aid = a.id;
It may help you.
I have tried your example.
SELECT * ,
(SELECT SUM(cnumber) FROM `table_c` WHERE `table_c`.`iaid` = `table_a`.`id`),
(SELECT COUNT(cnumber) FROM `table_c` WHERE `table_c`.`a.id` = `table_a`.`id`)
FROM `table_a`,`table_b` WHERE `table_b`.`id` = `table_a`.`b.id`
i got the Following output.

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

How do I inner join multiple tables?

I have tables A, B and C and I want to get matching values for from all tables (tables have different columns).
Table A (primary key = id)
+------+-------+
| id | name |
+------+-------+
| 1 | Ruby |
| 2 | Java |
| 3 | JRuby |
+------+-------+
Table B (pid is reference to A(id) - No primary key)
+------+------------+
| pid | name |
+------+------------+
| 1 | Table B |
+------+------------+
Table C (primary key = id, pid is reference to A(id))
+------+------+------------+
| id | pid | name |
+------+------+------------+
| 1 | 2 | Table C |
+------+------+------------+
So my below query returned nothing. Whats wrong here? Is it treated as AND when multiple inner joins present?
Select A.* from A
inner join B ON a.id = b.pid
inner join C ON a.id = c.pid;
As you first join
1 | Ruby | Table B
and then try to join Table C, there is no match for pid 2 in the aforementioned result, the result is therefore empty.
An inner join excludes everything that doesn't match. So after you joined against B, you were left with only one record (id=1). Your inner join against C doesn't have any matches from what's left, so you get nothing.
I suppose a union would do the trick:
select A.* from A join B on a.id = b.pid
union
select A.* from A join C on a.id = c.pid
Or there are other ways, like where a.id in (select pid from b) or a.id in (select pid from c)
When you inner-join like this, a single row from A needs to exist such that a.id = b.pid AND a.id = c.pid are true at the same time. If you examine the rows in your examples, you would find that there is a row in A for each individual condition, but no rows satisfy both conditions at once. That is why you get nothing back: the row that satisfies a.id = b.pid does not satisfy a.id = c.pid, and vice versa.
You could use an outer join to produce two results:
select *
from A
left outer join B ON a.id = b.pid
left outer join C ON a.id = c.pid;
a.id a.name b.pid b.name c.id c.pid c.name
1 | Ruby | 1 | Table B | NULL | NULL | NULL
2 | Java | NULL | NULL | 1 | 2 | Table C
Of course you return nothing!
Table A and B inner join returns 1st record of Table A (table A ID = 1)
then you join table C, there is no matching rows to join table C, and vice versa.