sql left join 2 tables - sql

I have a SQL query with a left join which works fine:
SELECT book.* FROM book
LEFT JOIN purchase ON book.book_id = purchase.book_id
WHERE purchase.user_id = 3
ORDER BY purchase.purchase_date
But I need also infos from purchase table, so I tried:
SELECT purchase.*, book.*
FROM purchase, book
LEFT JOIN purchase ON book.book_id = purchase.book_id
WHERE purchase.user_id = 3
ORDER BY purchase.purchase_date
But it does not work, I have this error message: #1066 - Not unique table/alias: 'purchase'
How can I do modify the first request to get data from purchase table also ?

Your 1st statement was nearly exactly what you want, you just need to name in the SELECT, which fields you want to return from purchase table.
e.g.
SELECT book.*, purchase.user_id
FROM book
LEFT JOIN purchase ON book.book_id = purchase.book_id
WHERE purchase.user_id = 3
ORDER BY purchase.purchase_date
You don't need to list "purchase" in the FROM clause as well as in the JOIN - because you have, that is why you are seeing the error.

You do not need to refer to the purchase table in the FROM clause - that would mean that you are both cross-joining book and purchase tables and then joining purchase table again. Because there are two instances of purchase table, they need to have unique alias - thus the error. You probably just need this:
SELECT purchase.*, book.*
FROM purchase LEFT JOIN purchase ON book.book_id = purchase.book_id
WHERE purchase.user_id = 3 ORDER BY purchase.purchase_date;

Related

How to put conditions on left joins

I have two tables, CustomerCost and Products that look like the following:
I am joining the two tables using the following SQL query:
SELECT custCost.ProductId,
custCost.CustomerCost
FROM CUSTOMERCOST Cost
LEFT JOIN PRODUCTS prod ON Cost.productId =prod.productId
WHERE prod.productId=4
AND (Cost.Customer_Id =2717
OR Cost.Customer_Id IS NULL)
The result of the join is:
joins result
What i want to do is when I pass customerId 2717 it should return only specific customer cost i.e. 258.93, and when customerId does not match then only it should take cost as 312.50
What am I doing wrong here?
You can get your expected output as follows:
SELECT Cost.ProductId,
Cost.CustomerCost
FROM CUSTOMERCOST Cost
INNER JOIN PRODUCTS prod ON Cost.productId = prod.productId
WHERE prod.productId=4
AND Cost.Customer_Id = 2717
However, if you want to allow customer ID to be passed as NULL, you will have to change the last line to AND Cost.Customer_Id IS NULL. To do so dynamically, you'll need to use variables and generate the query based on the input.
The problem in the original query that you have posted is that you have used an alias called custCost which is not present in the query.
EDIT: Actually, you don't even need a join. The CUSTOMERCOST table seems to have both Customer and Product IDs.
You can simply:
SELECT
Cost.ProductId, Cost.CustomerCost
FROM
CUSTOMERCOST Cost
WHERE
Cost.Customer_Id = 2717
AND Cost.productId = 4
You seem to want:
SELECT c.*
FROM CUSTOMERCOST c
WHERE c.productId = 4 AND c.Customer_Id = 2717
UNION ALL
SELECT c.*
FROM CUSTOMERCOST c
WHERE c.productId = 4 AND c.Customer_Id IS NULL AND
NOT EXISTS (SELECT 1 FROM CUSTOMERCOST c2 WHERE c2.productId = 4 AND c2.Customer_Id = 2717);
That is, take the matching cost, if it exists for the customer. Otherwise, take the default cost.
SELECT custCost.ProductId,
custCost.CustomerCost
FROM CUSTOMERCOST Cost
LEFT JOIN PRODUCTS prod
ON Cost.productId =prod.productId
AND (Cost.Customer_Id =2717 OR Cost.Customer_Id IS NULL)
WHERE prod.productId=4
WHERE applies to the joined row. ON controls the join condition.
Outer joins are why FROM and ON were added to SQL-92. The old SQL-89
syntax had no support for them, and different vendors added different,
incompatible syntax to support them.

SQL filtering data

I need some advice on how to query a database more efficiently.
The situation can be explained as this. If a customer comes into the store and buys something that creates a sale in the [sales table] this table links via transaction reference to [transactions table] but there may be a number of items purchased, each of these creates a row in the [products table].
To add to this there are also two promotion tables that indicate whether the product is on price promotion [promotions] or on a multibuy promotion[sales flag]. Both of these also link back to the sales table.
I want to be able to bring back all the rows so every product of every transaction that was in one of the promotions is available.
The conditions work as follows
Product ¦ Price promo (promotions-promotion no) ¦Multibuy(salesflag-On promotion)
-----------------------------------
1.Product A -1 0 = neither price promo nor multibuy
2.Product B -1 1 = multibuy only ie 0 false 1 true
3.Product C 8245 0 = price promo only can be any number other than -1
These are the three scenarios yet they could all happen in one customer transaction.
so how could I check every transaction for the circumstances of products B and C and return the full basket if either is present?
All I can think to do is a further select within the where clause but this would return all the relevant transaction numbers and the main query would then search the entire database for these transaction numbers it seems a very long winded way as it is a huge database I'm working on + I'm fairly basic level sql at the moment.
SELECT
TRANS.TransactionReference
,TRANS.TID
,CONVERT(VARCHAR(10),T.Day,103) as 'Date'
,PROD.HierarchyLevel2
,PROD.HierarchyLevel3
,PROD.HierarchyLevel4
,PROD.ProductDescription
,PROD.ExternalProductCode
,PROD.Size
,CUST.CardNumber
,S.SalesQty /*unless Group everything somehow will get multiple lines per qty sold all with qty of 1*/
,S.SalesValue
,STOR.Area
,STOR.StoreCode
,STOR.StoreName
FROM Reporting.Sales as S
INNER JOIN Reporting.Products as PROD
on S.ProductID = PROD.ProductID
INNER JOIN Reporting.Time as T
on S.DateID = T.DateID
INNER JOIN Reporting.Stores as STOR
on S.StoreID = STOR.StoreID
INNER JOIN Reporting.Promotions as PROM
on S.PromotionID = PROM.PromotionID
INNER JOIN Reporting.SalesFlags as FLAG
on S.SalesFlagID = FLAG.SalesFlagID
INNER JOIN Reporting.Transactions as TRANS
on S.TID = TRANS.TID
INNER JOIN Reporting.Customers as CUST
on S.CustomerID = CUST.CustomerID
WHERE
T.Day BETWEEN '2014-01-23' and '2014-02-11'
AND TRANS.TransactionReference IN(SELECT distinct TRANS.TransactionReference
FROM Reporting.Sales as S
INNER JOIN Reporting.Transactions as TRANS
on S.TID = TRANS.TID
INNER JOIN Reporting.Promotions as PROM
on S.PromotionID = PROM.PromotionID
INNER JOIN Reporting.SalesFlags as FLAG
on S.SalesFlagID = FLAG.SalesFlagID
INNER JOIN Reporting.Time as T
on S.DateID = T.DateID
WHERE
PROM.PromotionNumber = '-1'
AND FLAG.OnPromotion = 1)
AND T.Day BETWEEN '2014-01-23' and '2014-02-11'
ORDER BY
TRANS.TransactionReference
;
The above would only take account of the sales where a basket contained a multibuy and i havent worked through how to include the sales where the basket can also contain a price promo. But already I'm leaving the query for 10 minutes and getting no results back- just keeps running.
The inner joins I have used I believe are all primary keys as the database has been set up cleverly to all link back to the sales table through keys built into this table.
Thanks in advance,
Kind regards,
Adam.

WHERE using a temporary table

I have three tables: customers, orders and refunds. Some customers (not all) placed orders and for some orders (not all) there were refunds.
When I join the three tables like this (the details are not that important):
SELECT ...
FROM customers LEFT JOIN orders
ON customers.customer_id=orders.customer_id
LEFT JOIN refunds
ON orders.order_id=refunds.order_id;
//WHERE order_id IS NOT NULL;// uncomment to filter out customers that have no orders
I get a big table in which all customers are listed (even the ones that have not placed any orders and they have NULL in the 'order_id' column), with all their orders and the orders' refunds (even if not all orders have refunds):
NAME ORDER_ID ORDER AMOUNT REFUND
------------------------------------------------------------
Natalie 2 12.50 NULL
Natalie 3 18.00 18.00
Brenda 4 20.00 NULL
Adam NULL NULL NULL
Since I only want to see only customers that have placed orders, i.e in this case I want to filter Adam from the table, I uncomment the 'WHERE' row from the SQL query above.
This yields the desired result.
My question is:
On which table is the WHERE executed - on the original 'orders' table (which has no order_id that is NULL) or on the table that is result of the JOINs?
Apparently it is the latter, but just want to make sure, since it is not very obvious from the SQL syntax and it is a very important point.
Thank you
In this case, you're making SQL work harder than it has to. It is operating on the results (likely a MERGE event, or something along those lines).
There's a chance SQL is realizing what you're doing and optimizing the plan and changing to an INNER JOIN for you. But I can't be certain (and neither can SQL -- it can change how it optimizes over time).
In the case where you only want where an order is there, use an INNER JOIN instead. SQL will be much more efficient at this.
SELECT ...
FROM customers
INNER JOIN orders
ON customers.customer_id=orders.customer_id
LEFT JOIN refunds
ON orders.order_id=refunds.order_id;
You can change the LEFT JOIN as INNER JOIN to eliminate customers which don't have any order
SELECT ...
FROM customers INNER JOIN orders
ON customers.customer_id=orders.customer_id
LEFT JOIN refunds
ON orders.order_id=refunds.order_id;
It's because you're using LEFT JOIN, which will return all rows from the left hand table, in your case this is the Customer Table, and return NULL where no corresponding values appear in the right hand tables.
Just rewrite it using inner joins, so only rows where matching data is found will be returned.
SELECT ...
FROM customers
INNER JOIN orders
ON customers.customer_id=orders.customer_id
INNER JOIN refunds
ON orders.order_id=refunds.order_id;

Suggestion needed for this Hive Query

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.

Return customers with no sales

I'm a bit of a beginner with SQL so apologies if this seems trivial/basic. I'm struggling to get my head around it...
I am trying to generate results that show all customers that are in the customer table that have never placed an order and will therefore have no entry on the invoice table.
In other words, I want to select all customers from the customer table where there is no entry for their customer number in the invoice table.
Many thanks,
Mike
SELECT *
FROM customer c
WHERE NOT EXISTS (
SELECT 1
FROM invoice i
WHERE i.customerid = c.customerid
)
I would suggest you also read Oracle's documentation on different types of table joins here.
if customer_id is the collumn that identify the customer you should do something like this...
select * from Customer
where customer_id not in (select customer_id from invoice)
If you want to return all customer rows, then you will want to use a LEFT JOIN
select *
from customer c
left join invoices i
on c.customerid = i.customerid
where i.customerid is null
See SQL Fiddle with Demo
If you need help learning JOIN syntax, then here is a great visual explanation of joins.
A LEFT JOIN will return all rows from the customer table even if there is not a matching row in the invoices table. If you wanted to return only the rows that matched in both tables, then you would use an INNER JOIN. By adding the where i.customerid is null to the query it will return only those rows with no match in invoices.