SQL UNION but fill in NULL value - sql

I have two tables and a section of Table1 looks something like this
EmployeeID
FirstName
LastName
Gender
Age
A100
Bob
Odenkirk
Male
30
A101
Jon
Jones
NULL
36
Table2 looks similar but contains a duplicate entry with some varying amount of missing/available information, i.e.,
EmployeeID
FirstName
LastName
Gender
Age
A101
Jon
Jones
Male
NULL
A103
Angelina
Jolie
Female
40
I'm fairly new to SQL and I initially tried
SELECT *
FROM Table1
UNION
SELECT *
FROM Table2
But obviously, the A101 row has different NULL values so it doesn't get treated as duplicates and I get:
EmployeeID
FirstName
LastName
Gender
Age
A100
Bob
Odenkirk
Male
30
A101
Jon
Jones
NULL
36
A101
Jon
Jones
Male
NULL
A103
Angelina
Jolie
Female
40
Is there a general way (i.e. if the table is large and not sure which values might be missing from Table1 or Table2) to somehow "fill in" the NULL values and get the following target output:
EmployeeID
FirstName
LastName
Gender
Age
A100
Bob
Odenkirk
Male
36
A101
Jon
Jones
Male
30
A103
Angelina
Jolie
Female
40

Assuming you can never have "competing" values (i.e., both tables either have the same value or one of them has a null), you could union all the two queries, and then group by the EmployeeID and use max to get the present values and ignore nulls:
SELECT EmployeeID, MAX(FirstName), MAX(LastName), MAX(Gender), MAX(Age)
FROM (SELECT EmployeeID, FirstName, LastName, Gender, Age
FROM Table1
UNION ALL
SELECT EmployeeID, FirstName, LastName, Gender, Age
FROM Table2) T
GROUP BY EmployeeID

You could try to use FULL OUTER JOIN and COALESCE function
SELECT COALESCE(t1.EmployeeID, t2.EmployeeID),
COALESCE(t1.FirstName, t2.FirstName) AS FirstName,
COALESCE(t1.LastName, t2.LastName) AS LastName,
COALESCE(t1.Gender, t2.Gender) AS Gender,
COALESCE(t1.Age, t2.Age) AS Age,
FROM Table1 t1 FULL OUTER JOIN Table2 t2 ON t1.EmployeeID = t2.EmployeeID
If your DBMS does not support FULL OUTER JOIN, you could try to use UNION and COALESE function.
-- Retrieve all employees in Table1, then fill in NULL value using COALESCE function if it's exists in Table2
SELECT t1.EmployeeID,
COALESCE(t1.FirstName, t2.FirstName) AS FirstName,
COALESCE(t1.LastName, t2.LastName) AS LastName,
COALESCE(t1.Gender, t2.Gender) AS Gender,
COALESCE(t1.Age, t2.Age) AS Age,
FROM Table1 t1 LEFT JOIN Table2 t2 ON t1.EmployeeID = t2.EmployeeID
UNION ALL
-- Retrieve the rest employees, which is existed in Table 2, but is not existed in Table1
SELECT *
FROM Table2 t2
WHERE NOT EXISTS
(SELECT 1
FROM Table1 t1
WHERE t1.EmployeeID = t2.EmployeeID)

Related

Getting records from 2 tables with common an uncommon columns

Below is similar example of the issue I have:
if I have this table 1:
Patient ID
Name
Check in Date
order name
preformed by
1
Jack
12/sep/2002
xray
Dr.Amal
2
Nora
15/oct/2002
ultrasound
Dr.Goerge
1
Jack
13/nov/2003
Medicine
Dr.Fred
table 2:
Patient ID
Name
Check in Date
order name
1
Jack
14/Jun/2002
xray 2
2
Nora
15/oct/2002
ultrasound
1
Jack
13/nov/2003
Medicine
3
Rafael
13/nov/2003
Vaccine
The result I need is as the following:
Name
Check in Date
order name
preformed by
Jack
12/sep/2002
xray
Dr.Amal
Nora
15/oct/2002
ultrasound
Dr.Goerge
Jack
13/nov/2003
Medicine
Dr.Fred
Jack
14/Jun/2002
xray 2
Null
Rafael
13/nov/2003
Vaccine
Null
If you noticed the result I need is all records of table 1 and all records of table 2 with no duplication and joining the same common fields and adding 'Preformed by' column from Table 1. I tried using 'UNION' as the following:
SELECT Name, Check_in_Date, order_name,preformed_by
FROM table1
UNION
SELECT Name, Check_in_Date, order_name,''
FROM table2
the result I get is 2 records for each patient with the same date one with preformed by one with null as the following:
Name
Check in Date
order name
preformed by
Jack
12/sep/2002
xray
Dr.Amal
Nora
15/oct/2002
ultrasound
Dr.Goerge
Nora
15/oct/2002
ultrasound
Null
Jack
13/nov/2003
Medicine
Dr.Fred
Jack
13/nov/2003
Medicine
null
Jack
14/Jun/2002
xray 2
Null
Rafael
13/nov/2003
Vaccine
Null
If the same ID has same check in date in both table it must return the preformed by of table 1 not null How can I do this?
Thank you.
What you need is a FULL JOIN matching by those three columns along with NVL() function in order to bring the values
from table2 which return null from table1 such as
SELECT NVL(t1.name,t2.name) AS name,
NVL(t1.check_in_date,t2.check_in_date) AS check_in_date,
NVL(t1.order_name,t2.order_name) AS order_name,
t1.preformed_by
FROM table1 t1
FULL JOIN table2 t2
ON t1.name = t2.name
AND t1.check_in_date = t2.check_in_date
AND t1.order_name = t2.order_name
or another method uses UNION to filter out duplicates and then applies an OUTER JOIN such as
SELECT tt.name, tt.check_in_date, tt.order_name, t1.preformed_by
FROM (
SELECT name, check_in_date, order_name FROM table1 UNION
SELECT name, check_in_date, order_name FROM table2
) tt
LEFT JOIN table1 t1
ON t1.name = tt.name
AND t1.check_in_date = tt.check_in_date
AND t1.order_name = tt.order_name
Demo

Changing record values based on whether there are duplicates when two tables are combined

I know I can join Table #1 and Table #2 with a UNION and then filter out duplicate Id's using DISTINCT. However, for the duplicate contacts I'd like to change DrinkPreference to Coke/Pepsi.
Is this possible?
Starting Table #1
Id
FirstName
LastName
DrinkPreference
123
Tom
Bannon
Pepsi
124
Sarah
Smith
Pepsi
Starting Table #2
id
FirstName
LastName
DrinkPreference
125
Jim
Henry
Coke
123
Tom
Bannon
Coke
Table? #3 - combined with DrinkPreference set to Coke/Pepsi where contact exists in both tables?
Id
FirstName
LastName
DrinkPreference
125
Jim
Henry
Coke
123
Tom
Bannon
Coke/Pepsi
124
Sarah
Smith
Pepsi
You can try this one
SELECT coalesce(t1.firstname, t2.firstname) AS firstname,coalesce(t1.lastname,t2.lastname) AS lastname, CASE WHEN t1.drinkpreferences IS NULL THEN t2.drinkpreferences WHEN t2.drinkpreferences IS NULL THEN t1.drinkpreferences
ELSE t1.drinkpreferences || '/' || t2.drinkpreferences END AS drinkpreferences FROM table1 t1 FULL JOIN table2 t2 ON t1.id = t2.id
Achievable using multiple unions and joins.
select distinct FirstName, LastName, case when ct = 2 then 'Coke/Pepsi' else DrinkPreference end
from (
select FirstName, LastName, DrinkPreference, Id from table1
union all
select FirstName, LastName, DrinkPreference, Id from table2) a
left join
(
select count(1)ct, Id from
(select Id from table1
union all
select Id from table2) t1
group by Id
) b on b.Id = a.Id

Display Value From Another Row

I have to write a query to display the value for ID2 when ID=ID. Currently the table looks like the below.
ID
ID2
fname
lname
address1
address2
city
state
123
123
joe
smith
12 main st
los angeles
CA
122
122
james
jones
13 main st
new york
NY
123
3210
joe
smith
14 main st
los angeles
CA
124
124
mary
jones
15 main st
new york
NY
The desired output would look like this. Where I can do some sort of a self join to get the ID2 value.
ID
ID2
fname
lname
address1
address2
city
state
other ID
123
123
joe
smith
12 main st
los angeles
CA
3210
122
122
james
jones
13 main st
new york
NY
124
124
mary
jones
15 main st
new york
NY
Any ideas/suggestions greatly appreciated!
Update:
I was able to get the result with the additional columns using the below script. Thanks all for your assistance on this one!
with cte (id, id2, fname, lname, address1, address2, city, state)
as (select *
from (select *,row_number() over(partition by id order by id desc) as rn from your_table
) x
where rn = 2)
select x.id, x.id2, x.fname, x.lname, x.address1, x.address2, x.city, x.state, c.id2, c.address1
from (select *,row_number() over(partition by id order by id desc) as rn from your_table
) x
left join
cte c on x.id=c.id
where x.rn = 1
dbfiddle below
https://dbfiddle.uk/?rdbms=postgres_9.5&fiddle=4010ab08c5e32d9293d10e985adbfd7a
You could do something like the following, using a correlated subquery; using max ensures it only returns a single row should you have an Id with more than one different ID2:
select *,
(select max(id2) from t t2 where t2.id = t.id and t2.id2 != t.id) OtherId
from t
where id = id2
If you have two rows max with the same id, then you can try this :
SELECT (array_agg(t.*) FILTER (WHERE id = id2))[1].*
, (array_agg(t.id2) FILTER (WHERE id <> id2))[1] AS "other ID"
FROM your_table AS t
GROUP BY id
If you may have more than two rows with the same id then you can try this :
SELECT (array_agg(t.*) FILTER (WHERE id = id2))[1].*
, array_agg(t.id2) FILTER (WHERE id <> id2) AS "other IDs"
FROM your_table AS t
GROUP BY id
see the test result in dbfiddle

Best way to get distinct rows from a database

I have two tables tbPerson and tbComment with data such as below:
tbPerson
PersonID FirstName LastName
1 William Tell
2 Joe Smith
3 Sam Hampton
tbComment
CommentID PersonID CommentValue CommentPosition
45 2 This is my comment 100
46 2 This is my second comment 100
47 2 This is my third comment 100
48 1 A comment 101
49 3 This comment rules 102
50 3 A comment here 102
I need a query that only returns:
William Tell 101
Joe Smith 100
Sam Hampton 102
I figured it was something like below but this returns multiple rows. I only want the three rows.
SELECT FirstName, LastName, CommentPosition
FROM tbPerson
JOIN tbComment ON tbPerson.PersonID = tbComment.PersonID
GROUP BY FirstName, LastName, CommentPosition
For your data, your query should return only three rows. If you have multiple comments for a person, You need to choose which comment position you want.
The following would return the minimal value:
SELECT FirstName, LastName, MIN(CommentPosition) as CommentPosition
FROM tbPerson JOIN
tbComment
ON tbPerson.PersonID = tbComment.PersonID
GROUP BY FirstName, LastName;
Notice that CommentPosition was removed from the GROUP BY clause.
If comment position varies, try this:
SELECT FirstName, LastName, CommentPosition
FROM tbPerson
INNER JOIN (
SELECT PersonID, MIN(CommentPosition) AS CommentPosition
FROM tbComment
GROUP BY PersonID
) T1 ON tbPerson.PersonID = T1.PersonID
This will also handle the case of multiple people with the same name.
You can use SELECT DISTINCT FirstName, LastName, ...to get exactly what you want. Here's a tutorial on it.
http://www.mysqltutorial.org/mysql-distinct.aspx

select rows from a database table with common field in sqlite

i have a table as follows:
create table table1(id integer,firstname text,lastname text);
firstname lastname======== =========
1 ben taylor
2 rob taylor
3 rob smith
4 rob zombie
5 peter smith
6 ben smith
7 peter taylor
I want to select rows with a lastname , where the lastname must be shared by ben and rob and firstnames must be ben and rob.
Hence in the above table the result of the select query must be:
1 ben taylor
2 rob taylor
3 rob smith
6 ben smith
what must be the sql query to get the above results?
I tried - select * from table1 as a,table1 as b where a.firstname='ben' and b.firstname='rob' and a.lastname=b.lastname this joined all the resultant rows which is not what i inteneded.
You can use two filtering joins to demand that the lastname is shared with both a Ben and a Rob:
select *
from Table1 t1
join Table1 rob
on rob.firstname = 'rob'
and t1.lastname = rob.lastname
join Table1 ben
on ben.firstname = 'ben'
and t1.lastname = ben.lastname
where t1.firstname in ('ben', 'rob')
Live example at SQL Fiddle.
select *
from table1 where first_name = 'ben' or first_name = 'rob'
and last_name
in (select last_name from table1 where first_name = 'rob' and last_name
in (select last_name from table1 where first_name = 'ben'))