MySQL Multiple JOIN query - mysql

I have 3 tables:
Table A:
id int
value varchar
Table B:
id int
a_id default null
Table C:
id int
a_id not null
And I need to group number of B rows and C rows by A.value:
+---------+----------------------+----------------------+
| A.value | COUNT(DISTINCT B.id) | COUNT(DISTINCT C.id) |
+---------+----------------------+----------------------+
| NULL | 100 | 0 |
| 1 | 543 | 324 |
...
The problem is that the B table has a nullable foreign key while C.a_id can not be null.
So after hour of trying I can't get the right query. Either C.a_id are losing or B.a_id.
What is the right way to get it?

Because the values are pretty large, it might be better to do the calculations in subqueries:
select a.name, Distinct_B, Distinct_C
from (select distinct a.name from TableA a) a left outer join
(select a.value, count(distinct b.id) as Distinct_B
from TableA a join
TableB b
on a.id = b.a_id
group by a.value
) b
on a.value = b.value left outer join
(select a.value, count(distinct c.id) as distinct_C
from TableA a join
TableC c
on a.id = c.a_id
) c
on a.value = c.value
This looks more complicated, but it does not require a partial cartesian product within each a.value. Also, it can be simplified, if there are no multiple a.values allowed.
If you want to keep all B values that have "NULL" a_id by assigning them a NULL a.value, then use this subquery instead:
select a.value, sum(Distinct_B), sum(Distinct_C)
from ((select distinct a.name, 0 as Distinct_B, 0 as Distinct_C
from TableA a
) union all
(select a.value, count(distinct b.id) as Distinct_B, 0 as Distinct_C
from TableB b left outer join
TableA a
on a.id = b.a_id
group by a.value
) union all
(select a.value, 0 as Distinct_B, count(distinct c.id) as distinct_C
from TableC c left outer join
TableA a
on a.id = c.a_id
)
) t
group by a.value
This uses aggregation instead of a join to bring the values together.

Related

How can I return 1 row from a subquery in select statement if subquery has more than 1 result?

I have this query:
select a.*, b.*, (select c.* from tableC c where c.id_tableA = a.id) from tableA a inner join tableB b on a.id = b.id_tableA where b.id_user = 50;
The subquery (which is tableC) is returning me more than 1 row as expected. How can I return only 1 row from tableC so it could match with the rest of the query?
So far I have tried this:
(select c.* from tableC c where c.id_tableA = a.id limit 1)
It didn't work as mysql said:
"Operand should contain 1 column(s)"
You are mixing two things. Scalar subquery in SELECT list should return only one value (both row and column). Using LIMIT 1 will get you one row, but still many columns.
So you could specify column name:
select a.*, b.*,
(select c.col_name from tableC c where c.id_tableA = a.id order by .. limit 1)
from tableA a
inner join tableB b on a.id = b.id_tableA
where b.id_user = 50;
or use normal JOIN:
select a.*, b.*, c.*
from tableA a
inner join tableB b
on a.id = b.id_tableA
left join tableC c
on c.id_tableA = a.id
where b.id_user = 50;
if column id from table C is a primary key then it should have no problem
but if no, try to add another condition that will filter your subquery results like ,
for example here is the start_date:
SELECT a.column_1, b.column_2,
(SELECT column_3 FROM tableC
WHERE (id = a.id
AND (start_date = (SELECT MAX(b.start_date)
from tableC as c
where a.id = c.id ))) AS column_3
FROM tableA as a INNER JOIN
tableB as b ON b.id = a.id
WHERE b.id_user = 50;

MySQL: join 3 tables and select an identical row as alias

I want to join 3 tables in something like the following manner:
SELECT a.id
FROM tableA AS a
LEFT JOIN tableB AS b ON a.id = b.id
LEFT JOIN tableC AS c ON a.id = c.id
WHERE
b.name = c.name OR b.name IS NULL OR c.name IS NULL;
I can't be sure, that table b or c will have a row to join, but if both have a row to join, the name column must be identical.
My question is: I want to select this name column, but I only want to select it once. So, if b and c have a row to join, I want the name from either of them, If just one has a row to join, I want the name of that row.
The column name in the result should be in each case identical.
Table examples
tableA
id
----
1
2
3
4
tableB
id | name
----|------
2 | X
3 | Y
tableC
id | name
----|------
2 | Q
3 | Y
4 | Z
desired result:
id | name
----|------
1 | (NULL)
2 | X (name is from tableB)
2 | Q (name is from tableC)
3 | Y (name is from tableB or tableC)
4 | Z (name is from tableC)
I hope this will help you
SELECT
a.id,
(CASE COALESCE(b.`name`, '') WHEN '' THEN c.`name` ELSE b.`name` END) AS name2,
b.`name` AS B,
c.`name` AS C
FROM foo1 AS a
LEFT JOIN foo2 AS b ON (a.id = b.id)
LEFT JOIN foo3 AS c ON (a.id = c.id)
ORDER BY a.id;
SELECT a.id, IFNULL(b.name, c.name)
FROM tableA AS a
LEFT JOIN tableB AS b ON a.id = b.id
LEFT JOIN tableC AS c ON a.id = c.id
WHERE
b.name = c.name OR b.name IS NULL OR c.name IS NULL;
This query should return what you want. IFNULL(X, Y) works by saying if X is null, then use Y. With your example, if b.name is not null, b.name will be shown. If b.name is null, then c.name will be shown. If something is in c.name, then the name will be printed, but if c.name is null NULL will be shown as you desired.
Another possibility, would be best to get rid of LEFT at all, if not necessary.
SELECT a.id, b.name
FROM tableA AS a
LEFT JOIN tableB AS b ON a.id = b.id
UNION
SELECT a.id, c.name
FROM tableA AS a
LEFT JOIN tableC AS c ON a.id = c.id
if you don't want the rows where name is NULL, remove LEFT

Two right joins

I am trying to select all records in TABLEC and its equivalent value in TABLEA or TABLEB using right join. I am using MYSQL 5.5.47.
--Table data as follows
TABLEA TABLEB TABLEC
ID FNAME ID MNAME ID LNAME
0 ANOOP 0 N 0 SINGH
1 BIMA 2 SITA 3 RAJ
4 CIMI 4 B 5 KUMAR
6 RAVI 5 A 6 D
--Using below query and trying to select all records in TABLEC and its equivalent value in TABLEA or TABLEB
SELECT A.FNAME, B.MNAME, C.LNAME
FROM TABLEA AS A
RIGHT JOIN TABLEB AS B ON A.ID = B.ID
RIGHT JOIN TABLEC AS C ON C.ID = B.ID
--I am getting the following result
ANOOP N SINGH
NULL NULL RAJ
NULL A KUMAR
***NULL*** NULL D
The highlighted value doesn’t show the value as 'RAVI' instead it shows NULL in MYSQL 5.5.47. I tried to modify the '=' condition in second join related to C & A but still no luck. What am I doing wrong here? How do I get the value 'RAVI' in place of NULL? Any suggestion would be highly helpful.
DROP TABLE IF EXISTS table_a;
DROP TABLE IF EXISTS table_b;
DROP TABLE IF EXISTS table_c;
CREATE TABLE table_a
(id INT NOT NULL PRIMARY KEY
,fname VARCHAR(12) NULL
);
INSERT INTO table_a VALUES
(0,'ANOOP'),
(1,'BIMA'),
(4,'CIMI'),
(6,'RAVI');
CREATE TABLE table_b
(id INT NOT NULL PRIMARY KEY
,mname VARCHAR(12) NULL
);
INSERT INTO table_b VALUES
(0,'N'),
(2,'SITA'),
(4,'B'),
(5,'A');
CREATE TABLE table_c
(id INT NOT NULL PRIMARY KEY
,lname VARCHAR(12) NULL
);
INSERT INTO table_c VALUES
(0,'SINGH'),
(3,'RAJ'),
(5,'KUMAR'),
(6,'D');
SELECT a.fname
, b.mname
, c.lname
FROM table_c c
LEFT
JOIN table_a a
ON a.id = c.id
LEFT
JOIN table_b b
ON b.id = c.id;
+-------+-------+-------+
| fname | mname | lname |
+-------+-------+-------+
| ANOOP | N | SINGH |
| NULL | NULL | RAJ |
| NULL | A | KUMAR |
| RAVI | NULL | D |
+-------+-------+-------+
4 rows in set (0.02 sec)
You are trying to select all records in TABLEC and its equivalent value in TABLEA or TABLEB using right join. So Table A and B is joined to Table c records. So we need to use Left join(you will get all records of Table C and common records of Table A and B). More info please ref this link
SELECT
ifnull(A.FNAME,""),
ifnull(B.MNAME,""),
ifnull(C.LNAME,"")
FROM
TABLEA AS A
LEFT JOIN
TABLEB AS B
ON
A.ID = B.ID
LEFT JOIN
TABLEC AS C
ON
C.ID = B.ID
The problem is you don't have a table that contains all the ids. So you have to make one. Then you can join from that.
Get all the ids with this query
SELECT ID FROM TABLEA
UNION
SELECT ID FROM TABLEB
UNION
SELECT ID FROM TABLEC
Now we can use this query/table of ids to join the others
SELECT A.FNAME, B.MNAME, C.LNAME
FROM (
SELECT ID FROM TABLEA
UNION
SELECT ID FROM TABLEB
UNION
SELECT ID FROM TABLEC
) I
LEFT JOIN TABLEA A ON I.ID = A.ID
LEFT JOIN TABLEB B ON I.ID = B.ID
LEFT JOIN TABLEC C ON I.ID = C.ID
Of course if you had another table (TABLEID) that had a list of all IDs you could use that instead of the sub-query above. It might be your model has such a table, but we won't know unless you tell us.
As you said "select all records in TABLEC and its equivalent value in TABLEA or TABLEB", so you need to join the C with A and C with B. So your need to update your query as :
SELECT A.FNAME, B.MNAME, C.LNAME
FROM TABLEC AS C RIGHT JOIN TABLEB AS B
ON B.ID = C.ID
RIGHT JOIN TABLEA AS A
ON C.ID = A.ID
If you want all the record that exist in A, B and C. The NULL value will shown for the record which doesn't have the value,
SELECT A.FNAME, B.MNAME, C.LNAME
FROM (
TABLEA AS A
LEFT JOIN TABLEB AS B ON B.ID = A.ID
)
RIGHT JOIN TABLEC AS C ON ( C.ID = B.ID
OR B.ID = NULL
OR A.ID = C.ID )
WHERE 1
Use this works perfectly
SELECT A.FNAME, B.MNAME, C.LNAME
FROM TABLEC AS C
LEFT JOIN TABLEA AS A ON (A.ID = C.ID)
LEFT JOIN TABLEB AS B ON (B.ID = C.ID)

query impossible for me?

I have 2 columns a and b with a 1:n relation:
A.id, a.text, a.b_id(fk), a.value --
B.id(pk), b.text etc
I want to create a query that returns the number of b.id with count(a.value=1) > 0
I tried this:
Select count(b.id)
from a.id
join b on a.b_id=b_id
group by b.id
having count(if(a.value=1),1,null))>0
...but without result. It seems simple but for me is a problem.
You don't need HAVING because standard INNER join won't return b rows without matching a rows anyway. You don't need GROUP BY either - use COUNT(DISTINCT ) instead:
SELECT COUNT(DISTINCT b.id) AS cnt
FROM b
JOIN a ON a.b_id = b.id
WHERE a.value = 1
This should do:
SELECT COUNT(*)
FROM TableB AS B
WHERE EXISTS(SELECT 1 FROM TableA
WHERE id = B.id
AND value = 1)
Try this:-
Select count(b.id) as x
FROM b
JOIN a ON a.b_id = b.id
WHERE a.value = 1
group by b.id
A simplification on #MarcinJuraszek's answer. If the foreign key can be trusted, there is no need to join table b:
SELECT COUNT(DISTINCT a.b_id) AS cnt
FROM a
WHERE a.value = 1 ;

All conditional data required in mysql

I have three table a,b,c having id common between them.
Table a:-
id name value
1 a 4
2 v 6
Table b:-
id abc
2 54
3 56
Table c:-
id bcd
1 54
3 34
Now what i want is what ever is id in where condition, data comes from all tables.
Please advice me how to do that.
Expected Result-
if query is
select * from a left join b on a.id=b.id left join c on a.id=c.id where b.id=3
id name value bcd abc
3 NULL NULL 34 56
if query is
select * from a left join b on a.id=b.id left join c on a.id=c.id where a.id=1
id name value bcd abc
3 a 4 54 NULL
What about this approach to the problem? :)
SELECT
z.id,
a.name,
a.value,
c.bcd,
b.abc
FROM
(
SELECT
DISTINCT y.id id
FROM
(
SELECT id FROM a
UNION ALL
SELECT id FROM b
UNION ALL
SELECT id FROM c
) y
) z
LEFT JOIN a ON z.id = a.id
LEFT JOIN b ON z.id = b.id
LEFT JOIN c ON z.id = c.id
where z.id = 3
sql fiddle
This way you just need to give the query the number not caring about which tables it exists in.
It's depends on what you are setting in WHERE condition. If you are setting WHERE b.ID = 3 then you need to join other tables with B like this:
SELECT A.ID AS A_ID,A.Name, A.value
,B.Id as B_ID,B.abc
,C.id AS C_ID, c.bcd
FROM b
LEFT JOIN a ON a.id = b.id
LEFT JOIN c ON a.id = c.id
WHERE b.id=3;
This is happens because b.ID = 3 is not in Table A and Table C is joined with Table A.
If you set Table A.ID = 1 then you have to join other tables with A using LEFT JOIN like this:
SELECT A.ID AS A_ID,A.Name, A.value
,B.Id as B_ID,B.abc
,C.id AS C_ID, c.bcd
FROM A
LEFT JOIN B ON a.id = b.id
LEFT JOIN c ON a.id = c.id
WHERE A.id=1;
See this SQLFiddle
This is technically impossible, when you are using ID in where how can you get data in case there Id not present in any of the perticular table, you are changing the logic of where ;).
But what you can do is
SELECT * FROM
(SELECT AID AS ID,NAME,VALUE FROM A
UNION
SELECT BID as ID,NAME,NULL AS VALUE FROM B
UNION
SELECT CID as ID,NAME ,NULL AS VALUE FROM C)
WHERE ID =''
Hope this helps
else please clarify. what you want.
Regards
Ashutosh Arya
I will try to guess, even though I barely find an explanation to the expected result:
SELECT
b.id,
a.name,
a.value,
c.bcd,
b.abc
FROM
b
INNER JOIN c ON b.id = c.id
LEFT JOIN a ON b.id = a.id
sql fiddle