SELECT last entry for each user - mysql

I have a TABLE user_orders with fields -->
order_id int(10),
user_id int(10),
order_payment_id int(10)
order_created int(10)
I want to find the last order_payment_id used for each user_id. I try
SELECT order_payment_id, user_id , MAX(order_id) as lastone
FROM user_orders
GROUP BY user_id;
but the result is wrong

You can use a self-join to filter out the relevant data
SELECT o1.*
FROM user_orders o1
JOIN
(
SELECT user_id, MAX(order_created) as lastone
FROM user_orders
GROUP BY user_id
) o2 on o1.user_id = o2.user_id
and o1.order_created = o2.lastone
GROUP BY o1.user_id

The general GROUP BY rule is:
If a GROUP BY clause is specified, each column reference in the SELECT list must either identify a grouping column or be the argument of a set function.
SELECT order_payment_id, user_id , order_id as lastone
FROM user_orders uo
WHERE order_id = (select MAX(order_id) from user_orders
where user_id = uo.user_id)

The MAX should be in the WHERE clause, or it will select the maximum order_id for the whole table.
SELECT order_payment_id, user_id ,order_id as lastone
FROM user_orders u1
WHERE u1.order_id = (SELECT MAX(order_id)
FROM user_orders u2
WHERE u1.user_id = u2.user_id);
Using this query, you can get rid of the GROUP BY except if order_id is not unique.
That way you'll get the last order associated with the user.

Related

How to run where exists with distinct faster

I have a query with WHERE EXISTS and DISTINCT. I just want to find distinct emails with same order_id
How can I make this faster
select DISTINCT(user_email)
from (
SELECT *
FROM orders mto
WHERE EXISTS
(
SELECT 1
FROM orders mti
WHERE mti.order_id = mto.order_id
LIMIT 1, 1
)
) as z
INNER JOIN marketplaces as u ON z.ump_id = u.id
You are doing excess/needless work - the EXISTS will always return TRUE in your situation.
If you want a list of all (unique) emails which have at least 1 order, then
SELECT
user_email
FROM
marketplaces
LEFT JOIN
orders ON ump_id = marketplaces.id
WHERE orders.id IS NOT NULL
GROUP BY user_email
If you want to get a list with all (unique) emails for a given order ID, then
SELECT
user_email
FROM
orders
LEFT JOIN
orders ON ump_id = marketplaces.id
WHERE orders.id = 17
GROUP BY user_email

MySQL query only returns value if rows exist

I have the following query which works perfectly, but only if each select finds a row.
I've attempted to add IFNULL to return 0 if no rows were found but I'm still not getting the correct return.
SELECT IFNULL(paid_value,0)-IFNULL(ordered_value,0)+IFNULL(credit_value,0) AS account_balance
FROM
(
SELECT customer_id, SUM(order_total) AS ordered_value
FROM orders
WHERE customer_id = '1'
GROUP BY customer_id
) AS orders
LEFT JOIN
(
SELECT customer_id, SUM(amount) AS paid_value
FROM transactions
WHERE customer_id = '1'
GROUP BY customer_id
) as payments
ON orders.customer_id = payments.customer_id
LEFT JOIN
(
SELECT customer_id, SUM(amount) AS credit_value
FROM credits
WHERE customer_id = '1'
GROUP BY customer_id
) as credits
ON orders.customer_id = credits.customer_id
This query currently returns empty, it's not returning NULL or 0.
When I run
SELECT customer_id, SUM(order_total) AS ordered_value
FROM orders
WHERE customer_id = '1'
GROUP BY customer_id
It also returns empty, not NULL or 0, unless there's a row. In order for the full query to work, each of the 3 separate queries need to have a row in them.
Any ideas?
It if because none of the columns have a result set, so an empty result set is returned, if you want to always display a row in any case you can try with some tricks like this one for example :
SELECT IFNULL(SUM(payments.paid_value),0)-IFNULL(SUM(orders .ordered_value),0)+IFNULL(SUM(credits.credit_value),0) AS account_balance
FROM
(SELECT 1 AS idx, 0 AS paid_value, 0 AS ordered_value, 0 AS credit_value) a
LEFT JOIN
(
SELECT 1 AS idx, customer_id, SUM(order_total) AS ordered_value
FROM orders
WHERE customer_id = '1'
GROUP BY customer_id
) AS orders
ON a.idx = orders.idx
LEFT JOIN
(
SELECT customer_id, SUM(amount) AS paid_value
FROM transactions
WHERE customer_id = '1'
GROUP BY customer_id
) as payments
ON orders.customer_id = payments.customer_id
LEFT JOIN
(
SELECT customer_id, SUM(amount) AS credit_value
FROM credits
WHERE customer_id = '1'
GROUP BY customer_id
) as credits
ON orders.customer_id = credits.customer_id
GROUP BY a.idx
The example is a proof of concept that can be adapted even to other situations where you need to always returns a row with default values even with no elements in underlying tables .
If you want row event if there is no data available, then I suppose you should either have UNION. Or In this scenario you can include you Customer Table & put each Table on Right Side, make a LEFT JOIN.
I think following select will give you an IDEA.
SELECT O.customer_id, SUM(O.order_total) AS ordered_value
FROM Customers C
left join orders O on C.ID =o.customer_id
WHERE O.customer_id = '1'
GROUP BY O.customer_id

MySQL: SELECT if non of group by members is equal to x

I have troubles getting proper data.
I have table structure like:
id INT(11) AI
order_id INT(11)
status varchar(45)
This table log status changes for orders.
So order_id's will have few statuses.
Now I need to select rows and group them by order_id, where order never had status (not even one status with given order_id) != 'example'
We don't show orders, where one of members had status = example
Sample data
1 12 ready
1 12 example
2 13 ready
2 13 sent
So I don't want order 12 to show at all, because one of it members have "example" status
I've tried grouping results, but it's not enough.
you can do it by simple join query :
select a.order_id
from ordrstatus as a left outer join (select orderid , count(*) as status from orderstatus where status = 'example' group by orderid) as b on a.orderid = b.orderid
where b.status = 0 or b.status is NUll
Join query always run faster then IN query . by using Join in query it will run only one time .
You can try like this...it will return all order id which never had status -example
Select
Order_id,
from TableName A where Not Exists(
Select id from TableName B where
status='example' and
a.Order_id=b.Order_id
)
group by Order_id
Not quite sure if you want the records for order which have had a status of example, or ones which have never had a status of example
To get a list of orders (with the status grouped up) which have had a status of example:-
SELECT a.order_id, GROUP_CONCAT(a.status)
FROM SomeTable a
INNER JOIN
(
SELECT order_id, COUNT(*)
FROM SomeTable
WHERE status = 'example'
GROUP BY order_id
) b
ON a.order_id = b.order_id
GROUP BY order_id
To get those which have NEVER had a status of exmaple
SELECT a.order_id, GROUP_CONCAT(a.status)
FROM SomeTable a
LEFT OUTER JOIN
(
SELECT order_id, COUNT(*)
FROM SomeTable
WHERE status = 'example'
GROUP BY order_id
) b
ON a.order_id = b.order_id
WHERE b.order_id IS NULL
GROUP BY order_id
EDIT
SELECT a.order_id, GROUP_CONCAT(a.status)
FROM SomeTable a -- Statuses
LEFT OUTER JOIN
(
SELECT order_id, COUNT(*)
FROM SomeTable
WHERE status = 'example'
GROUP BY order_id
) b -- Get any order id which has had a status of example (as a LEFT JOIN)
ON a.order_id = b.order_id
INNER JOIN
(
SELECT order_id, MAX(id) AS Latestid
FROM SomeTable
GROUP BY order_id
) c -- Get the latest status for each order (ie, max id)
ON a.order_id = c.order_id
LEFT OUTER JOIN
(
SELECT order_id, id
FROM SomeTable
WHERE status = 'example2'
) d -- Get the id of the order status of example2
ON a.order_id = d.order_id AND c.Latestid = d.id -- join on the same order id and that the record id matches the latest record id
WHERE b.order_id IS NULL -- reject those where a match was found on example for any status
AND d.order_id IS NULL -- reject those where a match was found on example2 for the latest status
GROUP BY order_id
try this
SELECT Order_ID FROM tbl_Orders
WHERE Status NOT IN ('example')
GROUP BY Order_ID
SELECT DISTINCT x.order_id
FROM order_status x
LEFT
JOIN order_status y
ON y.order_id = x.order_id
AND y.status = 'example'
WHERE y.id IS NULL;

sql select the product with most users

users:
uid int(11) - userid(primary key, auto_increment)
name varchar(255)
pass varchar(64)
created int(11)
projects:
pid int(11) .....
name varchar(150)
description varchar(255)
created int(11)
users_projects:
uid int(11) - user id
pid int(11) - product id
How can i select the project with the most ussers assigned to it?
sql query.
You could use something like this:
select p.pid,
p.name,
up.TotalUsers
from projects p
inner join
(
select pid, count(uid) TotalUsers
from users_projects
group by pid
) up
on p.pid = up.pid
order by TotalUsers Desc
-- limit 1
See SQL Fiddle with Demo
This will return the list of all projects and a count of the Total Users per project. If you want to return the project will the most users, then you will include the limit 1 that is commented out.
If you have more than one project that has the same number of users, then you would want to use something similar to this:
select p.pid,
p.name,
up.TotalUsers
from projects p
inner join
(
select pid, count(uid) TotalUsers
from users_projects
group by pid
) up
on p.pid = up.pid
where totalusers = (select count(*) Total
from users_projects
group by pid
order by total desc
limit 1)
See SQL Fiddle with Demo
Thanks #JW for the Fiddle
The following query will include multiple projects having the same number of users and happens to be the most number of users.
SELECT a.name userName, c.name ProjectName
FROM users a
INNER JOIN users_projects b
ON a.uid = b.uid
INNER JOIN projects c
ON b.pid = c.pid
INNER JOIN
(
SELECT pid, COUNT(*) totalCount
FROM users_projects
GROUP BY pid
HAVING COUNT(*) = (SELECT COUNT(*) x
FROM users_projects
GROUP BY pid
ORDER BY x DESC
LIMIT 1)
) d ON b.pid = d.pid
ORDER BY a.Name ASC
SQLFiddle Demo (with duplicate project)
SQLFiddle Demo
If you are looking for only one project, the following is the fastest way:
select project_id, count(*) as NumUsers
from user_projects
group by project_id
order by count(*) desc
limit 1
(I am assuming that "product" = "project").

MySQL group by last entry

My table payment_status have these fields:
id
payment_id
status
created
created_by
Many entries could have the same payment_id... So, I want to get the last status for one payment_id...
I have this request that work but take too much time to load... I would like to have an optimize version to group by payment_id and take the last status.
SELECT pstatus.*
FROM `payment_status` AS pstatus
WHERE pstatus.id = (
SELECT id
FROM `payment_status`
WHERE pstatus.status = '200'
ORDER BY created DESC
LIMIT 1
)
GROUP BY pstatus.payment_id
ORDER BY pstatus.payment_id DESC
Try this query -
SELECT t1.* FROM payment_status t1
JOIN (SELECT payment_id, MAX(created) max_created
FROM payment_status
GROUP BY payment_id
) t2
ON t1.payment_id = t2.payment_id AND t1.created = t2.max_created;
...then add WHERE conditions you need.
Try to use JOIN:
SELECT p1.*
FROM payment_status p1
INNER JOIN
(
SELECT id, MAX(created) MaxCreated
FROM payment_status
WHERE status = '200'
GROUP BY id
) p2 ON p1.id = p2.id AND p1.created = p2.MaxCreated
ORDER BY p1.payment_id DESC
this should work
SELECT *
FROM payment_status
WHERE status = '200'
ORDER BY created DESC
LIMIT 1
shouldn't yo ujust be able to do this?:
(assuming that created is the timestamp, so "last" = "most recent")
SELECT pstatus.*
FROM `payment_status` AS pstatus
GROUP BY pstatus.payment_id, pstatus.status
ORDER BY pstatus.payment_id DESC, pstatus.created DESC
each row returns should have the payment_id with the most recent status.
I suppose you are using MySQL database.
Here I have a solution is fast and readable:
select
substring_index(
group_concat(status order by created desc)
, ',', 1
) as status_latest
from payment_status
group by payment_id
I'm quite sure it's fast than others SQL statement,
You may try it.