I am some hours trying to make a query, but I haven't sucess! :(
With this query I select users_id and purchase_date of users that bought by key:
SELECT user_id, purchase_date FROM purchases AS p
INNER JOIN keys AS pc
ON p.transaction_id = pc.unique_id
WHERE pc.generator_id = 96768
GROUP BY user_id
Now I want select from these users who bought after this.
Eg.: SELECT * FROM purchases WHERE user_id = {users of query above} AND purchase_date > {purchase_date of query above}
You can join a table with a subquery:
SELECT pr.*
FROM purchases pr
INNER JOIN (
SELECT p.user_id, p.purchase_date
FROM purchases p
INNER JOIN keys pc ON p.transaction_id = pc.unique_id
WHERE pc.generator_id = 96768
GROUP BY p.user_id
) p0 ON pr.user_id = p0.user_id AND pr.purchase_date > p0.purchase_date
Info: MySQL JOIN Syntax
Related
In MySQL I have 4 tables:
- product(id)
- order(id)
- order_detail_1(id, product_id, order_id, qty)
- order_detail_2(id, product_id, order_id, qty)
I want to get the sum of the quantity of products sold from the 2 tables (order_detail_1, order_detail_2) grouping them by product
produt can existe in order_detail_1 and not in order_detail_2 and vice versa
i tested this query and it worked but I want a simpler query without the union and the subquery.
select tmp.product_id ,sum(tmp.qty) from
(
(
select order_detail_1.product_id ,sum(order_detail_1.qty)
from order_detail_1
inner join order on order_detail_1.id_order = order.id
where order_detail_1.product_id is not null
group by order_detail_1.product_id
)
union all
(
select order_detail_2.product_id ,sum(order_detail_2.qty)
from order_detail_2
inner join order on order_detail_2.id_order = order.id
where order_detail_2.product_id is not null
group by order_detail_2.product_id
)
) tmp
group by tmp.product_id
It looks like you're not using order table other then checking if it exists, so you can use EXISTS()
SELECT p.product_id,sum(p.qty) as qty
FROM (SELECT product_id,qty,id_order FROM order_detail_1
WHERE product_id IS NOT NULL
UNION ALL
SELECT product_id,qty,id_order FROM order_detail_2
WHERE product_id IS NOT NULL) p
WHERE EXISTS(SELECT 1 FROM order o
WHERE o.id = p.id_order)
GROUP BY p.product_id
If a product is in only one table, you can use left join:
select p.id, (coalesce(sum(od1.qty), 0) + coalesce(sum(od2.qty, 0))) as qty
from product p left join
order_detail_1 od1
on od1.product_id = p.id left join
order_detail_2 od2
on od2.product_id = p.id
group by p.id;
This formulation depends on the fact that the two tables are exclusion -- a product is in only one table.
EDIT:
If products can exist in both tables, then you need to aggregate them first:
select p.id, (coalesce(od1.qty, 0) + coalesce(od2.qty, 0)) as qty
from product p left join
(select product_id, sum(qty) as qty
from order_detail_1 od1
group by product_id
) od1
on od1.product_id = p.id left join
(select product_id, sum(qty) as qty
from order_detail_2 od2
group by product_id
) od2
on od2.product_id = p.id;
One table is Users with id and email columns.
Another table is Payments with id, created_at, user_id and foo columns.
User has many Payments.
I need a query that returns each user's email, his last payment date and this last payment's foo value. How do I do that? What I have now is:
SELECT users.email, MAX(payments.created_at), payments.foo
FROM users
JOIN payments ON payments.user_id = users.id
GROUP BY users.id
This is wrong, because foo value does not necessarily belong to user's most recent payment.
Try this :
select users.email,foo,create_at
from users
left join(
select a.* from payments a
inner join (
select id,user_id,max(create_at)
from payments
group by id,user_id
)b on a.id = b.id
) payments on users.id = payments.user_id
If users has no payment yet, then foo and create_at would return NULL. if you want to exclude users who has no payment, then use INNER JOIN.
One approach would be to use a MySQL version of rank over partition and then select only those rows with rank = 1:
select tt.email,tt.created_at,tt.foo from (
select t.*,
case when #cur_id = t.id then #r:=#r+1 else #r:=1 end as rank,
#cur_id := t.id
from (
SELECT users.id,users.email, payments.created_at, payments.foo
FROM users
JOIN payments ON payments.user_id = users.id
order by users.id asc,payments.created_at desc
) t
JOIN (select #cur_id:=-1,#r:=0) r
) tt
where tt.rank =1;
This would save hitting the payments table twice. Could be slower though. Depends on your data!
I have three tables, users, activities and purchases.
Users has many activities and purchases, activities has 4 types.
I need to query users like this:
[
{
user_id: 1,
// from activities
post_count: 2,
updated_count: 3,
print_count: 4,
share_count: 5,
// from purchases
purchase_count: 6
},
...
]
I use this sql:
SELECT u.id, post.post_count, updated.update_count, print.print_count, share.share_count, purchase.purchase_count
FROM users as u
LEFT JOIN (
SELECT user_id, activity_type, count(*) as post_count
FROM activities
WHERE activity_type = 1
GROUP BY user_id
) post
ON u.id = post.user_id
LEFT JOIN (
SELECT user_id, activity_type, count(*) as update_count
FROM activities
WHERE activity_type = 2
GROUP BY user_id
) updated
ON u.id = updated.user_id
LEFT JOIN (
SELECT user_id, activity_type, count(*) as print_count
FROM activities
WHERE activity_type = 3
GROUP BY user_id
) print
ON u.id = print.user_id
LEFT JOIN (
SELECT user_id, activity_type, count(*) as share_count
FROM activities
WHERE activity_type = 4
GROUP BY user_id
) share
ON u.id = share.user_id
LEFT JOIN (
SELECT user_id, count(*) AS purchase_count
FROM purchases
GROUP BY user_id
) purchase
ON u.id = purchase.user_id
how can i optimize performance with this query?
Great thanks to Eugen Rieck
I modified his query to this, then it works.
SELECT
users.id AS user_id,
SUM(IF((activities.activity_type=1),1,0)) AS post_count,
SUM(IF((activities.activity_type=2),1,0)) AS update_count,
SUM(IF((activities.activity_type=3),1,0)) AS print_count,
SUM(IF((activities.activity_type=4),1,0)) AS share_count,
IFNULL(purchase.count,0) AS purchase_count
FROM
users
LEFT JOIN activities ON activities.user_id=users.id
LEFT JOIN (
SELECT user_id, count(*) AS count
FROM purchases
GROUP BY user_id
) purchase
ON users.id = purchase.user_id
GROUP BY users.id
Currently you run the activities table 4 times - this could be folded into one:
SELECT
users.id AS user_id,
SUM(IF(activites.activity_type=1,1,0)) AS post_count,
SUM(IF(activites.activity_type=2,1,0)) AS update_count,
SUM(IF(activites.activity_type=3,1,0)) AS print_count,
SUM(IF(activites.activity_type=4,1,0)) AS share_count,
IFNULL(COUNT(purchases.id),0) AS purchase_count
FROM
users
INNER JOIN activities ON activities.user_id=users.id
LEFT JOIN purchases ON purchases.user_id=users.id
GROUP BY users.id
I am trying to follow the instructions in this answer how to get the latest row in the joined table.
I have two tables.
Projects : id, title
Status : project_id, status_id, created(DATETIME)
When I know the project ID (example = 2) I have the correct query to select the latest status update.
SELECT projects. * , project_state_project_map.status_id AS status,
project_state_project_map.created AS status_created
FROM projects
LEFT JOIN (
SELECT *
FROM project_state_project_map
WHERE project_id = 2
ORDER BY created DESC
LIMIT 1
)
project_state_project_map ON project_state_project_map.project_id = projects.id
WHERE projects.id = 2
LIMIT 1
However, I cannot figure out how to select all projects with their current status. What do I have to change to the sql to get all projects with their latest states.
I would suggest altering your query to use an aggregate function to get the latest date with status:
SELECT p. *,
pm1.status_id AS status,
pm1.created AS status_created
FROM projects p
LEFT JOIN project_state_project_map pm1
ON pm1.project_id = p.id
INNER JOIN
(
SELECT max(created) MaxDate, project_id
FROM project_state_project_map
WHERE project_id = 2
GROUP BY project_id
) pm2
ON pm1.project_id = pm2.project_id
AND pm1.created = pm2.MaxDate
WHERE p.id = 2
This gets the max(created) date for each project, then this result is used to return the status with that date.
This could also be written as:
SELECT p. *,
pm.status_id AS status,
pm.created AS status_created
FROM projects p
LEFT JOIN
(
SELECT pm1.project_id,
pm1.status_id,
pm1.created
FROM project_state_project_map pm1
INNER JOIN
(
SELECT max(created) MaxDate, project_id
FROM project_state_project_map
WHERE project_id = 2
GROUP BY project_id
) pm2
ON pm1.project_id = pm2.project_id
AND pm1.created = pm2.MaxDate
) pm
ON pm.project_id = p.id
WHERE p.id = 2;
I have a users table and a payments table, for each user, those of which have payments, may have multiple associated payments in the payments table. I would like to select all users who have payments, but only select their latest payment. I'm trying this SQL but i've never tried nested SQL statements before so I want to know what i'm doing wrong. Appreciate the help
SELECT u.*
FROM users AS u
INNER JOIN (
SELECT p.*
FROM payments AS p
ORDER BY date DESC
LIMIT 1
)
ON p.user_id = u.id
WHERE u.package = 1
You need to have a subquery to get their latest date per user ID.
SELECT u.*, p.*
FROM users u
INNER JOIN payments p
ON u.id = p.user_ID
INNER JOIN
(
SELECT user_ID, MAX(date) maxDate
FROM payments
GROUP BY user_ID
) b ON p.user_ID = b.user_ID AND
p.date = b.maxDate
WHERE u.package = 1
SELECT u.*, p.*
FROM users AS u
INNER JOIN payments AS p ON p.id = (
SELECT id
FROM payments AS p2
WHERE p2.user_id = u.id
ORDER BY date DESC
LIMIT 1
)
Or
SELECT u.*, p.*
FROM users AS u
INNER JOIN payments AS p ON p.user_id = u.id
WHERE NOT EXISTS (
SELECT 1
FROM payments AS p2
WHERE
p2.user_id = p.user_id AND
(p2.date > p.date OR (p2.date = p.date AND p2.id > p.id))
)
These solutions are better than the accepted answer because they work correctly when there are multiple payments with same user and date. You can try on SQL Fiddle.
SELECT u.*, p.*, max(p.date)
FROM payments p
JOIN users u ON u.id=p.user_id AND u.package = 1
GROUP BY u.id
ORDER BY p.date DESC
Check out this sqlfiddle
SELECT u.*
FROM users AS u
INNER JOIN (
SELECT p.*,
#num := if(#id = user_id, #num + 1, 1) as row_number,
#id := user_id as tmp
FROM payments AS p,
(SELECT #num := 0) x,
(SELECT #id := 0) y
ORDER BY p.user_id ASC, date DESC)
ON (p.user_id = u.id) and (p.row_number=1)
WHERE u.package = 1
You can try this:
SELECT u.*, p.*
FROM users AS u LEFT JOIN (
SELECT *, ROW_NUMBER() OVER(PARTITION BY userid ORDER BY [Date] DESC) AS RowNo
FROM payments
) AS p ON u.userid = p.userid AND p.RowNo=1
There are two problems with your query:
Every table and subquery needs a name, so you have to name the subquery INNER JOIN (SELECT ...) AS p ON ....
The subquery as you have it only returns one row period, but you actually want one row for each user. For that you need one query to get the max date and then self-join back to get the whole row.
Assuming there are no ties for payments.date, try:
SELECT u.*, p.*
FROM (
SELECT MAX(p.date) AS date, p.user_id
FROM payments AS p
GROUP BY p.user_id
) AS latestP
INNER JOIN users AS u ON latestP.user_id = u.id
INNER JOIN payments AS p ON p.user_id = u.id AND p.date = latestP.date
WHERE u.package = 1
#John Woo's answer helped me solve a similar problem. I've improved upon his answer by setting the correct ordering as well. This has worked for me:
SELECT a.*, c.*
FROM users a
INNER JOIN payments c
ON a.id = c.user_ID
INNER JOIN (
SELECT user_ID, MAX(date) as maxDate FROM
(
SELECT user_ID, date
FROM payments
ORDER BY date DESC
) d
GROUP BY user_ID
) b ON c.user_ID = b.user_ID AND
c.date = b.maxDate
WHERE a.package = 1
I'm not sure how efficient this is, though.
SELECT U.*, V.* FROM users AS U
INNER JOIN (SELECT *
FROM payments
WHERE id IN (
SELECT MAX(id)
FROM payments
GROUP BY user_id
)) AS V ON U.id = V.user_id
This will get it working
Matei Mihai given a simple and efficient solution but it will not work until put a MAX(date) in SELECT part so this query will become:
SELECT u.*, p.*, max(date)
FROM payments p
JOIN users u ON u.id=p.user_id AND u.package = 1
GROUP BY u.id
And order by will not make any difference in grouping but it can order the final result provided by group by. I tried it and it worked for me.
My answer directly inspired from #valex very usefull, if you need several cols in the ORDER BY clause.
SELECT u.*
FROM users AS u
INNER JOIN (
SELECT p.*,
#num := if(#id = user_id, #num + 1, 1) as row_number,
#id := user_id as tmp
FROM (SELECT * FROM payments ORDER BY p.user_id ASC, date DESC) AS p,
(SELECT #num := 0) x,
(SELECT #id := 0) y
)
ON (p.user_id = u.id) and (p.row_number=1)
WHERE u.package = 1
This is quite simple do The inner join and then group by user_id and use max aggregate function in payment_id assuming your table being user and payment query can be
SELECT user.id, max(payment.id)
FROM user INNER JOIN payment ON (user.id = payment.user_id)
GROUP BY user.id
If you do not have to return the payment from the query you can do this with distinct, like:
SELECT DISTINCT u.*
FROM users AS u
INNER JOIN payments AS p ON p.user_id = u.id
This will return only users which have at least one record associated in payment table (because of inner join), and if user have multiple payments, will be returned only once (because of distinct), but the payment itself won't be returned, if you need the payment to be returned from the query, you can use for example subquery as other proposed.