SQL Rewriting Tips - mysql

I wrote this query on MySQL:
select a.customer_id,b.first_name,sum(a.amount) as top from payment a
left JOIN customer b on a.customer_id=b.customer_id
group by a.customer_id order by top desc limit 1;
Is there any way I can avoid limit clause and get max(sum(a.amount)) ?
I tried the below but getting invalid group by error ?
select a.customer_id,b.first_name,sum(a.amount) as top from payment a
left JOIN customer b on a.customer_id=b.customer_id
group by a.customer_id having max(sum(a.amount));

I think the limit is better, unless you want to return all the maximum records.
So the statement may like this:
select a.customer_id,b.first_name,sum(a.amount) as top from payment a
left JOIN customer b on a.customer_id=b.customer_id
group by a.customer_id
having sum(a.amount) =
(select max(top1) from
(select a.customer_id,b.first_name,sum(a.amount) as top1 from payment a
left JOIN customer b on a.customer_id=b.customer_id
group by a.customer_id
) as tmp
);
If mysql can support temporary table like mssql, you don't have to repeat your sum twice.

You could do this:
select customer_id, first_name, top from (
select a.customer_id,b.first_name,sum(a.amount) as top from payment a
left JOIN customer b on a.customer_id=b.customer_id
group by a.customer_id
) as t
having top = max(top)
I think the limit is fine though, might be faster.

Related

How to write a SQL query to get sum of transactions?

I want sql query for this . I have three tables customer,account, transaction
the schema of the are as below
I want to get customer details, account id, sum of transaction id
Your query is quite close. Try this:
SELECT c.customer_id, a.Account_id, SUM(t.transaction_amount) as amount
FROM Account a INNER JOIN
Customer c
ON a.customer_id = c.customer_id INNER JOIN
Transaction t
ON a.account_id = t.account_id
GROUP BY c.customer_id, a.account_id;
Note the use of table aliases to simplify the query. And -- more importantly -- the SELECT columns and GROUP BY columns are consistent.
Because the customer_id is in the Account table, you don't need the Customer table. So, you can simplify this to:
SELECT a.customer_id, a.Account_id, SUM(t.transaction_amount) as amount
FROM Account a INNER JOIN
Transaction t
ON a.account_id = t.account_id
GROUP BY a.customer_id, a.account_id;
You can Sum the transactions grouped by CustomerID and then join to the customers table on the CustomerID like this:
SELECT c.Customer_name, c.Customer_mobile, t.Transaction_sum
FROM Customer c
JOIN (
SELECT t.CustomerID, SUM(t.Sum) as Transaction_sum
FROM transactions t
GROUP BY t.CustomerID
) t on t.CustomerID = c.CustomerID

Subquery left join refer to parent ID

I am trying to make a query to fetch the newest car for each user:
select * from users
left join
(select cars.* from cars
where cars.userid=users.userid
order by cars.year desc limit 1) as cars
on cars.userid=users.userid
It looks like it says Unknown column "users.userid" in where clause
I tried to remove cars.userid=users.userid part, but then it only fetches 1 newest car, and sticks it on to each user.
Is there any way to accomplish what I'm after? thanks!!
For this purpose, I usually use row_number():
select *
from users u left join
(select c.* , row_number() over (partition by c.userid order by c.year desc) as seqnum
from cars c
) c
on c.userid = u.userid and c.seqnum = 1;
One option is to filter the left join with a subquery:
select * -- better enumerate the columns here
from users u
left join cars c
on c.userid = u.userid
and c.year = (select max(c1.year) from cars c1 where c1.userid = c.userid)
For performance, consider an index on car(userid, year).
Note that this might return multiple cars per user if you have duplicate (userid, year) in cars. It would be better to have a real date rather than just the year.
Maybe there are better and more efficient way to query this. Here is my solution;
select users.userid, cars.*
from users
left join cars on cars.userid = users.userid
join (SELECT userid, MAX(year) AS maxDate
FROM cars
GROUP BY userid) as sub on cars.year = sub.maxDate;

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.

MySQL query and count from other table

I would like to get the data from one table, and count all results from other table, depending on the first table data, here is what I tried:
SELECT
cars.*, (
SELECT
COUNT(*)
FROM
uploads
WHERE
uploads.cid = cars.customer
) AS `count`,
FROM
`cars`
WHERE
customer = 11;
I dont really have an idea why its not working, as I'm not a regular MySQL user/coder...
Could anyone direct me in the right direction with this one?
SELECT
c.*, COUNT(u.cid) AS count
FROM
cars c
LEFT JOIN
uploads u
ON
u.cid=c.customer
WHERE
u.customer = 11;
GROUP BY c.cid
Try it by joining both tables using LEFT JOIN
SELECT a.customer, COUNT(b.cid) totalCount
FROM cars a
LEFT JOIN uploads b
ON a.customer = b.cid
WHERE a.customer = 11
GROUP BY a.customer
using COUNT(*) in LEFT JOIN will have records to have a minimum count of 1.
SELECT cars.*,COUNT(uploads.*) as uplloaded
from cars
left outer join uploads on uploads.cid = cars.customer
where cars.customer = 11
group by uploads.cid;
Try this :
SELECT customer, COUNT(cid) totalCount
FROM cars
INNER JOIN uploads
ON (customer = cid)
WHERE customer = 11
GROUP BY customer