Left Join that always includes null records - sql

I'm using Oracle 11gR2 and I am trying to write a query that returns address data from two tables, CUSTOMERS and LOCATIONS. A given customer may (or may not) have different locations, each with their own address.
I would like to return the address for every customer, and all their locations. For example, if the tables contained data like:
CUSTOMERS
CUSTOMER_ID ADDRESS
1 "New York"
2 "California"
LOCATIONS
CUSTOMER_ID LOCATION_ID ADDRESS
1 1 "New Jersey"
Then I want the results to look like:
CUSTOMER_ID LOCATION_ID ADDRESS
1 "New York"
1 1 "New Jersey"
2 "California"
My first thought was something like this:
SELECT
CUSTOMERS.CUSTOMER_ID,
LOCATIONS.LOCATION_ID,
NVL(LOCATIONS.ADDRESS,CUSTOMERS.ADDRESS) ADDRESS
FROM
CUSTOMERS
LEFT JOIN
LOCATIONS ON (CUSTOMERS.CUSTOMER_ID=LOCATIONS.CUSTOMER_ID)
The problem with that is that when a customer does have locations, it does not return a row with null values for location data, so I don't get a row with the address in the CUSTOMERS table. It gives me something like this:
CUSTOMER_ID LOCATION_ID ADDRESS
1 1 "New Jersey"
2 "California"
It's missing the New York address for customer 1. I tried this...
SELECT
CUSTOMERS.CUSTOMER_ID,
LOCATIONS.LOCATION_ID,
NVL(LOCATIONS.ADDRESS,CUSTOMERS.ADDRESS) ADDRESS
FROM
CUSTOMERS
LEFT JOIN
LOCATIONS ON (CUSTOMERS.CUSTOMER_ID=LOCATIONS.CUSTOMER_ID OR LOCATIONS.CUSTOMER_ID IS NULL)
But it gave me the same results as the first query. Is there a way to return a null record for the second table even when there is a match on the join condition?

You don't need a join here at all:
SELECT customer_id, NULL AS location_id, address
FROM customers
UNION ALL
SELECT customer_id, location_id, address
FROM locations

You can try a full outer join. For example:
SELECT
CUSTOMERS.CUSTOMER_ID,
LOCATIONS.LOCATION_ID,
NVL(LOCATIONS.ADDRESS,CUSTOMERS.ADDRESS) ADDRESS
FROM CUSTOMERS
FULL OUTER JOIN LOCATIONS ON (CUSTOMERS.CUSTOMER_ID=LOCATIONS.CUSTOMER_ID)

If you want to join the two tables even when there is a non match, you will need to use IS NULL on your joined columns.
For example.
Table 1:
CustomerID
CustomerName
.
Table 2:
CustomerID
CustomerEmail
.
Select,
CustomerID,
CustomerName,
ISNULL (CustomerEmail, NULL) AS CustomerEmail
FROM table1
LEFT JOIN table2
ON table1.CustomerID = table2.CustomerID
This wil bring back results with NULL

Related

Create a additional row from join sql?

I have 2 tables: Person and House with 1-n relation.
I want to the result return as picture below:
Row always have a Person column with a null House column.
Thanks.
you can use unionall to join the result set with person table something like
select p.name,h.name as housename from person p join house h on p.id=h.personid
union all (select name,null from person)
order by name,housename

Two group by tables stich another table

I have 3 tables I need to put together.
The first table is my main transaction table where I need to get distinct transaction id numbers and company id. It has all the important keys. The transaction ids are not unique.
The second table has item info which is linked to transaction id numbers which are not unique and I need to pull items.
The third table has company info which has company id.
Now I've sold some of these with the first one through a group by id. The second through a subquery which creates unique ids and joins onto the first one.
The issue I'm having is the third one by company. I cannot seem to create a query that works in the above combinations. Any ideas?
As suggested here is my code. It works but that's because for the company I used count which doesn't give the correct number. How else can I get the company number to come out correct?
SELECT
dep.ItemIDAPK,
dep.TotalOne,
dep.company,
company.vendname,
appd.ItemIDAPK,
appd.ItemName
FROM (
SELECT
csi.ItemIDAPK,
sum(f.TotalOne) as TotalOne,
count(f.DimCurrentcompanyID) company
FROM dbo.ReportOne F with (nolock)
INNER JOIN dbo.DSaleItem csi with (nolock)
on f.DSaleItemID = csi.DSaleItemID
INNER JOIN dbo.DimCurrentcompany cv
ON f.DimCurrentcompanyID = cv.DimCurrentcompanyID
INNER JOIN dbo.DimDate dat
on f.DimDateID = dat.DimDateID
where (
dat.date >='2013-01-29 00:00:00.000'
and dat.date <= '2013-01-30 00:00:00.000'
)
GROUP BY csi.ItemIDAPK
) as dep
INNER JOIN (
SELECT
vend.DimCurrentcompanyID,
vend.Name vendname
FROM dbo.DimCurrentcompany vend
) As company
on dep.company = company.DimCurrentcompanyID
INNER JOIN (
SELECT
c2.ItemIDAPK,
ItemName
FROM (
SELECT DISTINCT ItemIDAPK
FROM dbo.dimitem AS C
) AS c1
JOIN dbo.dimitem AS c2 ON c1.ItemIDAPK = c2.ItemIDAPK
) as appd
ON dep.ItemIDAPK = appd.ItemIDAPK
For further information my output is the following example, I know the code executes and the companyid is incorrect as I just put it with a (count) in their to make the above code execute:
Current Results:
Item Number TLS CompanyID Company Name Item Number Item Name
111111 300 303 Johnson Corp 29323 Soap
Proposed Results:
Item Number TLS CompanyID Company Name Item Number Item Name
111111 300 29 Johnson Corp 29323 Soap

How to write a SQL query for the below scenario

Sales
order id : 1(primary key)
Billing address id -250
Shipping address id -285
Address table has the below entries
id :250
Addressline1 : XXX
Addressline2 :YYY
id :285
Addressline1 : AAA
Addressline2 :BBB
How to write a query to retrieve the order id, billing address, shipping address in a single query ?
Use the following query with join. The below is a sample code.
select order.orderid,
ad1.Addressline1,
ad1.Addressline2,
ad2.Addressline1,
ad2.Addresslinne2
from order
join address ad1 on ad1.id=order.billingaddressid
join address ad2 on ad2.id=order.shippingaddressid
You can join to the address table multiple times (this uses an outer join -- depends on potential null values):
select s.id,
billing.Addressline1,
shipping.Addressline1
from sales s
left join address billing on s.billingaddressid = billing.id
left join address shipping on s.shippingaddressid = shipping.id
A Visual Explanation of SQL Joins

Select unrelated columns from two unrelated tables

Is there an easy way to select two columns from two unrelated tables, in order to use those columns in an insert?
Disclaimer: Ideally, I would never need to do this because the schema would have been set up with a little something called "foreign keys" and "referential integrity". But, it seems as though neither of these concepts existed on the planet on which this schema was created.
Here's a simplified version of what I need to do:
Customer Table
Id Name
------------
1 Eddie
2 Stone
3 Mike
Product Table
Id Name
---------------------
100 Drum sticks
101 Guitar strings
102 Amplifier
Invoice Table
Id CustomerName ProductName
---------------------------------------
1000 Eddie Guitar Strings
1001 Mike Amplifier
In my SQL code, I've got a :CustomerId and a :ProductId, and ideally I'd like to do something like this:
INSERT Invoice (
Id,
CustomerName,
ProductName
)
SELECT
:InvoiceId,
Customer.Name,
Product.Name
FROM
Customer,
Product
WHERE
Customer.CustomerId = :CustomerId
and Product.ProductId = :ProductId
This works fine and dandy if both the Customer and Product records exist, but I need to also cater for the scenario where one of them doesn't exist. (Yes, really.)
The only way I can think to do it is by declaring variables like :CustomerName and :ProductName and pre-populating them outside of the insert statement.
Is there a way to achieve this without going down the extra variables approach?
You could do this:
INSERT Invoice (
Id,
CustomerName,
ProductName
)
SELECT
:InvoiceId,
(
SELECT
Customer.Name
FROM
Customer
WHERE
Customer.CustomerId = :CustomerId
),
(
SELECT
Product.Name
FROM
Product
WHERE
Product.ProductId = :ProductId
)
FROM RDB$DATABASE
Next to the answer provided by Arion, you could use a FULL OUTER JOIN with a join condition that is always true. This only works correctly if both subqueries produce a single row.
SELECT
:InvoiceId,
CustomerName,
ProductName
FROM (
SELECT CustomerName
FROM Customer
WHERE CustomerId = :CustomerId
) a
FULL OUTER JOIN (
SELECT ProductName
FROM Product
WHERE Product.ProductId = :ProductId
) b
ON 1 = 1

SQL join on multiple Rows to Single row

i am struggling to do a join in the way that I need, I have two tables that house data I need to link(just to be clear). One table holds Outbound Call data, number dialed, duration ect. the other table holds contact details of the people dialed. Set out as below:
CustID | Number 1 | Number 2 | Number 3 | Number 4
1 | 072454584 | | 017726593 |
2 | |0125456852| | 0125785448
So if we wanted to call Customer 1 we would try both numbers, with only one connecting.
What i need to do is join the number dialed to the customer records but so that it compares each number till it matches match (hoping this makes sense). I've tried a case when Statement but it didn't work. Whats the best approach in doing this?!?!
I would probably take this approach to the query.
with myphones
AS
(
SELECT CustomerId, Phone1 As Phone FROM ContactDetails
UNION
SELECT CustomerId, Phone2 As Phone FROM ContactDetails
UNION
SELECT CustomerId, Phone3 As Phone FROM ContactDetails
UNION
SELECT CustomerId, Phone4 As Phone FROM ContactDetails
)
SELECT p.CustomerId, p.Phone, oc.*
FROM myphones p
INNER JOIN outboundcalls oc ON p.Phone = oc.Phone
You want to use a series of left outer join's with a conditional statement on the match:
select cd.CustId,
coalesce(oc1.number, oc2.number, oc3.number, oc4.number) as MatchingNumber,
(case when oc1.Number is not null then 'Number1'
when oc2.Number is not null then 'Number2'
when oc3.Number is not null then 'Number3'
when oc4.Number is not null then 'Number4'
end) as WhichMatch
from ContactDetails cd left outer join
OutboundCalls oc1
on cd.number1 = oc1.number left outer join
OutboundCalls oc2
on cd.number2 = oc2.number left outer join
OutboundCalls oc3
on cd.number3 = oc3.number left outer join
OutboundCalls oc4
on cd.number4 = oc4.number;
The left outer join attempts to match to each number in the list. The coalesce() will choose the first matching number, and the case tells you which number matches.
Note that if you have multiple successful outbound calls for a given customer, you will get multiple rows in the output.