Sorry for the confusing title, but I wasn't sure how to define this issue.
The background is:
two tables - a customer table and an account table.
The tables are joined on a Customer ID number.
The Account Table contains a NAICS or SIC code field.
This NAICS (SIC) code is used to select the customers. Only specific SICS are deined in the report specification.
The customer, however, may have additional accounts under OTHER SIC or NAICS codes.
Both the Accounts with the SIC or NAICS code that is used in the WHERE to filter for the customer AND any additional accounts linked to that customer must be selected.
A simplified version of the query is here:
SELECT
dbo.CUSTOMER.customer_id,
dbo.CUSTOMER.customer_full_name,
dbo.ACCOUNT.account_id,
dbo.ACCOUNT.date_first_account_opened,
dbo.ACCOUNT.NAICS_No,
dbo.ACCOUNT.NAICS_description
FROM
dbo.CUSTOMER
LEFT OUTER JOIN dbo.ACCOUNT WITH (nolock)
ON dbo.CUSTOMER.account_id = dbo.ACCOUNT.account_id
WHERE
dbo.account.NAICS)No in ('6011','6062', '6021', '6022', '6035', '6036', '6029', '6081', '522110')-- SIC and NAICS codes
This code will return X number of customers and their associated accounts as selected by the condition in the WHERE clause. What I need to get are any OTHER accounts associated with a customer that are NOT in the WHERE filter list.
Any ideas?
When outer joining don't put criteria on it in the where clause. Your where clause turns your join into an inner join because only found matches have a NAICS_No. Move your where clause to the ON clause.
SELECT
dbo.CUSTOMER.customer_id,
dbo.CUSTOMER.customer_full_name,
dbo.ACCOUNT.account_id,
dbo.ACCOUNT.date_first_account_opened,
dbo.ACCOUNT.NAICS_No,
dbo.ACCOUNT.NAICS_description
FROM
dbo.CUSTOMER
LEFT OUTER JOIN dbo.ACCOUNT WITH (nolock)
ON dbo.CUSTOMER.account_id = dbo.ACCOUNT.account_id
AND dbo.account.NAICS_No in ('6011','6062', '6021', '6022', '6035', '6036', '6029', '6081', '522110')-- SIC and NAICS codes
Something like this should work:
SELECT
cu.customer_id,
cu.customer_full_name,
ac.account_id,
ac.date_first_account_opened,
ac.NAICS_No,
ac.NAICS_description
FROM
dbo.CUSTOMER cu
INNER JOIN dbo.ACCOUNT ac
ON cu.account_id = ac.account_id
WHERE cu.customer_id in (-- List of customers with at least one "target" account
select distinct cu2.customer_id
from dbo.CUSTOMER cu2
inner join dbo.ACCOUNT ac2
on ac2.account_id = cu2.account_id
where ac.NAICS_No in ('6011','6062', '6021', '6022', '6035', '6036', '6029', '6081', '522110'))
The subequery gets a list of all customers with at least one of the specified accounts, and the "outer" query gets all accounts for those customers.
Related
I have 2 tables-one customers, one transactions. One customer does not have any transactions. How do I handle that? As I'm trying to join my tables, the customer with no transaction does not show up as shown in code below.
SELECT Orders.Customer_Id, Customers.AcctOpenDate, Customers.CustomerFirstName, Customers.CustomerLastName, Orders.TxnDate, Orders.Amount
FROM Orders
INNER JOIN Customers ON Orders.Customer_Id=Customers.Customer_Id;
I need to be able to account for the customer with no transaction such as querying for least transaction amount.
Use below updated query - Right Outer join is used instead of Inner join to show all customers regardless of the customer placed an order yet.
SELECT Orders.Customer_Id, Customers.AcctOpenDate,
Customers.CustomerFirstName, Customers.CustomerLastName,
Orders.TxnDate, Orders.Amount
FROM Orders
Right Outer JOIN Customers ON Orders.Customer_Id=Customers.Customer_Id;
INNER Joins show only those records that are present in BOTH tables
OUTER joins gets SQL to list all the records present in the designated table and shows NULLs for the fields in the other table that are not present
LEFT OUTER JOIN (the first table)
RIGHT OUTER JOIN (the second table)
FULL OUTER JOIN (all records for both tables)
Get up to speed on the join types and how to handle NULLS and that is 90% of writing SQL script.
Below is the same query with a left join and using ISNULL to turn the amount column into 0 if it has no records present
SELECT Orders.Customer_Id, Customers.AcctOpenDate, Customers.CustomerFirstName, Customers.CustomerLastName
, Orders.TxnDate, ISNULL(Orders.Amount,0)
FROM Customers
LEFT OUTER JOIN Orders ON Orders.Customer_Id=Customers.Customer_Id;
try this :
SELECT Orders.Customer_Id, Customers.AcctOpenDate, Customers.CustomerFirstName, Customers.CustomerLastName, Orders.TxnDate, Orders.Amount
FROM Orders
Right OUTER JOIN Customers ON Orders.Customer_Id=Customers.Customer_Id;
I strongly recommend LEFT JOIN. This keeps all rows in the first table, along with matching columns in the second. If there are no matching rows, these columns are NULL:
SELECT c.Customer_Id, c.AcctOpenDate, c.CustomerFirstName, c.CustomerLastName,
o.TxnDate, o.Amount
FROM Customers c LEFT JOIN
Orders o
ON o.Customer_Id = c.Customer_Id;
Although you could use RIGHT JOIN, I never use RIGHT JOINs, because I find them much harder to follow. The logic of "keep all rows in the first table I read" is relatively simple. The logic of "I don't know which rows I'm keeping until I read the last table" is harder to follow.
Also note that I included table aliases and change the CustomerId to come from customers -- the table where you are keeping all rows.
Using CASE will replace "null" with 0 then you can sum the values. This will count customers with no transactions.
SELECT c.Name,
SUM(CASE WHEN t.ID IS NULL THEN 0 ELSE 1 END) as TransactionsPerCustomer
FROM Customers c
LEFT JOIN Transactions t
ON c.Name = t.customerID
group by c.Name
SELECT c.Name,
SUM(CASE WHEN t.ID IS NULL THEN 0 ELSE 1 END) as numberoftransaction
FROM customers c
LEFT JOIN transactions t
ON c.Name = t.customerID
group by c.Name
I have three tables:
Table1 - Customer profile
customer id
customer name
customer country
customer total sales
Table2 - Contract list
contract number
customer id
customer state
contract sales
business code
Table3 - Business index
business code
business type
Each customer has xxx amount of contracts, and each 'customer total sales' in Table1 equals to sum of the 'contract sales' in Table2 for that particular customer.
What I want to list is all customer name, each customer's total sales, country; also list each customer's state and business type.
My query tried to pull customer id, customer name, customer country, customer total sales from Table1 and customer state from Table2 and business type from Table3. But when I Left Join Table1.customer id-->Table2.customer id, and left join Table2.business code-->Table3.business type, it pull all data I wanted but there were xxx row of duplicate records for each customer.
Then, I attempt to find out what caused it by removing one join and changing the join properties. It pops up message for 'ambiguous outer join' when:
I remove either one of the join leaving only two tables linked with one Left join;
I change either one of the joins to either Right join or Inner join
In T-SQL:
As table Customer_profile and Contract_list both have customer_id column table alias should be used to avoid ambigous column name error. Also to remove duplicate records Distinct key word should be added.Following query should work:
select distinct cp.customer_id,
cp.customer_name,
cp.customer_country,
cp.customer_total_sales ,
cl.customer_state,
bi.business_type
from Customer_profile cp
left join Contract_list cl on cp.customer_id = cl.customer_id
left join Business_index bi on bi.business_code = cl.business_code ;
I have a select statement with 5 ID columns. I need to lookup and select the corresponding customer names from a Customer master table that stores Ids/names and come up with a Customer report. The tables columns are as below:
origCustomerID,Tier1PartnerID,Tier2PartnerID,DistributorId,EndCustomerID,productId,OrderTotal,OrderDate
The first 5 columns are ID columns that match CustID column in the Customers table. Note that NOT all of these columns will contain a value for a given record at all times, i.e. they could be null at times. Given the current constraints in hiveQL, I can only think of the following way, but this takes up a lot of time and is not the best possible way. Could you please suggest any improvements to this?
Select origCustomerID,a.name,Tier1PartnerID,b.name,Tier2PartnerID,
c.name,DistributorId,d.name,EndCustomerID,e.name,productId,OrderTotal,OrderDate
From Orders O
LEFT OUTER JOIN customers a on o.origCustomerID = a.custid
LEFT OUTER JOIN customers b on o.Tier1PartnerID = a.custid
LEFT OUTER JOIN customers c on o.Tier2PartnerID = a.custid
LEFT OUTER JOIN customers d on o.DistributorId = a.custid
LEFT OUTER JOIN customers e on o.EndCustomerID = a.custid
If the id values are always either customer ids or NULL (i.e. in the case they are not NULL you are sure they are customer ids and not something else) and each record in the Orders table matches at most one customer (i.e. every record has at most one id in those five columns; or possible the same id several times), you could perhaps use COALESCE in your matching expression.
I can't test this at the moment, but this should join the records using the first non-NULL id from the Orders table.
SELECT [stuff]
FROM Orders O
LEFT OUTER JOIN customers a
ON COALESCE(o.origCustomerID,
o.Tier1PartnerID,
o.Tier2PartnerID,
o.DistributorId,
o.EndCustomerID) = a.custid
Hope that helps.
I often get asked the questions in an interview that "what is an outer join in SQL"?
While it can be answered, I wonder what might be some classic and good real life examples where a (LEFT) OUTER JOIN is used?
In the Northwind database on the Customers and Orders table.
Doing an inner join will only give you customers that have placed orders.
Doing an outer join will get all customers and orders for customers that have placed orders.
To add to Robin Day's answer, you can also use a Left Outer Join to grab only customers who have NOT placed orders by checking for NULL.
SELECT *
FROM Customer
LEFT OUTER JOIN Order
ON Customer.CustomerId = Order.CustomerId
WHERE Order.CustomerId IS NULL
Following is the visual represntation of the left outer join
SELECT <select_list>
FROM Table_A A
LEFT JOIN Table_B B
ON A.Key = B.Key
read more about joins in the below article
http://www.codeproject.com/KB/database/Visual_SQL_Joins.aspx ( one of the best article must read )
A LEFT OUTER JOIN can be used when you want all records from one table, as well as records from another table if any.
E.g., given table User and Address, where Address has a FK to User and there could be 0 or more addresses per user:
select *
from User u
left outer join Address a on u.UserID = a.UserID
This will ensure you get all User records, regardless of whether there was a corresponding Address record or not.
If you want to show all Users that do not have addresses, you can do this:
select *
from User u
left outer join Address a on u.UserID = a.UserID
where a.UserID is null
Classic example is cutomers and orders. Some customers have orders and others do not. You want to show a list of customers with total sales. So you do a left outer join from the customer to the order and get:
Customer A: $100;
Customer B: $0;
Customer C: $500
instead of:
Customer A: $100;
Customer C: $500
Here is an example:
I need a list of all customers, with their vouchers, I also need the customers that never used vouchers.
SELECT *
FROM Customer
LEFT OUTER JOIN Voucher
ON Customer.CustomerId = Voucher.CustomerId
Get a list of all customers including any details of orders they have made. Some customers may not have made orders and therefore an INNER JOIN would exclude them from this list.
SELECT
*
FROM
Customer
LEFT OUTER JOIN
Order
ON
Customer.CustomerId = Order.CustomerId
I have a workorder system using SQL Express 2008. I have a table called Workorders that has several personnel that are linked to it via UserID. In the Workorder table I have TechID for the Technician, CustomerID for the Customer, QAID for quality assurance. These are linked back to the User Table via UserID (User Table PK). I want to join the tables to return Technician Name, Customer Name, and QA Name from the User Table and other job information information from the Workorder Table. I have no idea how to construct the join.
What about something a bit like this :
select tech.name as tech_name,
customer.name as customer_name,
qa.name as qa_name
from Workorders
inner join User as tech on tech.userId = Workorders.techId
inner join User as customer on customer.useId = Workorders.CustomerId
inner join User as qa on qa.useId = Workorders.QAID
(Might need some tunning, but the idea should be here)
ie, you are :
starting with a workorder
inner join on its tech guy (a User),
and then inner joinning on its customer (another user)
and so on
And this allows you to get each name, using the right alias in the select clause.
Note that I used aliases in the select clause too -- that might be usefull to have "worker_name" and "tech_name", instead of just two columns names "name" -- especially if you are calling this query from some other programming language.
Note : if one of those userId field can be NULL, you might want to use a left join, instead of an inner join.
select tus.Name as 'TechnicianName',
cus.Name as 'CustomerName',
qus.Name as 'QaName',
wod.*
from WorkOrders wod
left outer join
Users tus on tus.UserId = wod.TechId
left outer join
Users cus on cus.UserId = wod.CustomerId
left outer join
Users qus on qus.UserId = wod.QaId