Select Union Query? - mysql

I have two tables:
Table A:
id name
------------
1 Scott
2 Dan
3 Sam
Table B:
id name
------------
1 Dan
2 Andi
3 Jess
My result needs to be:
Id Name Found
1 Scott A
2 Dan C i.e. found in both
3 Sam A
2 Andi B
3 Jess B
I am able to do a UNION to fetch the result but how do I create the Found column?

Use:
SELECT CASE
WHEN y.name IS NULL THEN z.id
WHEN z.name IS NULL THEN y.id
ELSE y.id
END AS id,
x.name,
CASE
WHEN y.name IS NULL THEN 'B'
WHEN z.name IS NULL THEN 'A'
ELSE 'C'
END AS found
FROM (SELECT a.name
FROM TABLE_A a
UNION
SELECT b.name
FROM TABLE_B b) x
LEFT JOIN TABLE_A y ON y.name = x.name
LEFT JOIN TABLE_B z ON z.name = x.name
Alternative:
SELECT COALESCE(y.id, z.id) AS id,
x.name,
CASE
WHEN y.name IS NULL THEN 'B'
WHEN z.name IS NULL THEN 'A'
ELSE 'C'
END AS found
FROM (SELECT a.name
FROM TABLE_A a
UNION
SELECT b.name
FROM TABLE_B b) x
LEFT JOIN TABLE_A y ON y.name = x.name
LEFT JOIN TABLE_B z ON z.name = x.name

The way to do this is to use a FULL OUTER JOIN, but since this is not supported in MySQL you can instead use a combination of a LEFT JOIN and INNER JOIN and RIGHT JOIN.
(
SELECT A.Id, A.Name, 'A' AS Found
FROM A LEFT JOIN B ON A.Name = B.Name
WHERE B.Name IS NULL
)
UNION ALL
(
SELECT B.Id, B.Name, 'B' AS Found
FROM A RIGHT JOIN B ON A.Name = B.Name
WHERE A.Name IS NULL
)
UNION ALL
(
SELECT A.Id, A.Name, 'C' AS Found
FROM A JOIN B ON A.Name = B.Name
)
In fact, you only need a LEFT and RIGHT JOIN because you can handle the INNER JOIN at the same time as you do one of the other two joins. I think the above demonstrates the principle more clearly, but in practice the following will give better performance:
SELECT A.Id, A.Name, IF(B.Name IS NULL, 'A', 'C') AS Found
FROM A LEFT JOIN B ON A.Name = B.Name
UNION ALL
SELECT B.Id, B.Name, 'B' AS Found
FROM A RIGHT JOIN B ON A.Name = B.Name
WHERE A.Name IS NULL
Result:
Id Name Found
1 Scott A
2 Dan C
3 Sam A
2 Andi B
3 Jess B

select tmp.name, case count(*) when 1 then tmp.tbl else 'C' end found
from (select id, name, 'A' tbl from TableA
union all
select id, name, 'B' tbl from TableB) as tmp
group by tmp.name;
+-------+-------+
| name | found |
+-------+-------+
| Andi | B |
| Dan | C |
| Jess | B |
| Sam | A |
| Scott | A |
+-------+-------+

It looks to me like you effectively want to simultaneously do a left and right join. This technically isn't possible, since you always need to have one reference table. The only way I can think of doing this would be the following:
SELECT tableA.*
LEFT JOIN tableB.* USING name
UNION DISTINCT SELECT tableB.*
LEFT JOIN tableA USING name
After thinking some more, you may also be able to do:
SELECT tableA.*
LEFT JOIN tableB.* USING name
RIGHT JOIN tableB.* USING name
...although I'm not sure that's valid.

Wrong answer
You are looking for "union distinct" instead of just union.
Corrected when I got the smack down and realized I mis read the question.
Generate a bitmask as below, using simple power notation to make it clearer that im just inserting ingrementing powers of 2 for the bitmask.
with data as (
SELECT Id,Name, 2^0 as bitmask FROM A
UNION ALL
SELECT Id,Name, 2^1 as bitmask FROM B
UNION ALL
SELECT Id,Name, 2^2 as bitmask FROM C)
SELECT Id,Name, SUM(bitmask)
FROM data
GROUP BY Id,Name

Related

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

Count rows that have the same set of related records

This is probably pretty simple but I can't figure this out. Here are the tables I'm working with.
table_a
id other_data
-------------
1 blah
2 foo
3 bar
table_b
ref_a ref_c
-------------
1 1
1 2
2 3
3 3
table_c
id name
----------
1 TestA
2 TestB
3 TestC
What I'm trying to get is something like this where I'm counting the number of rows (table_a) that have the same set of children (table_b). I also want to be able to get related data from another table (The name from table_c).
TestA,TestB 1
TestC 2
I know it probably uses Group By and GROUP_CONCAT but I can't get this to work.
I tried this but it doesn't work.
SELECT GROUP_CONCAT(DISTINCT table_c.name separator ', ') as 'combo_text', COUNT(DISTINCT table_a.id)
FROM table_a
INNER JOIN table_b
on table_a.id = table_b.ref_a
INNER JOIN table_c
on table_c.id = table_b.ref_c
GROUP BY table_b.ref_a
SELECT a.id, count(a.id) as count, GROUP_CONCAT(name) as names
FROM table_a a
JOIN table_b b ON (a.id = b.ref_a)
JOIN table_c c ON (b.ref_c = c.id)
GROUP BY a.id
sqlFiddle demo
based on your result you want something like this
SELECT names, count(count) as count FROM
(SELECT a.id, count(a.id) as count, GROUP_CONCAT(name) as names
FROM table_a a
JOIN table_b b ON (a.id = b.ref_a)
JOIN table_c c ON (b.ref_c = c.id)
GROUP BY a.id
)T1
GROUP BY names
sqlFiddle demo

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

MySQL Multiple JOIN query

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.

How to select last date in 2 record in Mysql

How to select last date in 2 records in Mysql ?
TABLE A
SID NAME Sex
1 Jam M
2 Da F
TABLE B
ID Title SID Date
1 A 1 2012-07-31 09:57:10
2 NULL 1 2012-07-31 09:57:13
3 A 2 2012-07-31 10:10:13
4 NULL 2 2012-07-31 10:57:13
I want to inner join those two tables,
but select only one record only of Table B(distinct of SID) where title not null then show the
biggest Date of title is null field.
Result want the biggest Date of Null title:
ID Name Title SID Date
1 Jam A 1 **2012-07-31 09:57:13**
2 DA A 2 **2012-07-31 10:57:13**
How to do that ?
SELECT b.ID, a.NAME, b.Title, b.SID, b.Datea
from tablea a left outer join
(( SELECT sid, MAX(datea) AS latest
FROM tableb
where title is not null
GROUP BY sid) AS dt
INNER JOIN tableb b ON b.sid= dt.sid and b.datea=dt.latest )
on a.sid=b.sid
I think something like this will work for you:
SELECT b.ID, a.NAME, b.Title, b.SID, b.Date
FROM TABLEA a
INNER JOIN (SELECT SID, IFNULL(Title, "") AS Title,
MAX(IF(Title IS NULL, Date, NULL)) Date
FROM TABLEB GROUP BY SID) b
ON a.SID = b,SID;
Try my code (it works):
SELECT b.id, b.title, b.sid, b.date
FROM table_b as b
JOIN table_a as a ON a.sid = b.sid
WHERE b.id in (SELECT MAX(id) FROM table_b GROUP BY sid)