select DISTINCT email based off of matching ID across tables - mysql

I'm trying to pull unique emails that have matching IDs across two tables.
SELECT line_items.order_id, line_items.id, orders.email, orders.name
FROM orders INNER JOIN
line_items
ON orders.id = line_items.order_id
WHERE line_items.order_id IN (SELECT DISTINCT email FROM orders WHERE status = 0 AND created_at BETWEEN '2018-01-10' AND NOW() )
LIMIT 50;
I know my error is based upon the fact that the line_items.order_is is an INT and therefore the IN parameter is looking for another int column to match against. However, I'm not sure how to modify this to get pull the proper results. Any and all help is greatly appreciated.

I'm trying to pull unique emails that have matching IDs across two tables.
If you mean distinct emails, then your subquery would appear to do this:
SELECT DISTINCT o.email
FROM o.orders
WHERE o.status = 0 AND o.created_at BETWEEN '2018-01-10' AND NOW();
Because an order should have at least one line item, I don't see why that table is necessary.
Your query comes close to answering the question: "Pull all orders for emails that have made a recent order with status 0". If that is what you want:
SELECT li.order_id, li.id, o.email, o.name
FROM orders o INNER JOIN
line_items li
ON o.id = li.order_id
WHERE o.email IN (SELECT o2.email FROM orders o2 WHERE o2.status = 0 AND o2.created_at BETWEEN '2018-01-10' AND NOW() )
LIMIT 50;

Hard to follow but I think you want:
SELECT sub.order_id, sub.line_item_id, sub.email, o.name
FROM
(SELECT o.email, MIN(o.id) AS order_id, MIN(i.id) AS line_item_id
FROM orders o
INNER JOIN line_items i
ON o.id = i.order_id
WHERE o.status = 0
AND o.created_at BETWEEN '2018-01-10' AND NOW()
GROUP BY o.email) sub
LEFT JOIN orders o
ON sub.order_id = o.id
In the sub-query, select each email along with the first order ID and line item ID. Then join this back to orders to pull the order name. This does assume that the MIN(line_item) will show up with the MIN(order_id) for each email, so you'll have to let me know if that is not a valid assumption.

Related

Get result from JOIN table by checking values in other rows

I have a 1..n relation between person and orders.
Order can have ACTIVE or CANCELLED status.
One person can have at most one ACTIVE order, but can have inifinite number of CANCELLED orders.
Lets assume that content of my join table looks like this:
To get persons with ACTIVE order my query is just:
select * from persons p inner join orders o on p.id = o.person_id where o.status = 'ACTIVE';
The result should be persons with id 1 and 3. That's simple.
But now, I need to get all those persons which don't have any ACTIVE shipping, so the result of such query should be person with id 2 (does not necessarily have to be distinct row). I can't just change to != 'ACTIVE' in above query, because then I get all those 3 persons, as all of them have orders with status other than ACTIVE.
If I were to explain it by words I'd say that for every row I need to check also other row's with the same p.id and check if ACTIVE status does not occur in any of them.
Any suggestions how can I achive that?
I need to get all those persons which don't have any ACTIVE shipping
You can use not exists:
select *
from persons p
where not exists (
select 1
from orders o
where o.person_id = p.id and o.status = 'ACTIVE'
);
For performance with this query, consider an index on orders(person_id, status).
Note that this also allows persons that have no order at all. If you want to avoid that, then use aggregation instead:
select p.*
from persons p
inner join orders o on o.person_id = p.id
group by p.id
having max(o.status = 'ACTIVE') = 0
You can use aggregation:
select p.*
from persons p inner join
orders o
on p.id = o.person_id
group by p.id
having sum(o.status = 'ACTIVE') = 0;

SQL beginner practice problems

Given two tables, orders (order_id, date, $, customer_id) and customers (ID, name)
Here's my method but I'm not sure if it's working & I'd like to know if there's faster/better way of solving these problems:
1) find out number of customers who made at least one order on date 7/9/2018
Select count (distinct customer_id)
From
(
Select customer_id from orders a
Left join customer b
On a.customer_id = b.ID
Group by customer_id,date
Having date = 7/9/2018
) a
2) find out number of customers who did not make an order on 7/9/2018
Select count (customer_id) from customer where customer_id not in
(
Select customer_id from orders a
Left join customer b
On a.customer_id = b.ID
Group by customer_id,date
Having date = 7/9/2018
)
3) find the date with most sales between 7/1 and 7/30
select date, max($)
from (
Select sum($),date from orders a
Left join customer b
On a.customer_id = b.ID
Group by date
Having date between 7/1 and 7/30
)
Thanks,
For problem 1, a valid solution might look like this:
SELECT COUNT(DISTINCT customer_id) x
FROM orders
WHERE date = '2018-09-07'; -- or is that '2018-07-09' ??
For problem 2, a valid solution might look like this:
SELECT COUNT(*) x
FROM customer c
LEFT
JOIN orders o
ON o.customer_id = x.customer_id
AND o.date = '2018-07-09'
WHERE o.crder_id IS NULL;
Assuming there are no ties, a valid solution to problem 3 might look like this:
SELECT date
, COUNT(*) sales
FROM orders
WHERE date BETWEEN '2018-07-01' AND '2018-07-30'
GROUP
BY date
ORDER
BY sales DESC
LIMIT 1;
The default format for a date in MySQL is YYYY-MM-DD, although this can be customized. You have to put quotes around it, otherwise it's treated as an arithmetic expression.
And none of your queries need to join with the customer table. The customer ID is already in the orders table, and you're not returning any info about the customers (like the name or address), you're just counting them.
1) You don't need the subquery or grouping.
SELECT COUNT(DISTINCT customer_id)
FROM orders
WHERE date = '2018-07-09'
2) Again, you don't need GROUP BY in the subquery. There's also a better pattern than NOT IN to get the count of non-matching rows.
SELECT COUNT(*)
FROM customer AS c
LEFT JOIN order AS o on c.id = o.customer_id AND o.date = '2018-07-09'
WHERE o.id IS NULL
See Return row only if value doesn't exist for various patterns to do this.
3) You can't use MAX($) in the outer query because the inner query doesn't return a column with that name. But even if you fix that, it still won't work, because the date column won't necessarily come from the same row that has the maximum. See SQL select only rows with max value on a column for more explanation of this.
You don't need a subquery at all. Use a query that returns the total sales for each day, then use ORDER BY to get the highest one.
SELECT date, SUM($) AS total_sales
FROM orders
WHERE date BETWEEN '2018-07-01' AND '2017-07-30'
GROUP BY date
ORDER BY total_sales DESC
LIMIT 1
If "most sales" is supposed to mean "most number of sales", replace SUM($) with COUNT(*).

Mysql Query: LEFT JOIN List all orders and "balance due" for a customer, if no orders, just list customer. 3 Tables

I have a customers table, orders table, and payments table.
From these I am trying to get one table that has for each record: customer#, order#, Balance due. But if a customer has no order then I just want to list the customer# with NULL values for the other fields.
Using a LEFT JOIN I was able to do this with just the customer# and order# but I cannot figure out how to get the balance due in there as well. I am fairly new to Mysql and tried to search the answer but was unable.
Here is what works for just the customer# and order#:
SELECT
customers.cust_num,
orders.order_id
FROM customers
LEFT JOIN orders ON customers.cust_num = orders.cust_num
But I am trying to incorporate "orders.invoice_amount - SUM(payments.amount) AS balance_due" as another column where the payments table is related to the orders table by a field called "order_id" in both.
Perhaps something like:
SELECT orders.invoice_amount - SUM(payments.amount) AS balance_due FROM payments, orders WHERE payments.order_id = orders.order_id
Any idea of how I could go about doing that or pointers in the right direction?
do an additional LEFT JOIN, but to a sub-query on payments grouped by order. So, if it finds a record in the prequeried result, you are good to go. Also, I changed to using table "aliases" for shorter readability, especially if table names get long, or you have to join multiple times to the same table in a query.
SELECT
c.cust_num,
coalesce( o.order_id, 0 ) as Order_ID,
coalesce( o.invoice_amount, 0 ) as InvoiceAmount,
coalesce( Prepaid.TotalPaid, 0 ) as TotalPaid,
coalesce( o.invoice_amount - coalesce( PrePaid.TotalPaid, 0 ), 0) as BalanceDue
FROM
customers c
LEFT JOIN orders o
ON c.cust_num = o.cust_num
LEFT JOIN
( select
p.order_id,
sum( p.amount ) as totalPaid
from
payments p
group by
p.order_id ) as PrePaid
on o.order_id = PrePaid.order_id
you can try something like
SELECT
customers.cust_num,
orders.order_id,
orders.invoice_amount - SUM(payments.amount)
FROM customers
LEFT JOIN orders ON customers.cust_num = orders.cust_num
INNER JOIN payments P ON P..order_id = orders.order_id
group by cust_num,order_id,orders.invoice_amount

MySQL - Select last record from second table matching with first table

I have two tables customers and orders, below is the structure.
Table - customers
id
customer_name
Table - orders
id
order_id
customer_id
customers table have customers records and orders table have orders placed by customers,
customer_id in orders table is linked to the id field of customers table.
Now one customer can have zero or one or more than one orders, i want to get the last order placed by customers only.
when i run the following query a simple invisible join, it returns all the orders by the customer
SELECT customers.customer_name,orders.order_id FROM orders,customers WHERE orders.customer_id=customers.id
I have also tried different JOIN statements but cannot get the last order by the customer, i want to get it in one SQL query for all customers.
Thank you in advance for your help.
In MySQL there is just few ways to make it work (that I now actually). The first one is sort your table as desc before the join:
SELECT c.customer_name, o.customer_id, o.order_id,o.id FROM customers c
INNER JOIN orders o
ON o.id = (SELECT id FROM orders WHERE customer_id = c.id ORDER BY id DESC LIMIT 1)
Using in real time is the only way to get it done, but if you need to make some join on not real time you can create a temporary table or a alias table sorting it to make your select, like this:
CREATE TABLE tmp_your_table AS
SELECT * FROM orders ORDER BY id DESC
So now you are able to make this join work:
SELECT c.customer_name, o.customer_id, o.order_id,o.id FROM customers c
INNER JOIN tmp_your_table o ON o.id = tmp_your_table.id
Try this query
SELECT
c.customer_name,
max(o.order_id)
FROM
customers c
INNER JOIN
orders o
ON
o.customer_id = c.id
GROUP BY
c.customer_name
You don't have any date field in the order table so assuming the latest order will be the one which has max(order_id).
Try this query
SELECT
c.customer_name,
o.order_id
FROM
customers c
INNER JOIN
orders o
ON
o.customer_id = c.id
ORDER BY
o.id desc
LIMIT 1;

SQL max of a count involving 3 tables

I have one table Customers with CustomerID and PhoneNumber, the second table is Orders that has CustomerId and OrderNumber and a third table OrderDetails that has OrderNumber, PriceOfOneUnit and UnitsOrdered. I need to find the PhoneNumber for the customer who placed the largest order (PriceOfOneUnit * UnitsOrdered). Doing a count(PriceOfOneUnit*UnitsOrdered) as A1 and then `Group By CustomerId Order By A1 DESC LIMIT 1 is evidently not working after joining the 3 tables. Can anyone help.
If we take you at your word, and what you want is the biggest single line-item rather than the largest order, you can find the largest line-item and then find the order to which it belongs and then the customer who placed that order. You can use a dummy aggregate function to pull back the order id from orderDetails.
EDIT:
OK, for someone just starting out, I think it can be clearer to think in terms of Venn diagrams and use what are called Inline Views and subqueries:
select customername, phone
from customer
inner join
(
select o.id, customerid from orders o
inner join
(
select od.orderid from orderdetail od
where (od.qty * od.itemprice) =
(
select max(od.qty * od.itemprice)
from orderdetail as od
)
) as biggestorder
on o.id = biggestorder.orderid
) as X
on customer.id = X.customerid
Each of the queries inside parentheses returns a set that can be joined/intersected with other sets.
Give this a try,
SELECT cus.CustomerId, cus.PhoneNumber
FROM Customers cus
INNER JOIN Orders a
ON cus.CustomerId = a.CustomerId
INNER JOIN OrderDetails b
On a.OrderNumber = b.OrderNumber
GROUP BY cus.CustomerId, cus.PhoneNumber
HAVING SUM(b.PriceOfOneUnit * b.UnitsOrdered) =
(
SELECT SUM(b.PriceOfOneUnit * b.UnitsOrdered) totalOrdersAmount
FROM Orders aa
INNER JOIN OrderDetails bb
On aa.OrderNumber = bb.OrderNumber
GROUP BY aa.CustomerId
ORDER BY totalOrdersAmount DESC
LIMIT 1
)