A simple nested SQL statement - sql

I have a question in SQL that I am trying to solve. I know that the answer is very simple but I just can not get it right. I have two tables, one with customers and the other one with orders. The two tables are connected using customer_id. The question is to list all the customers that did not make any order! The question is to be run in MapInfo Professional, a GIS desktop software, so not every SQL command is applicable to that program. In other words, I will be thankful if I get more than approach to solve that problem.
Here is how I have been thinking:
SELECT customer_id
from customers
WHERE order_id not in (select order_id from order)
and customer.customer_id = order.customer_id

How about this:
SELECT * from customers
WHERE customer_id not in (select customer_id from order)
The logic is, if we don't have a customer_id in order that means that customer has never placed an order. As you have mentioned that customer_id is the common key, hence above query should fetch the desired result.

SELECT c.customer_id
FROM customers c
LEFT JOIN orders o ON (o.customer_id = c.customer_id)
WHERE o.order_id IS NULL

... The NOT EXISITS way:
SELECT * FROM customers
WHERE NOT EXISTS (
SELECT * FROM orders
WHERE orders.customer_id = customer.customer_id
)

There are some problems with your approach:
There is probably no order_id in the customers table, but in your where-statement you refer to it
The alias (or table-name) order in the where-statement (order.customer_id) is not known because there is no join statement in there
If there would be a join, you would filter out all customers without orders, exactly the opposite of what you want
Your question is difficualt to answer to me because I do not know which SQL subset MapInfo GIS understands, but lets try:
select * from customers c where not exists (select * from order o where o.customer_id=c.customer_id)

Related

oracle sql statement help to query against multiple tables

I am struggling with a sql statement. I am hoping a guru can help a beginner out, currently I have multiple select in statements.. but think there is a better way as I have been stuck.
Below are the tables and pertinent columns in each table
country
-country_id
barcodes_banned_in_country
-barcode(varchar)
-country_id
-country_name
orders
-order_id
-country_name
item
-order_id
-item_id
-barcode(varchar)
The goal is to get all orders that are banned based off the barcode banned list.
Any help with this sql statement would be appreciated.
One option uses exists:
select o.*
from orders o
where exists (
select 1
from barcodes_banned_in_country bic
inner join item i on i.barcode = bic.barcode
where i.order_id = o.order_id and bic.country_name = o.country_name
)
This brings all orders whose at least one item have a barcode that is banned in the order's country.
If, on the other hand, you want the list of the banned barcodes per order, then you can join and aggregate:
select o.order_id, o.country_name, listagg(i.barcode, ',') banned_barcodes
from orders o
inner join item i
on i.order_id = o.order_id
inner join barcodes_banned_in_country bic
on i.barcode = bic.barcode
and bic.country_name = o.country_name
group by o.order_id, o.country_name
Note that, as commented by MT0, you should really be storing the id of the country in orders rather than the country name. Accordingly, you wouldn't need the country name in the banned barcodes table.

Why should I join two columns when I can do it in an easier way?

So think that we’ve got two tables and in one of them, we’ve got our customers and the other has our orders in it and we’ve got a customer id in both of them.
So I want to get the orders of a specific customer and in here I’ve got two ways to do it:
I can say (Select * from orders where id = ‘1’
I can say (Select * from customers join orders on id = customers.id where id = ‘1’
So I’m asking this, why should I use the second method as it’s longer to write or are there any other uses to this?
If you have the customer id, then simply do:
select o.*
from orders o
where o.customer_id = 1;
You only need to join the tables together if you want to use other information, such as:
select o.*
from orders o join
customers c
on o.customer_id = c.customer_id
where c.email = #email;

How to find all customers who didn't place an order?

I am new to stackoverflow, I recently went through an interview as graduate developer, and I was asked a question regarding SQL.
Given two table customers and orders, how to find all cusomers who didn't place an order. Can someone tell me what would be a query to achieve this?
--Forgot to mention that I was asked to use JOIN operator for this.
EDIT: I could not find answer to this question as this was technical as well as logical. Therefore, I think my question is different from the one identified as a duplicate.
SELECT c.CustomerId
,Name
FROM dbo.Customers c
LEFT JOIN dbo.Orders o ON o.CustomerId = c.CustomerId
WHERE OrderId IS NULL
This translates into a Correlated Subquery using EXISTS:
select *
from customers as c
where not exists
( select * from orders as o
where c.customer_id = o.customer_id)
No order exists for a given customer_id

Efficiency of joining subqueries in SQL Server

I have a customers and orders table in SQL Server 2008 R2. Both have indexes on the customer id (called id). I need to return details about all customers in the customers table and information from the orders table, such as details of the first order.
I currently left join my customers table on a subquery of the orders table, with the subquery returning the information I need about the orders. For example:
SELECT c.id
,c.country
,First_orders.product
,First_orders.order_id
FROM customers c
LEFT JOIN SELECT( id,
product
FROM (SELECT id
,product
,order_id
,ROW_NUMBER() OVER (PARTITION BY id ORDER BY Order_Date asc) as order_No
FROM orders) orders
WHERE Order_no = 1) First_Orders
ON c.id = First_orders.id
I'm quite new to SQL and want to understand if I'm doing this efficiently. I end up left joining quite a few subqueries like this onto the customers table in one select query and it can take tens of minutes to run.
So am I doing this efficiently or can it be improved? For example, I'm not sure if my index on id in the orders table is of any use and maybe I could speed up the query by creating a temporary table of what is in the subquery first and creating a unique index on id in the temporary table so SQL Server knows id is now a unique column and then joining my customers table to this temporary table? I typically have one or two million rows in the customers and orders tables.
Many thanks in advance!
You can remove one of your subqueries to make it a little more efficient:
SELECT c.id
,c.country
,First_orders.product
,First_orders.order_id
FROM customers c
LEFT JOIN (SELECT id
,product
,order_id
,ROW_NUMBER() OVER (PARTITION BY id ORDER BY Order_Date asc) as order_No
FROM orders) First_Orders
ON c.id = First_orders.id AND First_Orders.order_No = 1
In your above query, you need to be careful where you place your parentheses as I don't think it will work. Also, you're returning product in your results, but not including in your nested subquery.
For someone who is just learning SQL, your query looks pretty good.
The index on customers may or may not be used for the query -- you would need to look at the execution plan. An index on orders(id, order_date) could be used quite effectively for the row_number function.
One comment is on the naming of fields. The field orders.id should not be the customer id. That should be something like 'orders.Customer_Id`. Keeping the naming system consistent across tables will help you in the future.
Try this...its easy to understand
;WITH cte
AS (
SELECT id
,product
,order_id
,ROW_NUMBER() OVER (
PARTITION BY id ORDER BY Order_Date ASC
) AS order_No
FROM orders
)
SELECT c.id
,c.country
,c1.Product
,c1.order_id
FROM customers c
INNER JOIN cte c1 ON c.id = c1.id
WHERE c1.order_No = 1

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.