How to do a join with multiple conditions in the second joined table? - sql

I have 2 tables. The first table is a list of customers.
The second table is a list of equipment that those customers own with another field with some data on that customer (customer issue). The problem is that for each customer, there may be multiple issues.
I need to do a join on these tables but only return results of customers having two of these issues.
The trouble is, if I do a join with OR, I get results including customers with only one of these issues.
If I do AND, I don't get any results because each row only includes one condition.
How can I do this in T-SQL 2008?

Unless I've misunderstood, I think you want something like this (if you're only interested in customers that have 2 specific issues):
SELECT c.*
FROM Customer c
INNER JOIN CustomerEquipment e1 ON c.CustomerId = e1.CustomerId AND e1.Issue = 'Issue 1'
INNER JOIN CustomerEquipment e2 ON c.CustomerId = e2.CustomerId AND e2.Issue = 'Issue 2'
Or, to find any customers that have multiple issues regardless of type:
;WITH Issues AS
(
SELECT CustomerId, COUNT(*)
FROM CustomerEquipment
GROUP BY CustomerId
HAVING COUNT(*) > 1
)
SELECT c.*
FROM Customer c
JOIN Issues i ON c.CustomerId = i.CustomerId

SELECT *
FROM customers as c
LEFT JOIN equipment as e
ON c.customer_id = e.customer_id --> what you are joining on
WHERE (
SELECT COUNT(*)
FROM equipment as e2
WHERE e2.customer.id = c.customer_id
) > 1

You can do it with a sub query instead of Joins:
select * from Customer C where (select Count(*) from Issue I where I.CustomerID = C.CustomerID) < 2
or whatever value you want

Related

How to make it in one query

I have the following relational table schema
Customer(customer_id, customer_name, customer_city)
Branch(branch_id, branch_name, branch_city)
Account(account_id, balance, customer_id, branch_id)
Question name every customer who has accounts in branches at least two different cities.
It is working for me the next query but only if I create a view first. Is there any other option to do it together?
My solution which works:
Create view Cust as select c.Customer_Name, c.customer_ID, b.branch_name, b.branch_city from Customer as c inner Join Account as a on c.customer_ID=a.customer_ID join Branch as b on b.branch_id=a.branch_id
SELECT * FROM Cust as c inner join Cust as c1 on c.CustomerID=c1.CustomerID and c.branch_city <> c1.branch_city
Here is one option using exists with an aggregate query
select c.*
from customer c
where exists (
select 1
from account a
inner join branch b on b.branch_id = a.branch_id
where a.customer_id = c.customer_id
group by c.customer_id
having min(b.branch_city) <> max(b.branch_city)
)

Best way of excluding customers with a specific order

I'm running a pretty basic query. The issue is I'm trying to get results with all of our customers who never ordered product Y. The problem is, if I use a simple WHERE ProductColumn <> 'Product Y', it doesn't work because almost all of our customers have ordered other products.
Basically, I'm wondering how I could exclude on the customer level (instead of the order level) - if a customer has ordered Product Y, I don't want them showing up in my results at all.
Thanks.
You are probably looking for EXISTS().
If I want to find customers who have placed orders:
SELECT c.*
FROM customers c
WHERE EXISTS (
SELECT 1
FROM orders o
WHERE o.customerid = c.customerID
AND productID = 'Y'
)
If I want to find customers who have not placed orders:
SELECT c.*
FROM customers c
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.customerid = c.customerID
AND productID = 'Y'
)
Try this:
select * from customers c
where not exists(select 1 from customers
where cutomer_id = c.customer_id
and productcolumn = 'product y')
This assumes you have 'customer_id' column (or at least some id column in your table).
Simple LEFT JOIN should work:
SELECT c.*
FROM customers c
LEFT OUTER JOIN orders o
ON o.customerid = c.customerID
WHERE o.ProductColumn <> 'Product Y'

selecting entries that are referenced in one of multiple tables

I have four tables:
company
id | name
order
number | company_id
quote
number | company_id
invoice
number | company_id
I want to get the list of companies that are referenced in any of the three (i.e., exclude companies that aren't in any).
I wrote this query:
select c.id, c.name from company c
left outer join order o on o.company_id = c.id
left outer join quote q on q.company_id = c.id
left outer join invoice i on i.company_id = c.id
where o.number is not null or q.number is not null or i.number is not null
But the order, quote, and invoice tables are quite large. So the join causes the query to run forever (one join is O(m*n), three joins is O(m*n*p*q)).
I COULD run this query basically three times with only one join on each query (O(m*n) + O(m*p) + O(m*q)), but I'm hoping for a more optimal solution.
This is for Oracle
Join to a subquery which does a union of the three order tables:
SELECT
c.id, c.name
FROM company c
INNER JOIN
(
SELECT company_id FROM order
UNION
SELECT company_id FROM quote
UNION
SELECT company_id FROM invoice
) t
ON c1.id = t.company_id
The union queries should automatically remove duplicate company_id, such that only distinct companies which are present in any of the three tables should be in the result set of that subquery. The inner join then filters off any companies which were not present at all.

Sql query to display records that appear more than once in a table

I have two tables, Customer with columns CustomerID, FirstName, Address and Purchases with columns PurchaseID, Qty, CustomersID.
I want to create a query that will display FirstName(s) that have bought more than two products, product quantity is represented by Qty.
I can't seem to figure this out - I've just started with T-SQL
You could sum the purchases and use a having clause to filter those you're interested in. You can then use the in operator to query only the customer names that fit these IDs:
SELECT FirstName
FROM Customer
WHERE CustomerID IN (SELECT CustomerID
FROM Purchases
GROUP BY CustomerID
HAVING SUM(Qty) > 2)
Please try this, it should work for you, according to your question.
Select MIN(C.FirstName) FirstName from Customer C INNER JOIN Purchases P ON C.CustomerID=P.CustomersID Group by P.CustomersID Having SUM(P.Qty) >2
Please try this:
select c.FirstName,p.Qty
from Customer as c
join Purchase as p
on c.CustomerID = p.CustomerID
where CustomerID in (select CustomerID from Purchases group by CustomerID having count(CustomerID)>2);
SELECT
c.FirstName
FROM
Customer c
INNER JOIN Purchases p
ON c.CustomerId = p.CustomerId
GROUP BY
c.FirstName
HAVING
SUM(p.Qty) > 2
While the IN suggestions would work they are kind of overkill and more than likely less performant than a straight up join with aggregation. The trick is the HAVING Clause by using it you can limit your result to the names you want. Here is a link to learn more about IN vs. Exists vs JOIN (NOT IN vs NOT EXISTS)
There are dozens of ways of doing this and to introduce you to Window Functions and common table expressions which are way over kill for this simplified example but are invaluable in your toolset as your queries continue to get more complex:
;WITH cte AS (
SELECT DISTINCT
c.FirstName
,SUM(p.Qty) OVER (PARTITION BY c.CustomerId) as SumOfQty
FROM
Customer c
INNER JOIN Purchases p
ON c.CustomerId = p.CustomerId
)
SELECT *
FROM
cte
WHERE
SumOfQty > 2

Join SQL Statements optimization

I have 2 Tables:
Customer
ID
Customer_ID
Name
Sir_Name
Phone
Email
and
Table Invoice
Manager_Name
Manaer_First_Name
Customer_ID1
Customer_ID2
Customer_ID3
There is only one Customer.Customer_ID for each Customer or a Customer has no Customer_ID
In Invoice.Customer_ID1 i have the same Customer_ID.Customer_ID several times.
I Like to get all Records in Customer Table Join Invoice Table - check if the Customer_ID = Customer_ID1 if not check in Customer_ID = Customer_ID2 Or Customer_ID = Customer_ID2
If customer_ID is found in one of rows stop the search.
Probably the best way to write the query is:
select . . .
from customer c join
invoice i
on c.customer_id = coalesce(i.customer_id1, i.customer_id2, i.customer_id3);
This should be able to take advantage of an index on customer(customer_id). If this is not efficient, then another alternative is left join:
select . . ., coalesce(c1.col1, c2.col1, c3.col1) as col1, . . .
from invoice i left join
customer c1
on c1.customer_id = i.customer_id1 left join
customer c2
on c2.customer_id = i.customer_id2 left join
customer c3
on c3.customer_id = i.customer_id3;
The left join can take advantage of an index on customer(customer_id). You need to use coalesce() in the select to choose the field from the right table.
select
*
from [Table Invoice] A
JOIN [Customer] B
ON B.Customer_ID = A.Customer_ID1 OR (B.Customer_ID <> A.Customer_ID1 AND B.Customer_ID = A.Customer_ID2) OR (B.Customer_ID = A.Customer_ID3 AND B.Customer_ID <> A.Customer_ID2 AND B.Customer_ID <> A.Customer_ID1)
this would return you all the Invoices for all of the Customers. In case you need Invoices just for one customer - add
WHERE B.Customer_ID = #YourCustomerID
statement. If you need only one, first invoice, add 'TOP 1' to select statement:
SELECT TOP 1
Could a inner join on or clause
select Customer.*, Invocie.*
from Customer
inner join Invoice on ( Customer.Customer_ID = Invoce.Customer_ID1
OR Customer.Customer_ID = Invoce.Customer_ID2
OR Customer.Customer_ID = Invoce.Customer_ID3)
This is how I understand your request: You want all customers that have at least one entry in the invoice table. But per customer you want the "best" invoice record only; with ID1 match better than ID2 match and ID2 match better than ID3 match.
So join the tables to get all matches and then rank your matches with row_number giving the best matching record #1. Then only keep those rows ranked #1.
select *
from
(
select
c.*,
i.*,
row_number() over
(
partition by c.customer_id order by
case c.customer_id
when i.customer_id1 then 1
when i.customer_id2 then 2
when i.customer_id3 then 3
end
) as rn
from customer c
join invoice i on c.customer_id in (i.customer_id1, i.customer_id2, i.customer_id3)
)
where rn = 1;