SQL join two columns if one is blank - sql

I have one mapping table with different geographical resolutions. The user enters data into a table and I'm trying to join the data table to the mapping table with the condition that if you can join on zipcode value then join if zip isnt available then join on city.
Is is possible to do this within a join?

I would you something like this in your where clause:
WHERE a.ZipCode = ISNULL(b.ZipCode, a.ZipCode)
AND a.City = ISNULL(b.City,a.City)

Depending what your data looks like, you can use an OR in your JOIN clause
SELECT *
FROM a
JOIN b
ON a.Zip = b.Zip
OR a.City = b.City
or union two queries together
SELECT *
FROM a
JOIN b
ON a.Zip = b.Zip
UNION
SELECT *
FROM a
JOIN b
ON a.City = b.City
AND a.Zip <> b.Zip

WHERE (a.ZipCode IS NOT NULL AND b.ZipCode IS NOT NULL AND a.ZipCode = b.ZipCode)
OR (a.ZipCode IS NULL OR b.ZipCode IS NULL) AND a.City = b.City
Let's break it down:
(a.ZipCode IS NOT NULL AND b.ZipCode IS NOT NULL AND a.ZipCode = b.ZipCode)
First, we need to make sure that neither zipcode is NULL (unknown)
If we have 2 zipcodes, we can compare them, if not, we want to compare cities:
OR (a.ZipCode IS NULL OR b.ZipCode IS NULL) AND a.City = b.City
Here we make sure that at least one of the zipcodes is NULL (unknown) otherwise we would prefer to compare zipcodes. Then we compare Cities.
You could skip the NULL check in the second part and simplify the where clause to:
WHERE (a.ZipCode IS NOT NULL AND b.ZipCode IS NOT NULL AND a.ZipCode = b.ZipCode)
OR a.City = b.City
But in that case, it will not prioritize matching the ZipCode over the City, for example: If we have a city with ZipCode: 1000 and Name: Test, and another city with ZipCode: 2000, Name: Test.. it will now consider them to be a match.
When using an (INNER) JOIN, there is no difference wheter you specifiy the conditions in the JOIN or the WHERE clause, so these:
SELECT ... FROM a JOIN b ON a.id=b.a_id
SELECT ... FROM a, b WHERE a.id=b.a_id
Will produce the same result.

Related

SQL for 2 tables

I have these 2 tables:
Table A
Country State
-----------------
US OH
US FL
US WA
Table B
State LastVisitDate City
----------------------------------
OH 15/10/2019 Bedford
FL 10/12/2019 Bell
WA 20/09/2019 Perth
Table A can be linked with table B by field "State".
What would be the query to get the latest (last visit date) in country=US, doesn't matter which state, e.g.:
US,10-12-2019,FL,Bell
I tried inner join but couldn't make it work.
Always try to explain your question with sample data and query you tried for.
Here is query with some sample data:
CREATE TABLE A
(
COUNTRY VARCHAR(10),
State VARCHAR(10)
);
INSERT INTO A VALUES('US','OH'),
('US','FL'),
('AU','WA');
CREATE TABLE B
(
State VARCHAR(10),
LastVisitDate DATE,
City VARCHAR(20)
);
INSERT INTO B VALUES
('OH','2019-10-15','Bedford'),
('FL','2019-12-10','Bell'),
('WA','2019-09-20','Perth');
Expected Result:
US,10-12-2019,FL,Bell
Query:
SELECT a.COUNTRY,b.LastVisitDate,b.State,b.City
FROM A
INNER JOIN B ON a.State = b.State
WHERE a.COUNTRY = 'US'
ORDER BY b.LastVisitDate DESC
Limit 1;
SQL Fiddle
Before you try below code you must be reformat last visited date table structure like YYYY/MM/DD in SQL Table and you must replace actual tables name and columns name in below query.
SELECT MAX(table_b.last_visited_date) AS last_visited_date
FROM table_a
INNER JOIN table_b
ON table_a.state = table_b.state WHERE table_a.country = 'US' Limit 1;
Try this below script-
Note: As Tanmay said, you need to reformat your date values to get the correct output.
DEMO HERE
SELECT N.country,
N.mx_date LastVisitDate,
B.City,
B.State
FROM
(
SELECT Country,MAX(LastVisitDate) mx_date
FROM Table_A A
INNER JOIN Table_B B ON A.State = B.State
GROUP BY Country
)N
INNER JOIN Table_A A ON A.Country = N.Country
INNER JOIN Table_B B ON A.State = B.State
WHERE N.mx_date = B.LastVisitDate
SELECT top 1(country,
last_visit_date)
FROM A
JOIN B ON A.State = B.State
WHERE A.Country ="Country Name"
ORDER BY B.Last_visit_Date DESC

Find diff between three tables

I'm trying to find the difference between three tables, a, b, and c. I'd like to join all three, showing nulls where each table does not have records.
Each table has two columns that I would like to join on, lets say first and last name.
So far, this is what I'm doing.
Select * from a
FULL OUTER JOIN b on
a.firstName = b.firstName AND a.lastname = b.lastname
FULL OUTER JOIN c on
(c.firstName = a.firstName and c.lastName = a.LastNAME) OR
(c.firstName = b.firstName AND c.lastname = b.LastName)
I'm not confident that the logic in my output is correct. Is there a better way of doing this? In english, the first join joins A and B, including records in each table that don't exist in the other table. The second join joins A+B to C, joining C to either A or B if it matches.
You may want:
select firstname, lastname, sum(in_a), sum(in_b), sum(in_c)
from ((select firstname, lastname, 1 as in_a, 0 as in_b, 0 as in_c
from a
) union all
(select firstname, lastname, 0 as in_a, 1 as in_b, 0 as in_c
from b
) union all
(select firstname, lastname, 0 as in_a, 0 as in_b, 1 as in_c
from c
)
) abc
group by firstname, lastname;
This should each firstname/lastname pair with information about how many times the pair is in each table.
I prefer the union all/group by approach for this over left join for several reasons:
Without the using clause, the on clause gets a bit tricky as you add more tables.
Minimal use of coalesce().
Better handles duplicates within each table.
Handles NULL values for the keys.

Joining tables based on value

I'm having some hard time doing the join function on those two tables. I have simplified the example dataset as there are additional where-clauses involved for the first table however that doesn't seem to be a problem.
I would write the query for joining the two tables below:
select a.prod_code, a.prod_name, b.ref_value from Product_code a
left join Product_reference b on a.prod_code = b.pref_code
where a.prod_code <> 'CURTAIN' and b.ref_value = 'MAN'
The problem I'm facing is that I want to join tables kind of conditionally. I.e. if the ref_type value is 'MAN' in Product_reference table, I do want to join it, otherwise not.
For an example this query would not include "Chair" in the result as it does not have an ref_type 'MAN' available in the "Product_name". What I'd need though is still show it in the query result, just without joined value from the Product_reference table (given that value with ref_type 'MAN' does not exist for it), not leave it out altogether.
Meanwhile Product_name table record 'CURTAIN' should be left off (regardless if Product_reference ref_type 'MAN' exists or not)
Any recommendations?
Product_code
prod_code prod_name
A Table
B Chair
C Window
D Door
E Curtain
Product_reference
pref_code ref_type ref_value
A MAN x
A AUTO y
B AUTO z
C AUTO z1
C MAN x1
D AUTO zxc
E AUTO abc
E MAN cba
Move b.ref_value = 'MAN' to the join predicate:
SELECT a.prod_code, a.prod_name, b.ref_value
FROM Product_code a
LEFT JOIN Product_reference b ON a.prod_code = b.pref_code AND b.ref_value = 'MAN'
WHERE a.prod_code <> 'CURTAIN'
This will accomplish what you want, which is only left joining the data from table b where b.ref_value = 'MAN', instead of removing all other rows from the result set altogether.
Side note, thanks for including your query and sample data in your very well made question. We appreciate it.
you could use a inner join on the distinct product that have 'MAN'
select
a.prod_code
, a.prod_name
, b.ref_value
from Product_code a
inner join (
select distinct pref_code
from Product_reference
where ref_type = 'MAN') t2 on t2.pref_code = a.prod_code
and a.prod_code <> 'CURTAIN'

SQL JOIN on one field or the other

Trying to order a family by father's name or, if there is no father, then the mother's name where the names are in a separate "person" table, something like:
SELECT DISTINCT family.myid FROM family
JOIN person
ON family.father_id = person.myid OR
family.mother_id = person.myid
ORDER BY person.surname,
person.given_name;
In this version, the families without fathers end up unsorted at the bottom. Would like families without fathers to appear in the order by the mother's name. Sqlite SQL will suffice.
Basically, you need a separate join for the fathers and the mothers:
select f.*
from family f left join
person d
on f.father_id = d.myid left join
person m
on f.mother_id = m.myid
order by (case when d.myid is null then m.surname else d.surname end),
(case when d.myid is null then m.given_name else d.given_name end);
Because a value could be missing, this should be a left join.
COALESCE should work
ORDER BY COALESCE(NULLIF(b.surname, ''), c.surname),
COALESCE(NULLIF(b.given_name, ''), c.given_name)

Oracle SQL - Joining rows that don't exist?

Take these two example tables:
Table A
Name FirstOrLast
Jeremy First
Smith Last
Mark First
David First
Table B
Name
Jeremy
Smith
Mark
David
Jones
Jack
I would like the output to be this: Output only first names, but if Table A doesn't record if it's a first or last name, output it anyway. The correct output would be:
Jeremy
Mark
David
Jones
Jack
I've tried accomplishing this with an outer join, like so:
select
B.Name
from
A, B
where
A.name = B.name(+) and
A.FirstOrLast = 'First';
but no luck. How can I do this?
When you perform a left outer join and there is no record in A, a.firstOrLast will be null. While you're at it, you might as well switch to the standard syntax for joins:
select
B.Name
from
B
left outer join A on A.name = B.name
where
A.firstOrLast is null or A.firstOrLast = 'First';
However, the old syntax should also work:
select
B.Name
from
A, B
where
A.name = B.name(+) and
(A.firstOrLast is null or A.firstOrLast = 'First');
Try this:
SELECT B.Name
FROM B LEFT JOIN A ON B.Name = A.Name
WHERE A.FirstOrLast = 'First' OR A.Name IS NULL;
select b.name
from b
union
select a.namfe
from a
where a.firstOrLast = 'First'
SELECT Name
FROM B
MINUS
SELECT Name
FROM A
WHERE FirstOrLast <> 'First'