I have 2 MySQL tables:
Persons:
person_id
person_name
Orders:
person_id
cost
order_name
I need to make a ONE sql-query to get person name and it's cheapest order.
Want to write some like this: SELECT person_name, order=(SELECT order_name FROM orders WHERE order.person_id = person.person_id ORDER BY order.cost ASC LIMIT 1) FROM person
But it don't work.
I would use a correlated query. There are many ways to do it: as a subquery in the SELECT clause, or as a subquery in the WHERE clause.
In this case, I implemented the second option:
SELECT
person_name,
order_name
FROM Persons
INNER JOIN Orders ON Persons.person_id = Orders.person_id
WHERE
(
cost = (
SELECT MIN(cost)
FROM Orders
WHERE
(Orders.person_id = Persons.person_id)
)
)
remove order= and add AS order_name
Something like this:
SELECT p.person_name
, ( SELECT o.order_name
FROM orders o
WHERE o.person_id = p.person_id
ORDER BY o.cost ASC
LIMIT 1
) AS order_name
FROM person p
ORDER BY p.person_name
SELECT person_name, order_id, order_name, MIN(cost) as cost
FROM persons
INNER JOIN orders
ON persons.person_id = orders.person_id
GROUP BY person.person_name, order_id, order_name;
You can do this just by joining your 2 tables and use MIN.
Related
I have this schema here, and I need to find the name of the customer with the highest total amount for the orders. I have a SQL query here:
SELECT Name
FROM (SELECT Name, SUM(Amount) AS Total
FROM customer JOIN orders ON cust_id = ID
GROUP BY Name) AS Totals
WHERE Total = (SELECT MAX(Total)
FROM (SELECT Name, SUM(Amount) AS Total
FROM customer JOIN orders ON cust_id = ID
GROUP BY Name) AS X);
But this is very inefficient as it creates the same table twice. Is there any more efficient way to get the name?
If you want customer with the greatest total mount, then you can just join, order by and limit:
select c.name
from customer c
inner join orders o on o.cust_id = c.id
group by c.id, c.name
order by sum(o.amount) desc
limit 1
Note that this does not handle possible top ties. For this, you need a little more code. Instead of ordering, you would typically filter with a having clause:
select c.name
from customer c
inner join orders o on o.cust_id = c.id
group by c.id, c.name
having sum(o.amount) = (
select sum(o1.amount)
from orders o1
group by cust_id
order by sum(o1.amount) desc
limit 1
)
Finally: if you are running MySQL 8.0, this is simpler done with window function rank():
select name
from (
select c.name, rank() over(order by sum(o.amount) desc) rn
from customer c
inner join orders o on o.cust_id = c.id
group by c.id, c.name
) t
where rn = 1
Here's my orders table:
I want to select all orders excluding very first order of each customer (if customer has placed multiple orders).
So if a customer e.g. 215 has total 8 orders, then I will select his all last 7 orders excluding his very first order 70000 which was placed on 10 July 2017.
But if a customer e.g. 219 had placed only one order 70007, it must be selected by the query.
Using an anti-join approach:
SELECT o1.order_id, o1.customer_id, o1.order_date, o1.order_value
FROM orders o1
LEFT JOIN
(
SELECT customer_id, MIN(order_date) AS min_order_date, COUNT(*) AS cnt
FROM orders
GROUP BY customer_id
) o2
ON o1.customer_id = o2.customer_id AND
o1.order_date = o2.min_order_date
WHERE
o2.customer_site = 1 AND
(o2.customer_id IS NULL OR
o2.cnt = 1);
The idea here is to try to match each record in orders to a record in the subquery, which contains only first order records, for each customer. If we can't find a match, then such an order record cannot be the first.
You can try below -
select order_id,customer_id,order_date,order_Value
from tablename
group by order_id,customer_id,order_date,order_Value
having count(order_id)=1
union all
select order_id,customer_id,order_date,order_Value
from tablename a where order_date not in (select min(order_date) from tablename b
where a.customer_id=b.customer_id)
Solution
Dear #Tim Biegeleisen, your answer almost done. just add HAVING COUNT(customer_id)>1
So the query is below:
SELECT o1.order_id, o1.customer_id, o1.order_date, o1.order_value
FROM orders o1
LEFT JOIN (
SELECT customer_id, MIN(order_date) AS min_order_date
FROM orders
GROUP BY customer_id
HAVING COUNT(customer_id)>1
) o2
ON o1.customer_id = o2.customer_id AND
o1.order_date = o2.min_order_date
WHERE
o2.customer_id IS NULL;
I try to get a list of products with each newest and lowest offer price
Table product:
id | name
Table offer:
id | product_id | price | created | dealer_id
Table invalids:
id | offer_id | status
I have tried:
SELECT * FROM product INNER JOIN
(
SELECT offer.product_id , offer.price
FROM offer
LEFT JOIN invalids
ON offer.id = invalids.offer_id
WHERE invalids.id IS NULL
GROUP BY offer.dealer_id
ORDER BY offer.created DESC
) o
ON o.product_id = product.id
ORDER BY product.name
I have tried an sqlfiddle http://sqlfiddle.com/#!9/32658/3 with this offer values:
(`id`, `price`, `dealer_id`, `product_id`, `created`)
(1,12.60,1,1,'2015-05-17 08:44:45'),
(2,13.00,1,1,'2015-08-17 08:44:45'),
(3,20.00,1,1,'2015-08-17 08:45:30'),
(4,10.00,1,1,'2015-08-17 08:45:46'),
(5,4.00,2,1,'2015-05-17 08:44:11'),
(6,11.00,2,1,'2015-08-17 08:44:46'),
(7,5.00,2,1,'2015-08-17 08:45:31'),
(9,110.00,2,2,'2015-08-17 08:46:58'),
(10,11.00,2,2,'2015-08-17 08:47:12');
Expected value for product ID 1 is offer ID 7 with price 5.
These steps I think I must realize:
Order offers by created and group by dealer_id to get newest entries
Take result from step 1 and order it by price to get smallest price.
Make this for all products
Maybe I must use a second SELECT FROM offer with GROUP BY and ORDER BY but how do I get I the product_id from the first (outer) select?
Well I would start by getting the latest date for each product offer like this:
SELECT product_id, MAX(created) AS latestOffer
FROM offer
GROUP BY product_id;
Once you have that, you can join it to the original table to get that offer:
SELECT o.*
FROM offer o
JOIN(
SELECT product_id, MAX(created) AS latestOffer
FROM offer
GROUP BY product_id) tmp ON tmp.product_id = o.product_id AND tmp.latestOffer = o.created;
Here is an SQL Fiddle example.
This query should help you:
SELECT *
FROM product
JOIN (
SELECT product_id, min(price) as minPrice, max(created) as newestOffer
FROM offer
WHERE id NOT IN (SELECT offer_id FROM invalids)
GROUP BY 1
) as b
ON product.id = b.product_id
A shot in the dark based on what I understand you to be after...
lots of nested subqueries.. keep thinking there's got to be a better way...
SELECT OO.ID, OO.Price, OO.Dealer_Id, OO.Product_ID, OO.created, P.name
FROM Offer OO
INNER JOIN (
SELECT Min(Price) as MinP
FROM offer O
INNER JOIN (
SELECT max(OI.created) as LatestOffer, OI.Dealer_ID, OI.Product_ID
FROM Offer OI
LEFT JOIN invalids I
on OI.Id = I.offer_Id
WHERE I.ID is null
GROUP BY OI.Dealer_Id, OI.Product_Id
) B
on O.Dealer_Id = B.Dealer_Id
and O.Product_Id = B.Product_Id
and O.Created = B.LatestOffer
) Z
on OO.Price = Z.MinP
INNER JOIN product P
on P.ID = OO.Product_ID
SQL FIDDLE
I'm fairly new to more advanced SQL queries
Given the following tables and associated fields:
Person
PersonId, FirstName, LastName
Order
OrderId, PersonId, OrderDateTime
I want to write a query that will join both tables by PersonId and will retrieve every person and their most recent order.
So if James Doe (PersonId = 1) below has many orders in the orders table,
OrderId, PersonId, OrderDateTime
1 1 12/1/2013 9:01 AM
2 1 2/1/2011 5:01 AM
3 2 10/1/2010 1:10 AM
it will only take the most recent for his.
PersonId NameFirst NameLast OrderId OrderDateTime
1 James Doe 1 12/1/2013 9:01 AM
2 John Doe 3 10/1/2010 1:10 AM
I have been trying something like this
SELECT p.PersonID, o.OrderID, MAX(o.OrderDateTime) From Person p
JOIN Orders o ON p.PersonID = o.PersonID
GROUP BY p.PersonID,
Thanks
The inner query in this solution is a temporary table containing the most recent orders for each person. I join this back to the Orders table to get the fields you want, and then join again to the Person table.
SELECT p.PersonID, p.NameFirst, p.NameLast, o.OrderID, o.OrderDateTime
FROM Person p INNER JOIN Orders o
ON o.PersonId = p.PersonId
INNER JOIN
(
SELECT o1.PersonId, MAX(o1.OrderDateTime) AS maxTime
FROM Orders o1
GROUP BY o1.PersonId
) t
ON o.PersonId = t.PersonId AND o.OrderDateTime = t.maxTime
You can use variables to simulate ROW_NUMBER not available in MySQL:
SELECT p.PersonId, FirstName, LastName,
o.OrderId, o.OrderDateTime
FROM Person AS p
LEFT JOIN (
SELECT OrderId, OrderDateTime, PersonId,
#row_number := IF(#pid <> PersonId,
IF(#pid:=PersonId, 1, 1),
IF(#pid:=PersonId, #row_number+1, #row_number+1)) AS rn
FROM `Order`
CROSS JOIN (SELECT #row_number := 0, #pid := 0) vars
ORDER BY OrderDateTime DESC
) AS o ON p.PersonId = o.PersonId AND o.rn = 1
rn = 1 for the top level record within each PersonId slice of the derived table. Using this predicate in the ON clause of the LEFT JOIN we can match each row of Person to the most recent row of Order and obtain all Order fields.
Demo here
EDIT:
In SQL-Server the query looks like this:
SELECT p.*, o.OrderId, o.OrderDateTime
FROM Person AS p
LEFT JOIN (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY PersonId
ORDER BY OrderDateTime DESC) AS rn
FROM [Order]
) AS o ON p.PersonId = o.PersonId AND o.rn = 1
So the most recent order per every person. You could do something like this:
select p.*, o.*
from person p
inner join orders o on p.personId = o.personId
inner join (
-- get the max order per person
SELECT max(orderId) as orderId, personId
from orders
group by personId
) maxOrder on o.orderId = maxOrder.orderId
joining o onto maxOrder on the orderId filters the result set only to the orders that are also the maximum order per customer.
Note that I used the ID on the table rather than the datetime, as the ID is guaranteed to be unique (for help with joins) - this is not always available depending on the nature of the use in the table, but it looks like it should work for your case.
I have 2 tables:
1) person (person_id, person_name)
2) cars (car_id, person_id)
I want to get all the people's names that have cars with no duplicates.
This is what I have come up with:
SELECT person.person_name, cars.person_id
FROM cars
INNER JOIN person
ON person.person_id=cars.person_id
But I don't want duplicates, so I need to incorporate it using something like this:
SELECT DISTINCT person_id FROM cars
select person_name from person
where person_id in ( select person_id from cars )
SELECT DISTINCT person.person_name, person.person_id
FROM cars
INNER JOIN person
ON person.person_id=cars.person_id
Although there may be more performant alternatives.
SELECT p.person_id AS person_id, p.name AS name FROM person p, cars c
WHERE p.person_id = c.person_id
GROUP BY b.brand_id
SELECT DISTINCT(person.person_name)
FROM person, cars
WHERE person.person_id = cars.person_id
Try this:
SELECT p.person_id, p.person_name
FROM person p
WHERE EXISTS
(
SELECT 1
FROM cars c
WHERE c.person_id = p.person_id
)