MySQL Join Without Duplicates - mysql

I have a customers table, and an orders table. Each customer can have many orders.
I want to select every customer, along with their earliest order number from the orders table (it is important that I select the earliest order number, not just any order). I want to list customers whether they have an order or not, and I don't want to include customers twice if they have more than one order.
I'm using this:
SELECT *
FROM customers
LEFT JOIN orders
ON customers.id = orders.customer_id
GROUP BY customers.id
This gives me almost what I want, except it will pick whatever order ID it likes from the table. I need to be able to sort, and pick the smallest order ID.
Any ideas please?
Im pretty sure its something that's staring me in the face...
EDIT: Tables' Structures as requested
Customers:
| ID | Name | Address | Etc |
----------------------------------------
| 1 | Joe | 123 Fake Street | |
| 2 | Mike | 1600 Fake Road | |
| 3 | Bill | Red Square, Moscow | |
----------------------------------------
Orders:
| ID | Customer_ID | Date |
---------------------------
| 1 | 1 | ... |
| 2 | 2 | ... |
| 3 | 2 | ... |
| 4 | 1 | ... |
---------------------------

Create a virtual table (a/k/a subquery) with the lowest numerical order ID for each customer.
SELECT customer_id, min(order_id)
FROM orders
GROUP BY customer_id
Then join that table with the customer table, like so.
SELECT C.customer_id, firstorder.order_id
FROM CUSTOMERS as C
LEFT JOIN (
SELECT customer_id, min(order_id)
FROM orders
GROUP BY customer_id
) AS firstorder ON c.customer_id = firstorder.customer_id

Try this
Select customer.*,Order.OrderNo As EarilerORderNo
From Customers Left Join
(Select customer_id,orderid from orders order by customer_id,orderid desc) As Orders
ON Customers.Id=Orders.OrderID

Related

How to join two table to display sales for each id

I was trying to display the total sale of each id by combining the two table by using the id. I have two table 1. user table, 2. sales table
//user table
--------------
| id | name |
---------------
| 1 | yuki |
| 2 | soman |
---------------
// sales table
--------------
| id | total|
---------------
| 1 | 300 |
| 2 | 23 |
| 1 | 500 |
---------------
With my query it only display 1 sale witch is sales for yuki.
SELECT i.name,SUM(ROUND(s.total),2)) AS sales
FROM user i
INNER JOIN sales s
ON i.id = s.id
--------------
| name | sales|
---------------
| yuki | 800 |
---------------
I want to display the output like this, what did I missed from my query?
--------------
| name | sales|
---------------
| yuki | 800 |
|soman | 23 |
---------------
Your query needs a group by clause:
SELECT u.name, SUM(ROUND(s.total),2)) AS sales
FROM user u
INNER JOIN sales s ON s.id = u.id
GROUP BY u.id, u.name
Such error is much easier to spot when sql mode ONLY_FULL_GROUP_BY is enabled.
As an alternative, you might want to consider a correlated subquery, which avoids outer aggregation (it actually behaves like a LEFT JOIN, which is - probably - closer to what you want):
SELECT u.*,
(SELECT SUM(ROUND(s.total),2)) FROM sales s WHERE s.id = u.id) AS sales
FROM user u
Side note: user is a language keyword, hence not a good choice for a column name. Consider using something else, such as users for example.

Select the name from candidates table if he have highest entries in votes table

I have two table in database
Table1: candidates
id | name
--------
1 | John
2 | Eva
3 | Siera
Table2: votes
| candidateid |
--------
| 1 |
| 1 |
| 1 |
| 1 |
| 2 |
| 2 |
| 3 |
Please someone help me my question is about to how can i select the name of person from candidates who have more entries in votes table.?
Do a simple JOIN between the two tables.
Get total votes per candidate using COUNT function, over a grouping of candidate id.
Order the result by total votes in descending order, and using LIMIT 1 to get the details of the person with highest votes.
Try the following:
SELECT c.id,
c.name,
COUNT(*) AS total_votes
FROM candidates AS c
JOIN votes AS v ON v.candidateid = c.id
GROUP BY c.id
ORDER BY total_votes DESC LIMIT 1
There is no voteId in your sample data. You seem to want:
SELECT CandidateId, count(*)
FROM votes
GROUP by CandidateId;
You can use a JOIN to bring in the name from the other table.

MySQL - Selecting Duplicates across 3 columns and joining with another table to filter

I have a Purchases table, where I'm trying to select all rows where first name, surname and email are duplicates (for all 3).
Purchases table:
| purchase_id | product_id | user_id | firstname | surname | email |
| ------------- | -----------| ------------- | ----------- | --------- | ----------- |
| 1 | 1 | 777 | Sally | Smith | s#gmail.com |
| 2 | 2 | 777 | Sally | Smith | s#gmail.com |
| 3 | 3 | 777 | Sally | Smith | s#gmail.com |
| 4 | 1 | 888 | Bob | Smith | b#gmail.com |
Further to this, each product ID corresponds to a product type in a 'Products' table, and I'm trying to filter by 'lawnmower' purchases (so only product ID 1 & 2)
Products table:
| product_type | product_id |
| ------------- | -----------|
| lawnmower | 1 |
| lawnmower | 2 |
| leafblower | 3 |
I'm hoping to write a query that will return all purchases of the 'lawnmower' type where first name, last name, and email are duplicates (so would return the first two rows of the Purchases table).
This is where my query is at so far, however it's not returning accurate data (e.g. I know I have around 350 duplicates and it's returning 10,000 rows):
SELECT t. *
FROM database_name.purchases t
JOIN (
SELECT firstname, surname, email, count( * ) AS NumDuplicates
FROM database_name.purchases
GROUP BY firstname, surname, email
HAVING NumDuplicates >1
)tsum ON t.firstname = tsum.firstname
AND t.surname = tsum.surname
AND t.email = tsum.email
INNER JOIN database_name.products p2 ON t.product_id = p2.product_id
WHERE p2.product_type = 'lawnmower'
Just wanting to know what I need to tweak in my query syntax.
You know that you should be returning Sally Smith. Create a table from the results of your query above. Then Select * from that table where first_name=sally and surname=Smith. See if you can figure out where you are going wrong based on that. This will help you debug these type of issues yourself in the future.
Your inner SELECT does not filter on the product type. It gets all customers who have purchased any two items. Then you join it to purchases and therefore also get the purchases of customers who have bought any two items and, possibly only one, lawnmower. Add a filter on the product type in the subquery too:
SELECT t.*
FROM database_name.purchases t
INNER JOIN (SELECT purchases.userid
FROM database_name.purchases
INNER JOIN database_name.products
ON products.product_id = purchases.product_id
WHERE products.product_type = 'lawnmower'
GROUP BY userid
HAVING count(*) > 1) s
ON t.user_id = s.user_id
INNER JOIN database_name.products p
ON t.product_id = p.product_id
WHERE p.product_type = 'lawnmower';
Your schema also is problematic -- denormalised. firstname, surname and email depend on user_id (Note that I only grouped and joined using the user_id, that's enough,). So they shouldn't be in purchases, only user_id. product_type better by an ID referencing to some product type table too.

How to write a sql query returns list of orders which can be fulfilled with given items

I am new at SQL and I need to write a query which returns list of orders that can be fulfilled with the items in the inventory.
Lets say the following table shows orders and required items to get them fulfilled.
------------------------------
| OrderID | Required Item No |
------------------------------
| ORD001 | Item007 |
| ORD001 | Item008 |
| ORD001 | Item009 |
| ORD002 | Item008 |
| ORD002 | Item009 |
| ORD002 | Item012 |
| ORD003 | Item008 |
| ORD003 | Item014 |
So In order to get fulfilled,
ORD001 needs Item007,Item008,Item009
ORD002 needs Item008,Item009,Item012
ORD003 needs Item008,Item014
And Lets assume this table is show my inventory
--------------------------
| Item Unique Id |Item No |
--------------------------
| 001 | Item007|
| 002 | Item008|
| 003 | Item008|
| 004 | Item012|
| 005 | Item009|
| 006 | Item014|
| 007 | Item015|
In this case, the query should returns ORD001 and ORD003. Can somebody help me on this?
My first attempt
Following query returns my inventory
SELECT ItemNo, COUNT(ItemNo) AS QTY
FROM InventoryTable
GROUP BY ItemNo
------------------------------
| ItemNo | QTY |
------------------------------
| Item007| 1 |
| Item008| 2 |
| Item009| 1 |
| Item012| 1 |
| Item014| 1 |
| Item015| 1 |
This query returns list of required item for a spesific order
SELECT ItemNo, COUNT(ItemNo) AS QTY
FROM OrdersTable
Where OrderID ='ORD003'
Group By ReqItemNo
------------------------------
| ItemNo | QTY |
------------------------------
| Item008 | 1 |
| Item0014| 1 |
I could not find a way to subtract item quantities between these two tables and make the subtraction for each order and return available orders.
Thanks to #Gordon Linoff for his suggestion
select t1.OrderID
from t1
left join inventory i
on t.required_item_no = i.item_no
group by t1.OrderId
having count(*) = count(i.item_no)
This query seems correct but it returns ORD001,ORD002,ORD003 for the example above. When the Item009 is assigned to ORD001, because there will be no Item009 anymore, the query should not fulfill ORD002.
Basically, you want the order where the required items for an order are in inventory. This will provide that list, but it ignores other order requirements. IOW, if the first order depletes an item required for the 2nd order, it won't pick up on that.
SELECT
t1.OrderID,
COUNT(DISTINCT t1.required_item_no) as `required_items`,
COUNT(DISTINCT i.item_no) as `items_on_hand`
FROM t1
LEFT JOIN inventory i
ON t1.required_item_no = i.item_no
GROUP BY t1.OrderId
HAVING `required_items` = `items_on_hand`;
UPDATE
I see that the query above fails to consider the quantity of each inventory item, and would include items that exist with a zero quantity. The following update should address this issue.
Because of the way order items and inventory are stored, instead of directly referencing the tables, I've used a sub-select in the following. Performance will be an issue when a large number of items are added to the table. You may want to reconsider how the quantities are stored and have one row per inventory item with a column to store the quantity on hand. Same for the order table.
SELECT
t1.OrderID,
COUNT(DISTINCT t1.required_item_no) as `required_items`,
COUNT(DISTINCT i.item_no) as `items_on_hand`
FROM (
SELECT
OrderID,
required_item_no,
COUNT(*) as `QTY`
FROM OrdersTable
GROUP BY OrderID, required_item_no
) t1
LEFT JOIN (
SELECT
item_no,
COUNT(*) as `QTY`
FROM inventory
GROUP BY item_no
) i
ON t1.required_item_no = i.item_no AND
i.QTY >= t1.QTY
GROUP BY t1.OrderId
HAVING `required_items` = `items_on_hand`;

Add column from another table, but not affect count()

I have already a query with multiple JOINs, simple list of reservations
SELECT reservation.reservation_id, customer.customer_id, customer.name, count(ordered_services.reservation_id) AS num_of_ordered_services
FROM reservations
JOIN customers ON reservations.customer_id = customer.customer_id
LEFT JOIN ordered_services ON reservations.reservation_id = ordered_services.reservation_id
GROUP BY reservation.reservation_id, customer.customer_id, customer.name
ORDER BY reservation.reservation_id
which outputs something like
reservation_id | customer_id | name | num_of_ordered_services
1 | 1909091202 | John | 2
2 | 2512541508 | Jane | 3
I would like to add another column with information about payment, but simple JOIN, LEFT JOIN interferes with existing count() column. Like
SELECT reservation.reservation_id, count(payments.reservation_id) AS num_of_payments
FROM reservations
LEFT JOIN payments ON reservations.reservation_id = payments.reservation_id
GROUP BY reservation.reservation_id
ORDER BY reservation.reservation_id
reservation_id | num_of_payments
1 | 0
2 | 2
but in both a single result. How to achieve this?
PS: num_of_payments is not necessary, I only need to know if the payment for certain reservation exists or not (1, 0).
Thank you
tbl structure, nothing special:
reservations
reservation_id | customer_id | added
1 | 1909091202 | 2011-11-04 02:37:28
2 | 2512541508 | 2011-11-04 14:27:01
customers
customer_id | name | personal information columns ...
1909091202 | John | | |
2512541508 | Jane | | |
... | ... | | |
payments
payment_id | reservation_id | customer_id | total | added
1 | 2 | 1909091202 | 199 | 2011-11-04 02:37:28
2 | 2 | 2512541508 | 50 | 2011-11-04 14:27:01
You could use a subselect for the additional field.
SELECT reservation.reservation_id, customer.customer_id, customer.name,
count(ordered_services.reservation_id) AS num_of_ordered_services,
(SELECT count(*) FROM payments WHERE reservation.reservation_id=payments.reservation_id) AS num_of_payments
FROM reservations
JOIN customers ON reservations.customer_id = customer.customer_id
LEFT JOIN ordered_services ON reservations.reservation_id = ordered_services.reservation_id
GROUP BY reservation.reservation_id, customer.customer_id, customer.name
ORDER BY reservation.reservation_id
Something like the following should work:
select
reservation.reservation_id,
(case when exists (select * from payments p1 where p1.reservation_id = reservation.reservation_id) then 1 else 0 end) as one_or_many_payments_made
from reservation
GROUP BY reservation.reservation_id
ORDER BY reservation.reservation_id
But without your data, there is some guesswork here.