Change clear SQL to queryBuilder subquery - mysql

Order entity has OneToMany OrderStatsu so any order have many statuses and I want to find all orders where last status = x.
My query look like this (and work)
SELECT o.id, os.status_id FROM orders o
LEFT JOIN(
SELECT *
FROM order_status
GROUP BY orders_id DESC
) AS os
ON o.id = os.orders_id
But I want to get it as object.

First of all GROUP BY orders_id DESC is a wrong syntax. You can change your query like
SELECT o.id, os.status_id
FROM orders o
LEFT JOIN(SELECT status_id, orders_id,
GROUP_CONCAT(statuses ORDER BY statuses DESC) AS Status
FROM order_status
GROUP BY orders_id) os
ON o.id = os.orders_id
WHERE LEFT(os.Status) = 'X';

Related

How do I find the associated name of a max of a sum in a MySQL query?

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

Can you tell me what's wrong in the query I'm using to retrieve info on when a customer last placed an order?

I'm trying to write a query on MySQL workbench that shows me when a customer last ordered from my client's website. I'm using two tables for this purpose. One is called "orders", which is updated with order time whenever a new order is placed and assigns a unique id to each new order. The other table is called "orders_customers_details", which is also updated whenever a new order is placed and contains the unique id (based on a combination of customer's email address and phone number) of the customer placing the order, as well as a key which corresponds to the orders table's id.
My problem is that the query I'm using is not returning every customer's most recent order. In the case of some customers, I'm being given the date of their fourth or fifth most recent order.
I'm left joining orders on orders_customer_details:
left join orders o
on ocd.id = o.customer_details_id
I've also tried using a left outer join, and the following join:
from orders o, orders_customers_details ocd
where o.customer_details_id = ocd.id
In order to retrieve the most recent order only, I'm grouping by customer_id HAVING max(order.id). FYI, order.id increases with the placement of each new order, so the order with the highest id is the most recent order.
I've also tried
SELECT customer_id, MAX(order.id)
and then grouped by customer_id, but to no avail.
Here's the entire code:
select customer_id, o.id as id_of_last_order, date(order_date) as
last_ordered, timestampdiff(day, order_date, now()) as
days_since_last_ordered
from orders o, orders_customers_details ocd
where o.customer_details_id = ocd.id
group by customer_id having max(o.id)
order by customer_id;
A typical method in MySQL is to use a correlated subquery to get the most recent order:
select ocd.customer_id, o.id as id_of_last_order,
date(o.order_date) as last_ordered,
timestampdiff(day, o.order_date, now()) as days_since_last_ordered
from orders o join
orders_customers_details ocd
on o.customer_details_id = ocd.id
where o.order_date = (select max(o2.order_date)
from orders o2 join
orders_customers_details ocd2
on o2.customer_details_id = ocd2.id
where oc2.customer_id = ocd.customer_id
)
order by ocd.customer_id;
Of course, if o.id is an auto-incrementing column, then the largest value is from the most recent date. If this is the case, then you can just use aggregation:
select ocd.customer_id,
max(o.id) as id_of_last_order,
date(max(o.order_date)) as last_ordered,
timestampdiff(day, max(o.order_date), now()) as days_since_last_ordered
from orders o join
orders_customers_details ocd
on o.customer_details_id = ocd.id
group by ocd.customer_id
order by ocd.customer_id;
In MySQL 8+, you would simply use window functions:
select *
from (select ocd.customer_id, o.id as id_of_last_order,
date(o.order_date) as last_ordered,
timestampdiff(day, o.order_date, now()) as days_since_last_ordered,
row_number() over (partition by ocd.customer_id order by o.order_date desc) as seqnum
from orders o join
orders_customers_details ocd
on o.customer_details_id = ocd.id
) ocd
where seqnum = 1
order by ocd.customer_id;
Using a correlated sub query in the where clause to find the most recent order by customer and tidying up the code then something like this
select customer_id, o.id as id_of_last_order, date(order_date) as last_ordered,
timestampdiff(day, order_date, now()) as days_since_last_ordered
from orders_customers_details ocd
join orders o on o.customer_details_id = ocd.id
where ocd_id = (select max(ocd_id) from orders_customers_details ocd1 where ocd1.customer_id = ocd_customer_id);
Though I cannot be certain without table definitions sample data etc..
Try this-
SELECT
customer_details_id customer_id,
MAX(o.id) AS id_of_last_order,
MAX(order_date) AS last_ordered,
TIMESTAMPDIFF(DAY, MAX(order_date), NOW()) AS days_since_last_ordered
FROM orders O
INNER JOIN orders_customers_details OCD
ON O.customer_details_id = OCD.id
GROUP BY customer_details_id
ORDER BY customer_details_id;

How to get first value from this sql query

Im trying to get by subquery clientId of customer with most orders but only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
SELECT a.ClientName
FROM Clients as a
INNER JOIN Orders as b
ON a.Id=b.ClientId
WHERE b.ClientId
IN(SELECT b.ClientId,COUNT( b.ClientId) as MAKS FROM Orders as b
GROUP BY b.ClientId ORDER BY MAKS DESC)
Do we have some tools to handle this and how can i optimize this query? Thanks in advance.
You don't really need the inner join because you are asking for an ID that is the same in both tables,
SELECT ClientName FROM Clients
WHERE Id = (SELECT TOP 1 ClientId FROM Orders
GROUP BY ClientId
ORDER BY COUNT(ClientId) DESC)
Using Top 1, Count and Group By
SQL Server
SELECT Top 1 a.ClientName , count(b.orders_id) TotalOrders
FROM Clients as a
INNER JOIN Orders as b
ON a.Id=b.ClientId
GROUP BY a.client_name
order by TotalOrders desc
MySQL
SELECT a.ClientName , count(b.orders_id) TotalOrders
FROM Clients as a
INNER JOIN Orders as b
ON a.Id=b.ClientId
GROUP BY a.client_name
order by TotalOrders desc
LIMIT 1

MySQL Order By in Right Join Clause

I'm trying to do a right join in MySQL like so:
SELECT customers.id,customers.firstname,customers.lastname,customers.email,orders.time,orders.notes,pendings.date_updated,pendings.issue,appointments.closed,appointments.job_description,backup_plans.expiration FROM customers
RIGHT JOIN orders
ON customers.id = orders.customer_id
ORDER BY orders.time DESC LIMIT 1
RIGHT JOIN pendings
ON customers.id = pendings.customer_id
ORDER BY pendings.date_updated DESC LIMIT 1
RIGHT JOIN appointments
ON customers.id = appointments.customer_id
ORDER BY appointments.closed DESC LIMIT 1
RIGHT JOIN backup_plans
ON customers.id = backup_plans.customer_id
ORDER BY backup_plans.expiration DESC LIMIT 1
My intent is this: to select customers' name and email, along with the most recent order, pending, appointment, and backup plan exploration. When I execute this I get a syntax error:
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'RIGHT JOIN pendings
ON customers.id = pendings.customer_id
ORDER BY pendings.d' at line 5
I'm unfamiliar with joins and would appreciate any help.
EDIT 1:
It seems that I need to make a subquery per DanK's suggestion like so:
SELECT customers.id,customers.firstname,customers.lastname,customers.email,orderstmp.time,orderstmp.notes FROM customers
RIGHT JOIN (
SELECT orders.time,orders.notes,orders.customer_id FROM orders ORDER BY orders.time DESC LIMIT 1
) as orderstmp ON orderstmp.customer_id = customers.id
But when I do this, I only get one row result, whereas I want all the customer information.
EDIT 2:
Per Tom H's suggestion, I've built this query:
SELECT
customers.id,
SQ_O.time,
SQ_O.notes
FROM customers
LEFT JOIN (
SELECT
customers.id,
orders.time,
orders.notes
FROM customers
LEFT JOIN orders ON orders.customer_id = customers.id
ORDER BY orders.time DESC LIMIT 1
) AS SQ_O ON SQ_O.id = customers.id
which has all blank time and notes fields
and
SELECT
customers.id,
O1.time,
O1.notes
FROM customers
LEFT JOIN orders AS O1 ON O1.customer_id = O1.id
LEFT JOIN orders AS O2 ON O2.customer_id = customers.id AND O2.time > O1.time WHERE O2.customer_id IS NULL
Which reaches max execution time. I'm guessing this is due to my lack of familiarity with what's possible in MySQL in comparison to other dialects.
I also tried Correlated subqueries like this:
SELECT
customers.firstname,
customers.lastname,
customers.email,
(
SELECT CONCAT(orders.time,': ',orders.notes)
FROM orders
WHERE orders.customer_id = customers.id
ORDER BY orders.time DESC LIMIT 1
) as last_order
FROM customers
But the "last_order" column comes up blank.
FINAL, DISAPPOINTING EDIT
After trying a number of really stellar suggestions that helped me learn SQL significantly, I decided to write a PHP script to get me what I want. The project's under a bit of a deadline so whatever works, works. Thanks everyone!
You can only have one ORDER BY statement per query. You can of course use subqueries and refer to a result set as a virtual table but ultimately in a single SELECT you can only have one ORDER BY.
For instance:
SELECT something
FROM table
ORDER BY something -- One order By
With a subquery as a virtual table:
SELECT something
FROM (SELECT anotherthing, something
FROM table
ORDER BY anotherthing) -- this is an order by in a separate select statement..
ORDER BY something -- still only one Order by
------EDIT--------
For assistance with your join syntax, try something like this:
SELECT --fields,
FROM customers
RIGHT JOIN orders ON customers.id = orders.customer_id
RIGHT JOIN pendings ON customers.id = pendings.customer_id
RIGHT JOIN appointments ON customers.id = appointments.customer_id
RIGHT JOIN backup_plans ON customers.id = backup_plans.customer_id
ORDER BY orders.time DESC, pendings.date_updated DESC, appointments.closed DESC, backup_plans.expiration DESC
LIMIT 1
Try this:
SELECT customers.id,customers.firstname,customers.lastname,customers.email,orders.time,orders.notes,pendings.date_updated,pendings.issue,appointments.closed,appointments.job_description,backup_plans.expiration FROM customers
RIGHT JOIN orders
ON customers.id = orders.customer_id
RIGHT JOIN pendings
ON customers.id = pendings.customer_id
RIGHT JOIN appointments
ON customers.id = appointments.customer_id
RIGHT JOIN backup_plans
ON customers.id = backup_plans.customer_id
ORDER BY orders.time DESC, pendings.date_updated DESC, appointments.closed DESC, backup_plans.expiration DESC LIMIT 1
You can accomplish this through subqueries or with additional JOINs. Here's an example of each. (NOTE: I use SQL Server, so it's possible that some of the syntax that I'm used to isn't supported in the same way in MySQL). I'm only doing these example with the Orders, but hopefully you can extend the ideas to the other tables.
Using subqueries:
SELECT
C.id,
SQ_O.time,
SQ_O.notes
FROM
Customers C
LEFT OUTER JOIN
(
SELECT
C2.Customer_ID,
O.time,
O.notes
FROM
Customers C2
LEFT OUTER JOIN Orders O ON O.customer_id = C2.id
ORDER BY
O.time DESC LIMIT 1
) SQ_O ON SQ_O.customer_id = C.id
Using multiple JOINs:
SELECT
C.id,
O1.time,
O1.notes
FROM
Customers C
LEFT OUTER JOIN Orders O1 ON O1.customer_id = C.id
LEFT OUTER JOIN Orders O2 ON O2.customer_id = C.id AND O2.time > O1.time
WHERE
O2.customer_id IS NULL -- Basically we're excluding any rows where another order was found with a later time than O1
If exact matches in Orders.time are possible than you'll need additional criteria on which one to choose.
As long as you can rely on no customer having their two most recent orders having the same time, this should work:
SELECT c.firstname, c.lastname, c.email, o.*
FROM customers AS c
LEFT JOIN (
SELECT customer_id, MAX(`time`) AS maxTime
FROM orders
GROUP BY customer_id
) AS lastO ON c.id = lastO.customer_id
LEFT JOIN orders AS o
ON lastO.customer_id = o.customer_id
AND lastO.maxTime = o.`time`
;
As long as the other tables can also be relied upon to have only one MAX value per customer, you should be able to append similar JOINs for them. The issue with multiple of the same "last" time\date_updated\closed\etc.. for a customer is that they will multiply results. For example, pairs of the same time in orders and pairs of date_updated in pending on the same customer will result in 4 rows instead of two as every "last" row for that customer in orders is paired up with every "last" row in pending.

JOIN VS SUBQUERY

I need to do this but with a subquery, not a join. My problem is, how can I use a subquery to display another column? I could grab the info from there, but I'll be missing the order_date column from the orders table. Can I use a subquery to display it?
SELECT CONCAT(c.customer_first_name, ' ' , c.customer_last_name) AS customer_name, MAX(o.order_date) AS recent_order_date
FROM customers AS c
JOIN orders AS o
ON c.customer_id = o.customer_id
GROUP BY customer_name
ORDER BY MAX(o.order_date) DESC
It's not at all clear what resultset you are trying to return, but it looks an awful like the like the ubiquitous "latest row" problem.
The normative pattern for the solution to that problem is to use a JOIN to the inline view. If there's not a unique constraint, you run the possibility of returning more than one matching row.
To get the latest order (the row in the orders table with the maximum order_date for each customer, assuming that the (customer_id, order_date) tuple is unique, you can do something like this:
SELECT o.*
FROM ( SELECT n.customer_id
, MAX(n.order_date) AS latest_order_date
FROM orders n
GROUP BY n.customer_id
) m
JOIN orders o
ON o.customer_id = m.customer_id
AND o.order_date = m.latest_order_date
If you want to also retrieve columns from the customers table based on the customer_id returned from orders, you'd use a JOIN (not a subquery)
SELECT CONCAT(c.customer_first_name,' ',c.customer_last_name) AS customer_name
, c.whatever
, o.order_date AS recent_order_date
, o.whatever
FROM ( SELECT n.customer_id
, MAX(n.order_date) AS latest_order_date
FROM orders n
GROUP BY n.customer_id
) m
JOIN orders o
ON o.customer_id = m.customer_id
AND o.order_date = m.latest_order_date
JOIN customers c
ON c.customer_id = o.customer_id
ORDER BY o.order_date DESC, o.customer_id DESC
As I mentioned before, if a given customer can have two orders with the exact same value for order_date, there's potential to return more than one order for each customer_id.
To rectify that, we can return a unique key from the inline view, and use that in the join predicate to guarantee only a single row returned from orders.
(NOTE: this approach is specific to MySQL, with this syntax, other RDBMS will throw an error that essentially says "the GROUP BY must include all non-aggregates". But MySQL allows it.)
SELECT CONCAT(c.customer_first_name,' ',c.customer_last_name) AS customer_name
, c.whatever
, o.order_date AS recent_order_date
, o.whatever
FROM ( SELECT n.customer_id
, MAX(n.order_date) AS latest_order_date
, n.order_id
FROM orders n
GROUP BY n.customer_id
) m
JOIN orders o
AND o.customer_id = m.customer_id
AND o.order_date = m.latest_order_date
AND o.order_id = n.order_id
JOIN customers c
ON c.customer_id = o.customer_id
ORDER BY o.order_date DESC, o.customer_id DESC
I am not really sure i understand your question, but i think this works... (not tested though...)
SELECT
(
SELECT
CONCAT(c.customer_first_name, ' ' , c.customer_last_name)
FROM
customers c
WHERE
c.customer_id = o.customer_id
LIMIT 1
) AS customer_name,
MAX(o.order_date) AS recent_order_date
FROM
orders o
GROUP BY
customer_name
ORDER BY
MAX(o.order_date) DESC