Deaggrigate then apply With Rollup - mysql

need just a bit more tweaking to my query. I have a table that is filled with assignments and values such as this:
a_inv
assignment_id total
=========================
1 500
Then two tables that are related, categories and channels for the assignment, for example:
a_cat
assignment_id category_id
==============================
1 1
1 11
a_ch
assignment_id channel_id
==============================
1 16
1 25
So what I need to do is figure out how much was spent per category and channel (with rollup).
Here's the query that I got the closest with:
SELECT
ac.category_id, ach.channel_id, ((ia.total/COUNT(DISTINCT ac2.category_id))/COUNT(DISTINCT ach2.channel_id)) AS cur_total
FROM
a_cat ac
LEFT JOIN
a_ch ach ON ach.assignment_id = ac.assignment_id
LEFT JOIN
a_inv ia ON ia.assignment_id = ac.assignment_id
LEFT JOIN
a_cat ac2 ON ach.assignment_id = ac2.assignment_id
LEFT JOIN
a_ch ach2 ON ach2.assignment_id = ac.assignment_id
WHERE
ac.assignment_id = 1
GROUP BY
ac.category_id, ach.channel_id WITH ROLLUP
It gives me a result that is very close to what i need:
category_id channel_id cur_total
=====================================
1 16 125.0000
1 25 125.0000
1 NULL 125.0000 <---- this should be "250.0000"
11 16 125.0000
11 25 125.0000
11 NULL 125.0000 <---- this should be "250.0000"
NULL NULL 125.0000 <---- this should be "500.0000"
Any help is greatly appreciated.
Thanks!

Try this
SELECT category_id, channel_id, SUM(cur_total)
FROM (
SELECT
ac.category_id, ach.channel_id, ((ia.total/COUNT(DISTINCT ac2.category_id))/COUNT(DISTINCT ach2.channel_id)) AS cur_total
FROM
a_cat ac
LEFT JOIN
a_ch ach ON ach.assignment_id = ac.assignment_id
LEFT JOIN
a_inv ia ON ia.assignment_id = ac.assignment_id
LEFT JOIN
a_cat ac2 ON ach.assignment_id = ac2.assignment_id
LEFT JOIN
a_ch ach2 ON ach2.assignment_id = ac.assignment_id
WHERE
ac.assignment_id = 1
GROUP BY
ac.category_id, ach.channel_id
) as t1
GROUP BY category_id, channel_id WITH ROLLUP

Related

Get the row with latest date after inner join

I have this query:
SELECT
achievements.id,
achievements.name,
achievements.category,
userAchievements.createdAt
FROM
achievements
INNER JOIN
userAchievements ON userAchievements.achievementId = achievements.id
AND userAchievements.userId = 12
WHERE
achievements.type = 2
Result of this query is:
id
name
category
createdAt
8
First
1
2021-02-11
13
Second
2
2021-02-12
14
Third
4
2021-03-01
15
Fourth
4
2021-03-02
I have to leave only unique category with max createdAt field.
I need a query that will give the following result:
id
name
category
createdAt
8
First
1
2021-02-11
13
Second
2
2021-02-12
15
Fourth
4
2021-03-02
If I use group by then it returns row with id 14.
MySQL version - 5.7.33.
Achievements table:
id
name
category
8
First
1
13
Second
2
14
Third
4
15
Fourth
4
UserAchievements table:
id
achievementId
userId
createdAt
3
8
12
2021-02-11
7
13
12
2021-02-12
36
15
12
2021-03-02
40
14
12
2021-03-01
P.S.
I managed to write a query that solves the problem
SELECT
*
FROM
(SELECT
*,
IF(#prev <> category, #rn:=0, #rn),
#prev:=category,
#rn:=#rn + 1 AS rn
FROM
(SELECT
achievements.id,
achievements.name,
achievements.category,
userAchievements.createdAt
FROM
achievements
INNER JOIN userAchievements ON userAchievements.achievementId = achievements.id
AND userAchievements.userId = 12
WHERE
achievements.type = 2) as ach, (SELECT #rn:=0) rn, (SELECT #prev:='') prev
ORDER BY ach.createdAt DESC) t
WHERE
rn = 1
You can use window functions:
SELECT a.*
FROM (SELECT a.*,
ROW_NUMBER() OVER (PARTITION BY a.category ORDER BY a.created_at DESC) as seqnum
FROM achievements JOIN
userAchievements ua
ON ua.achievementId = a.id AND
ua.userId = 12
WHERE a.type = 2
) a
WHERE seqnum = 1;
This subquery basically search for all maximum dates, grouped by category.
SELECT MAX(_ua.createdAt) FROM `achievements` _a INNER JOIN user_achievements _ua ON _a.id = _ua.achievementId group by _a.category
So you maybe you can do :
SELECT a.* FROM achievements a
INNER JOIN user_achievements ua ON a.id = ua.achievementId
AND ua.createdAt IN (SELECT MAX(_ua.createdAt) FROM `achievements` _a INNER JOIN
user_achievements _ua ON _a.id = _ua.achievementId group by _a.category)

How to get sum of amount from multiple accounts which return by join

I have 3 tables like:
owner_details:-
owner_id owner_name
---------------------
1 A
2 B
3 C
-------------------
vehicle_owner:-
v_id vehicle_id owner_id
-------------------------
1 1 1
2 2 2
3 4 1
4 3 1
5 5 3
transaction:-
id v_id amount transaction_type
--------------------------------
1 1 100 0
2 2 250 1
3 1 150 1
4 3 450 1
5 1 200 0
6 4 300 1
7 5 150 0
8 5 200 1
transaction_type= 0 then (-) transaction_type=1 then (+)
Owner A (1) have 3 vehicles with v_id (1,3,4) in table vehicle_owner.
v_id (1,3,4) have 5 entries in table transaction (1,3,4,5,6) with sum of amount 600 (-100+150+450-200+300)
Now I want listing like this:-.
owner_id owner_name amount
---------------------
1 A 600
2 B 250
3 C 50
-------------------
You can use the following query:
SELECT od.owner_id, od.owner_name, SUM(t.amount) AS amount
FROM owner_details od INNER JOIN vehicle_owner vo ON od.owner_id = vo.owner_id
INNER JOIN `transaction` t ON vo.v_id = t.v_id
GROUP BY od.owner_id
If you want to use the additional transaction_type you can use the following:
SELECT od.owner_id, od.owner_name, SUM(CASE WHEN t.transaction_type = 0 THEN t.amount * -1 ELSE t.amount END) AS amount
FROM owner_details od INNER JOIN vehicle_owner vo ON od.owner_id = vo.owner_id
INNER JOIN `transaction` t ON vo.v_id = t.v_id
GROUP BY od.owner_id
demo: http://sqlfiddle.com/#!9/c5f8d/1/1
Try this:
SELECT A.owner_id, A.owner_name, SUM(IFNULL(amount,0)) AMOUNT
FROM owner_details A LEFT JOIN
vehicle_owner B
ON A.owner_id=B.owner_id
LEFT JOIN `transaction` C
ON C.v_id=B.v_id
GROUP BY A.owner_id, A.owner_name;
It works for me
SELECT od.owner_id,
od.owner_name,
Sum(t.amount) AS amount
FROM owner_details od
INNER JOIN vehicle_owner vo
ON od.owner_id = vo.owner_id
INNER JOIN (SELECT v_id,
Coalesce(Sum(CASE
WHEN type = 0 THEN -amount
ELSE +amount
end), 0.0) AS amount
FROM `transaction`
GROUP BY v_id) t
ON vo.v_id = t.v_id
GROUP BY od.owner_id
Thanks Sebastian Brosch for quick response !!!

MySQL getting value in same column as where conditional

Need some help creating a query that can get me the results I want.
I'm pulling information from 2 tables in the mysql database.
TABLE 1 - tblclients
ID firstname lastname
1 Bob K
2 Mary J
3 Tod M
tblcustomfieldsvalues.RelId = tblclients.ID
TABLE 2 - tblcustomfieldsvalues
ID fieldid RelId value
1 15 3 3500
2 15 2 1500
3 17 3 Calp
4 17 2 Amazon
5 17 2 Calp
TABLE 3 - tblcustomfields (JUST FOR REFERENCE)
ID FieldID name
1 15 Purchase Amount
2 17 Site
Desired Result:
I want to show the Purchase Amount in column 4 (FieldID = 15) where FieldID = 17 and value = 'calp'
ID FirstName LastName Value
1 Tod M 3500
2 Mary J 1500
Current Query:
SELECT tblclients.id, tblclients.firstname, tblclients.lastname, tblcustomfieldsvalues.value FROM tblclients INNER JOIN tblcustomfieldsvalues ON tblclients.id = tblcustomfieldsvalues.relid WHERE tblcustomfieldsvalues.fieldid = 17 AND tblcustomfieldsvalues.value = 'Calp'
Current Result:
ID FirstName LastName Value
1 Tod M Calp
2 Mary J Calp
One approach to this is conditional aggregation:
select c.*, value15 as value
from tblclients c join
(select cfv.relid,
max(case when fieldid = 15 then value end) as value15,
sum(case when fieldid = 17 and value = 'Calp' then 1 else 0 end) as cnt17
from tblcustomfieldsvalues cfv
group by cfv.relid
) cv
where cnt17 > 0;
You can also do this with joins. But you need separate joins for each for each of the fields:
SELECT c.id, c.firstname, c.lastname, cfv15.value
FROM tblclients c INNER JOIN
tblcustomfieldsvalues cfv17
ON c.id = cfv17.relid AND
cvf17.fieldid = 17 AND cfv17.value = 'Calp' INNER JOIN
tblcustomfieldsvalues cfv15
ON c.id = cfv15.relid AND
cvf15.fieldid = 15;
I am not clear what result you are expecting...
First of all I am sure you wrote fieldid=17 to get this result not 47.
If so
Your result is normal. You ask for fieldid 17 and also value 'Calp'.
Do you want fieldid 15 maybe ? Those were the ones has prices...
Or maybe
SELECT tblclients.id, tblclients.firstname, tblclients.lastname, tblcustomfieldsvalues.value FROM tblclients INNER JOIN tblcustomfieldsvalues ON tblclients.id = tblcustomfieldsvalues.relid WHERE tblcustomfieldsvalues.fieldid in (15,17) OR tblcustomfieldsvalues.value = 'Calp'
Your Query:
SELECT tblclients.id, tblclients.firstname, tblclients.lastname, tblcustomfieldsvalues.value FROM tblclients INNER JOIN tblcustomfieldsvalues ON tblclients.id = tblcustomfieldsvalues.relid WHERE tblcustomfieldsvalues.fieldid = 47 AND tblcustomfieldsvalues.value = 'Calp'

Sql: select only value that join table has value equal to 1

I have these tables in mysql
ADS
ID
1
2
PAYMENTS
id ads_id
1 1
2 1
3 1
4 2
5 2
INSTALLMENTS
id Payment_id paid
1 1 1
2 1 0
3 2 1
4 2 1
5 3 1
6 4 0
7 4 1
8 5 0
I want to get count of ads that have only payments with all installments.paid = 1.
As an example, return value is 2 for ads.id = 1 (only payment.id 2 AND 3 has all installments.paid = 1) and 0 for ads.id = 0
I tried with
SELECT ads.id AS id,
(
select count(*)
from payments
where
(SELECT count(*)
FROM installments
where (installments.payment_id = payments.id AND (installments.paid = 0))) <1
AND payments.ad_id = ads.id
)
FROM ads
LEFT JOIN payments ON ads.id = payments.ad_id
LEFT JOIN installments ON installments.payment_id = payments.id
GROUP BY ads.id
But doesn't work
One way to approach this is using group by and having. To get the ad ids, you don't actually need the ads table, because installments has this information:
select i.ads_id
from installments i join
payments p
on i.payment_id = p.id
group by i.ads_id
having min(i.paid) = 1 and min(i.paid) = max(i.paid);
Note that this is checking both the minimum and maximum value -- just in case some other value were to creep in. If the value could be NULL, you would also want and count(*) = count(i.paid).
Another approach to this would just use not exists or not in:
select a.*
from ads a
where not exists (select 1
from installments i join
payments p
on i.payment_id = p.id
where p.ads_id = a.id and i.paid <> 0
);

mySQL - GROUP BY but get the most recent row

I've got a budget table:
user_id product_id budget created
-----------------------------------------------------------------
1 1 300 2011-12-01
2 1 400 2011-12-01
1 1 500 2011-12-03
2 2 400 2011-12-04
I've also got a manager_user table, joining a manager with the user
user_id manager_id product_id
------------------------------------
1 5 1
1 9 2
2 5 1
2 5 2
3 5 1
What I'd like to do is grab each of the user that's assigned to Manager #5, and also get their 'budgets'... but only the most recent one.
Right now my statement looks like this:
SELECT * FROM manager_user mu
LEFT JOIN budget b
ON b.user_id = mu.user_id AND b.product_id = mu.product_id
WHERE mu.manager_id = 5
GROUP BY mu.user_id, mu.product_id
ORDER BY b.created DESC;
The problem is it doesn't pull the most recent budget. Any suggestions? Thanks!
To accomplish your task you can do as follows:
select b1.user_id,
b1.budget
from budget b1 inner join (
select b.user_id,
b.product_id,
max(created) lastdate
from budget b
group by b.user_id, b.product_id ) q
on b1.user_id=q.user_id and
b1.product_id=q.product_id and
b1.created=q.lastdate
where b1.user_id in
(select user_id from manager_user where manager_id = 5);
I'm assuming here that your (user_id, product_id, created) combination is unique.
For what it's worth, here's the code that returned what I was looking for:
SELECT DISTINCT(b1.id),mu.user_id,mu.product_id,b1.budget,b1.created
FROM budget b1
INNER JOIN (
SELECT b.user_id, b.product_id, MAX(created) lastdate
FROM budget b
GROUP BY b.user_id, b.product_id) q
ON b1.user_id=q.user_id AND
b1.product_id=q.product_id AND
b1.created=q.lastdate
RIGHT JOIN manager_user mu
ON mu.user_id = b1.user_id AND
mu.product_id = b1.product_id
WHERE mu.manager_id = 5;
Thanks for the help Andrea!