union on certain columns created from join? - mysql

The trouble I've been having with union is that the tables your querying must have all the same columns. Is there a way to query using an join with a union on ONLY certain coloumns?
SELECT Client.Client_ID, Client.First_Name, Client.Last_Name, Client.Company_Name from Client
union
select Agency_Employee.Employee_ID, Agency_Employee.First_Name, Agency_Employee.Last_Name, ''
from Agency_Employee
union
select Agency_ID, '', '', Agency_Name from Admin_Agency
SELECT proj.ProjectID, A.Project_Title, B.Account_ID, B.Username, B.Access_Type FROM Project_Assigned proj
JOIN Account B
ON proj.AccountID = B.Account_ID
JOIN Project A
ON proj.ProjectID = A.Project_ID
WHERE proj.ProjectID = 1;
In the above above I'm trying to combine a join query created which matches accounts assigned to project 1 and with union I'm trying to get additional information such as first_name, last_name from those account numbers from different tables such as client, agency_employee.

Yes it is.
You can do it by specifying the names of the columns- they should be the same.
In your case you can do it like that:
SELECT Client.Client_ID, Client.First_Name, Client.Last_Name, Client.Company_Name from Client
union
select Agency_Employee.Employee_ID as Client_ID, Agency_Employee.First_Name, Agency_Employee.Last_Name, '' as Company_Name
from Agency_Employee
union
select Agency_ID as Client_ID, '' as First_Name, '' as Last_Name, Agency_Name as Company_Name from Admin_Agency
I don't see in your code how exactly you want to merge your union with the following queries but you can do it with sub-queries. Here is the example:
select a.item1, b.count from table_a a
inner join (
select item1, count(*) as count
from table_b
group by item1
) b on b.item1 = a.item1

Related

union query only youngest result

I have two query that are the same except for the two different tables Transit_now & Transit_old, both have the same layout. Transit_old is an archive of transit_now. Both querys will only display the youngest value
I only want the youngest value to display when i use a union between the two querys. Now i have sometines two values from transit_now and tansit_old
Newest result can be in transit_old when there is no result for transit_now. There are multiple results on both tables, but that is coverd with the where statement in both querys
(
SELECT A.Batch, A.Date, B.name, B.Surname,
FROM Transit_now
INNER JOIN DB.USERS B
ON A.Transit_now.ID = B.Users.ID
where A.DATE = (select MAX(DATE) from Transit_now where (A.Transit_now.ID = B.Users.ID))
UNION
SELECT A.Batch, A.Date, B.name, B.Surname,
FROM Transit_old
INNER JOIN DB.USERS B
ON A.Transit_old.ID = B.Users.ID
where A.DATE = (select MAX(DATE) from Transit_now where (A.Transit_old.ID = B.Users.ID))
)

How can I join 3 tables in MySQL?

I have these 4 tables:
Suppliers(id_sup, name, city)
Companies( id_co, name, city)
Products(id_p, name, city)
Deliveries (id_sup, id_co, id_p)
in one query, I need to get a list of all cities (no duplicates) and for each, show the number of suppliers, companies, and products that can be found in that city.
In oracle SQL I would have used some Full OUTER JOIN. what's the alternative?
This is a suggested solution for joining 2 of the tables:
SELECT city
, COUNT(DISTINCT id_sup) Suppliers
, COUNT(DISTINCT id_co) Companies
FROM ( SELECT COALESCE(s.city,c.city) city
, id_sup
, id_co
FROM Suppliers AS s
LEFT OUTER JOIN Companies AS c ON c.city = s.city
UNION
SELECT COALESCE(s.city,c.city) city
, id_sup
, id_co
FROM Suppliers AS s
RIGHT OUTER JOIN Companies AS c ON c.city = s.city ) AS union_query
GROUP BY city
How to add the final table Products to the mix?
With UNION get all the cities from all 3 tables and LEFT join to the results the 3 tables to finally aggregate:
select t.city,
count(distinct s.id_sup) counter_suppliers,
count(distinct c.id_co) counter_companies,
count(distinct p.id_p) counter_products
from (
select city from suppliers union
select city from companies union
select city from products
) t
left join suppliers s on s.city = t.city
left join companies c on c.city = t.city
left join products p on p.city = t.city
group by t.city
See a simplified demo.
Just use union all and group by:
select city, sum(is_supplier), sum(is_company),
sum(is_product), sum(is_delivery)
from ((select city, 1 as is_suppler, 0 as is_company, 0 as is_product, 0 as is_delivery
from suppliers
) union all
(select city, 0 as is_suppler, 1 as is_company, 0 as is_product, 0 as is_delivery
from companies
) union all
(select city, 0 as is_suppler, 0 as is_company, 1 as is_product, 0 as is_delivery
from products
) union all
(select city, 0 as is_suppler, 0 as is_company, 0 as is_product, 1 as is_delivery
from deliveries
)
) c
group by city;
Or, even more simply in MySQL:
select city, sum(which = 'supplier'), sum(which = 'company'),
sum(which = 'product'), sum(which = 'delivery')
from ((select city, 'suppler' as which from suppliers
) union all
(select city, 'company' as which from companies
) union all
(select city, 'product' as which from products
) union all
(select city, 'delivery' as which from deliveries
)
) c
group by city;
to get all cities, since we don't have a city table as a dimension, we can get the domain from each of the city columns from each of the three tables, and combine them with UNION set operator:
SELECT cs.city
FROM suppliers cs
GROUP BY cs.city
UNION
SELECT cc.city
FROM companies cc
GROUP BY cc.city
UNION
SELECT cp.city
FROM products cp
GROUP BY cp.city
That should get us a list of distinct city values that appear in the three tables.
We could take that set, and do outer join operations to the individual tables. But that has the potential to generate cross product... if there are three suppliers related to a city and four companies related to the same city, we would generate a resultset of twelve rows.
To fix that, we could get a count of DISTINCT primary key values.
Or, we can pre-aggregate the results in inline views, returning a single row per city. That avoids the problem of semi-cartesian products.
Let's reference the query above in an line view in another query. We will alias it ci. (If we had a dimension table city, we could reference that,)
Something like this:
SELECT ci.city
, IFNULL(np.cnt_,0) AS cnt_products
, IFNULL(nc.cnt_,0) AS cnt_companies
, IFNULL(ns.cnt_,0) AS cnt_suppliers
FROM ( /* inline view query */ ) ci
LEFT
JOIN ( SELECT p.city
, COUNT(1) AS cnt_
FROM products p
GROUP BY p.city
) np
ON np.city = ci.city
LEFT
JOIN ( SELECT c.city
, COUNT(1) AS cnt_
FROM companies c
GROUP BY c.city
) nc
ON nc.city = ci.city
LEFT
JOIN ( SELECT s.city
, COUNT(1) AS cnt_
FROM suppliers s
GROUP BY s.city
) ns
ON ns.city = ci.city
ORDER BY ci.city
(In place of /* inline view query */, use the SQL text from the first query, to generate the distinct list of city.)

Get distinct records from 2 union sql statement

I would like to seek the advice of some sql experts.
I wrote sql statement for getting authors names from staff and alumni table. Some author's name will be in both tables. So the logic is if the author name is in staff, use that otherwise look for alumni table.
Here is my sql statement, seems fine but it is showing same author name from both staff and alumni table.
SELECT DISTINCT AP.Author_name, P.people_id, P.Name, P.Journal_name, AP.Author_sortorder FROM `Paper_Author` AS AP LEFT JOIN `People` AS P ON ( AP.Author_id = P.people_id ) WHERE AP.Paper_id =3838
UNION
SELECT DISTINCT AN.Author_name, N.People_id, N.Name, N.Journal_name, AN.Author_sortorder FROM `Paper_Author` AS AN LEFT JOIN `Alumni` AS N ON ( AN.Author_id = N.People_id ) WHERE AN.Paper_id =3838 ORDER BY Author_sortorder LIMIT 0 , 30
Result:
people_id-- Author_name-- Journal_name--
1 Name1 A1
2 Name2 B1
3 Name3 C1
3 Name3 C1
4 Name4 D
4 Name4
Expected Result :
people_id-- Author_name-- Journal_name--
1 Name1 A1
2 Name2 B1
3 Name3 C1
4 Name4 D
This can probably be solved by an additional select using the original result as a subquery
SELECT DISTINCT * FROM (
SELECT DISTINCT AP.Author_name, P.people_id, P.Name, P.Journal_name, AP.Author_sortorder FROM `Paper_Author` AS AP LEFT JOIN `People` AS P ON ( AP.Author_id = P.people_id ) WHERE AP.Paper_id =3838
UNION
SELECT DISTINCT AN.Author_name, N.People_id, N.Name, N.Journal_name, AN.Author_sortorder FROM `Paper_Author` AS AN LEFT JOIN `Alumni` AS N ON ( AN.Author_id = N.People_id ) WHERE AN.Paper_id =3838 ORDER BY Author_sortorder LIMIT 0 , 30
);
The difficulty with this problem is that you might need information from either the People or Alumni tables. We would like to just join to a single table containing the right information. Much of the complexity of the below query is in creating a table which contains the right metadata for each person.
SELECT
pa.Author_name,
pa.Author_sortorder,
t1.people_id,
t1.Name,
t1.Journal_name
FROM Paper_Author pa
LEFT JOIN
(
SELECT people_id, Name, Journal_name, 0 AS source
FROM People
UNION ALL
SELECT people_id, Name, Journal_name, 1
FROM Alumni
) t1
ON pa.Author_id = t1.people_id
INNER JOIN
(
SELECT people_id, MIN(source) AS source
FROM
(
SELECT people_id, 0 AS source
FROM People
UNION ALL
SELECT people_id, 1
FROM Alumni
) t
GROUP BY people_id
) t2
ON t1.people_id = t2.people_id AND
t1.source = t2.source
WHERE
pa.Paper_id = 3838;

Mysql to return the table name the id exists in

I have a design like this
accounts(id, username, email, password, ..)
admin_accounts(account_id, ...)
user_accounts(account_id, ....)
premium_accounts(account_id, ....)
id is the primary key in accounts
account_id is a foreign(references id on accounts table) and primary key in these three tables(admin, user, premium)
Knowing the id how can I find which type this user is with only one query? Also knowing that an id can only exists in one of the three tables(admin, user, premium)
Using case:
select
a.id,
case
when aa.account_id is not null then 'admin_accounts'
when ua.account_id is not null then 'user_accounts'
when pa.account_id is not null then 'premium_accounts'
else
'No detail found'
end as found_in
from
accounts a
left join admin_accounts aa on aa.account_id = a.id
left join user_accounts ua on ua.account_id = a.id
left join premium_accounts pa on pa.account_id = a.id
/*where -- In case you want to filter.
a.id = ....*/
using union
select
id,
found_in
from
(select account_id as id, 'admin_accounts' as found_in
from admin_accounts aa
union all
select account_id, 'user_accounts'
from user_accounts ua
union all
select account_id, 'premium_accounts'
from premium_accounts pa) a
/*where -- In case you want to filter.
a.account_id = ....*/
If you use UNION you can try applying a solution proposed here - mysql return table name
You can use union query like
(select id, 'admin' as user_type from account inner join admin_accounts) UNION
(select id, 'user' as user_type from account inner join user_accounts) UNION
(select id, 'premium' as user_type from account inner join premium_accounts);

Using IFNULL to set NULLs to zero

I have a table in which a field contains an integer or NULL.
parent_id
2
4
6
NULL
NULL
45
2
How would I go about adding an IFNULL statement so that ans_count will be populated with 0 instead of NULL?
Here is my SQL code:
...
(SELECT parent_id AS pid, COUNT(*) AS ans_count
FROM qa
GROUP BY parent_id) AS n
UPDATE
Full SQL below - thanks to all for your patience.
SELECT *
FROM qa
JOIN user_profiles
ON user_id = author_id
LEFT JOIN (SELECT cm_id,
cm_author_id,
id_fk,
cm_text,
cm_timestamp,
first_name AS cm_first_name,
last_name AS cm_last_name,
facebook_id AS cm_fb_id,
picture AS cm_picture
FROM cm
JOIN user_profiles
ON user_id = cm_author_id) AS c
ON id = c.id_fk
LEFT JOIN (SELECT parent_id AS pid, COUNT(*) AS ans_count
FROM qa
GROUP BY parent_id) AS n
ON id = n.pid
WHERE id LIKE '%'
ORDER BY id DESC
EDIT: NEW INFO BASED ON FULL QUERY
The reason the counts can be null in the query you specify is because a left join will return nulls on unmatched records. So the subquery itself is not returning null counts (hence all the responses and confusion). You need to specify the IFNULL in the outer-most select, as follows:
SELECT qa.*, user_profiles.*, c.*, n.pid, ifnull(n.ans_count, 0) as ans_count
FROM qa
JOIN user_profiles
ON user_id = author_id
LEFT JOIN (SELECT cm_id,
cm_author_id,
id_fk,
cm_text,
cm_timestamp,
first_name AS cm_first_name,
last_name AS cm_last_name,
facebook_id AS cm_fb_id,
picture AS cm_picture
FROM cm
JOIN user_profiles
ON user_id = cm_author_id) AS c
ON id = c.id_fk
LEFT JOIN (SELECT parent_id AS pid, COUNT(*) AS ans_count
FROM qa
GROUP BY parent_id) AS n
ON id = n.pid
WHERE id LIKE '%'
ORDER BY id DESC
OLD RESPONSE
Can you explain in more detail what you are seeing and what you expect to see? Count can't return NULLs.
Run this set of queries and you'll see that the counts are always 2. You can change the way the NULL parent_ids are displayed (as NULL or 0), but the count itself will always return.
create temporary table if not exists SO_Test(
parent_id int null);
insert into SO_Test(parent_id)
select 2 union all select 4 union all select 6 union all select null union all select null union all select 45 union all select 2;
SELECT IFNULL(parent_id, 0) AS pid, COUNT(*) AS ans_count
FROM SO_Test
GROUP BY IFNULL(parent_id, 0);
SELECT parent_id AS pid, COUNT(*) AS ans_count
FROM SO_Test
GROUP BY parent_id;
drop table SO_Test;
I didn't test this, but I think it will work
(SELECT IF( parent_id IS NULL, 0, parent_id) AS pid, COUNT(*) AS ans_count
FROM qa
GROUP BY parent_id) AS n
Simply wrap it around your statement:
IFNULL(
(SELECT parent_id AS pid, COUNT(*) AS ans_count
FROM qa
GROUP BY parent_id)
, 0
) AS n
Have you tried just counting the parent_id's?
(SELECT parent_id AS pid, COUNT(parent_id) AS ans_count
FROM qa
GROUP BY parent_id)
SELECT IFNULL(parent_id, 0) AS pid, COUNT(IFNULL(parent_id, 0)) AS ans_count
FROM qa
GROUP BY IFNULL(parent_id, 0)
Can you post actual data and full query which exhibits the behavior you are talking about? In my experience, COUNT(*) can never be NULL.
Can Count(*) ever return null?
Does COUNT(*) always return a result?