SUBSELECT (SQL) - sql

I have to tables. The first table (customers) shows data about the customers (id, firstname, lastname). The second table shows data about the orders (timestamp and customer id). So, i wanted to see the last order by a customer in my table "customers" and did a SUBSELECT and it worked.
SELECT id,firstname, lastname,
(SELECT timestamp FROM orders
WHERE customers.id = orders.customer_id
ORDER BY timestamp DESC LIMIT 1) AS last_order
FROM customers
WHERE (SELECT timestamp FROM orders
WHERE customers.id = orders.customer_id) IS NOT NULL
But, there are some customers who never ordered something and so is no value in the column "last_order". I am trying to filter these customers out with another SUBSELECT after the WHERE but i am failing. Can somebody help me?

The problem is with the second sub-query, it may return more than one value (one customer may have multiple orders); it should return only one value, so you may limit it by 1 as the following:
WHERE (SELECT timestamp FROM orders
WHERE customers.id = orders.customer_id Limit 1) IS NOT NULL
Another approach is to use Exists as the following:
SELECT id,firstname, lastname,
(SELECT timestamp_ FROM orders
WHERE customers.id = orders.customer_id
ORDER BY timestamp_ DESC LIMIT 1) AS last_order
FROM customers
WHERE exists (SELECT 1 FROM orders
WHERE customers.id = orders.customer_id);
Also, you can achieve the required result with a simple Join query as the following:
Select C.id,C.firstname, C.lastname, Max(O.timestamp_) AS last_order
From customers C
join orders O on
C.id=O.customer_id
Group By C.id,C.firstname, C.lastname
See a demo from db-fiddle.

Related

How to sort customer names by items purchased in SQL

Sorry new to this but,
I need to sort customer names by total quantity of items purchased by them, in desc order.
the purchased amount in the table is known as Inv_number
I would need the customer name and than the sum. This is what it should be
Thanks
I don't know what your tables are named or what fields they have on them, so I'll give a generic example:
SELECT c.CUST_NAME AS "cust_name",
SUM(oi.QTY_PURCHASED) AS "sum"
FROM CUSTOMERS c
INNER JOIN ORDERS o
ON o.ID_CUSTOMERS = c.ID_CUSTOMERS
INNER JOIN ORDER_ITEMS oi
ON oi.ID_ORDERS = o.ID_ORDERS
GROUP BY c.CUST_NAME
ORDER BY SUM(oi.QTY_PURCHASED) DESC,
c.CUST_NAME ASC
db<>fiddle here
Should work this way - you have to join the tables for customerinfo (customer in this script) and the items purchased (order_items). For simplicity I only made an example with two tables. if the customer_id is present in a kind of "order" table, you have to join order_items -> order -> customer
SELECT
x.customer_name,
y.customer_amount
FROM customer x
INNER JOIN (
SELECT
customer_id,
SUM(order_amount) AS customer_amount
FROM order_items
GROUP BY customer_id
) y ON x.id = y.customer_id
ORDER BY y.customer_amount DESC;

Query table columns from another table using the foreign key

I am trying to get customer email and phonenumber in orders table when I have the order id.
The structure of the schema is below with customer id in orders table:
orders
id
date
item
refid
customerFk
customers
id
name
email
phonenumber
when I try to retrieve the customers phonenumber or email table from the order refId in SQL Server using
select
from order a
where a.custtomerFK.email
I get this error
Cannot call method on bigints
Please assist with the proper query.
The proper syntax is:
SELECT o.date, o.item, o.refid c.Name, c.email, c.phonenumber
FROM orders o
LEFT JOIN customers c ON o.customerFk = c.id
ORDER BY o.date, o.item, c.name
You can use this if you have customers for each order
SELECT orders.date, orders.item, orders.refid
, customers.Name, customers.email, customers.phonenumber
FROM orders
INNER JOIN customers ON orders.customerFk = customers.id

Select only those records where all linked records fulfill a condition

I have a classical 1:n relation between two tables as given below:
I need to select only those customers where 'NOT OrderDate IS NULL' for all their orders. I started with
SELECT Customers.Id, Customers.LastName
FROM Customers, Orders
WHERE Customers.Id = Orders.CustomerId AND NOT Orders.OrderDate IS NULL AND ...
and wanted to build further with 'FOR ALL' but failed.
I tried the suggestions given in the answers but none gave the correct results.
Here is my workaround with two temp tables as shown below:
DECLARE #TableA TABLE (
Id int,
CountA int
)
DECLARE #TableB TABLE (
Id int,
CountB int
)
INSERT INTO #TableA (Id, CountA)
SELECT Customers.Id, COUNT(Orders.Id)
FROM Customers INNER JOIN
Orders ON Customers.Id = Orders.CustomerId
GROUP BY Customers.ID
INSERT INTO #TableB (Id, CountB)
SELECT Customers.Id, COUNT(Orders.Id)
FROM Customers INNER JOIN
Orders ON Customers.Id = Orders.CustomerId
WHERE (NOT Orders.OrderDate IS NULL)
GROUP BY Customers.ID
Select tA.Id
FROM #TableA tA INNER JOIN #TableB tB on tA.Id = tB.Id
WHERE tA.CountA = tB.CountB
Both temp tables differ only in that respect that the first selects the group count without a condition in Orders and the second temp selects them with a condition. Then joining the two temp tables where CountA = CountB gives only those customers where all related Orders fulfill the condition.
If someone finds a more elegant way, please let me know.
Any suggestions how to tackle this?
In cases like this, you need to think not of finding the records where all the related records meet a condition.
Instead, think of finding all the records WHERE there does NOT EXIST a related record that breaks the condition.
The most straightforward way to write this query, is with ALL:
SELECT Customers.Id, Customers.LastName
FROM Customers
WHERE '2000-01-01' < ALL(SELECT OrderDate FROM Orders WHERE Orders.CustomerId = Customers.Id)
You could also write it as a group query on the orders table, something like
WITH CustomerOrderDateRange(CustomerId, MinOrderDate, MaxOrderDate) AS (
SELECT CustomerId, MIN(OrderDate), MAX(OrderDate)
FROM Orders
GROUP BY CustomerId
)
SELECT Customers.Id, Customers.LastName
FROM Customers
JOIN CustomerOrderDateRange
ON Customers.Id = CustomerOrderDateRange.CustomerId
WHERE
CustomerOrderDateRange.MinOrderDate > '2000-01-01'
I think this is cleaner if you need multiple criteria, for example a max date range as well.
Just locate the records you want to exclude from it, and put in a not in clause
select *
from Customers
where Customers.Id not in (
SELECT Customers.Id
FROM Customers
join Orders on Customers.Id = Orders.CustomerId
WHERE Orders.OrderDate < '2000-01-01'
group by Customers.Id
)

Subquery, SQL Group By

I`m following and example give in Coursera course SQL for Data Science, this is the example:
Select customer_name
,customer_state
(Select Count (*) AS orders
FROM Orders
Where Orders.customer_id = Customer.customer_id) AS orders
From customers
Order By Customer_name
So I try to apply the same logic to my Chinook DB using this query:
Select FirstName
,State
(Select Count (*) As invoices
From invoices
Where invoices.CustomerId = customers.CustomerId) As Orders
From Customers
Order by Firstname
But it seems like I`m making a mistake as SQLite wont even let me run it Do you have any idea why?
Missing comma before subquery.
so the syntax will be error.
Select customer_name
,customer_state
,(Select Count (*) AS orders
FROM Orders
Where Orders.customer_id = Customer.customer_id) AS orders
From customers
Order By Customer_name

Select all orders by one customer

I have three tables, orders, orders_details and customers. I need to select orders by one customer for the orders table so I did this
orders columns:
id
customer_id
created
vat
discount
amount
paid
orders_details columns:
id
order_id
cost
qty
product
The SQL I used
SELECT
orders.*,
SUM(orders_details.qty*orders_details.cost) as amount,
SUM(orders_details.qty) AS qty
FROM
orders,
orders_details,
customers
WHERE
orders.customer_id = customers.id
AND orders_details.order_id = orders.id
AND orders.customer_id = 1
but I am getting a wrong qty of 30 instead of 20 and the amount is wrong
If you want to aggregate per order you need a GROUP BY clause. Also you should use proper JOIN syntax, and might consider using aliases to make the query more compact.
SELECT
o.*,
SUM(od.qty * od.cost) AS amount,
SUM(od.qty) AS qty
FROM orders o
INNER JOIN orders_details od ON od.order_id = o.id
INNER JOIN customers c ON o.customer_id = c.id -- not used, might be excluded
WHERE o.customer_id =1
GROUP BY o.id
Depending on what database system you are using you might need to include all columns referenced in o.* in the GROUP BY:
GROUP BY o.id, o.customer_id, o.created, o.vat, o.discount, o.amount, o.paid
Last note: as you don't seem to use any data from the customers table you probably could exclude that table altogether.
You're missing a GROUP BY clause which I'm guessing should be on orders.id.