Find Sum of cashbonus on the basis of user id in MySQL - mysql

ub_referrerid ub_redeemerid ub_referrer_cashbonus ub_redeemer_cashbonus
186 190 100 60
154 186 100 60
190 201 100 60
154 189 100 60
190 211 100 60
154 255 100 60
190 297 100 60
Now I have To Find The Sum Of cash Bonus on the basis of userid
suppose I have userid 154 which is present in both ub_referrerid and ub_redeemerid now I have to find the sum of userid 154 from column ub_referrer_cashbonus and ub_redeemer_cashbonus using MySQL

We create a CTE with UNION ALL two queries : one for referers bonuses and one for referees bonuses.
We then amalgamate the results.
create table ub(
ub_referrerid int,
ub_redeemerid int,
ub_referrer_cashbonus int,
ub_redeemer_cashbonus int);
insert into ub values
(186 ,190 ,100, 60 ),
(154 ,186, 100 ,60 ),
(190 ,201 ,100, 60 ),
(154 ,189 ,100, 60),
( 190 ,211, 100, 60 ),
(154 ,255 ,100 ,60),
( 190 ,297 ,100, 60);
with bonus as (
select
ub_referrerid id ,
sum(ub_referrer_cashbonus) referBonus,
0 redbonus
from
ub
group by
ub_referrerid
union all
select
ub_redeemerid id,
0 refCash,
sum(ub_redeemer_cashbonus) redCash
from
ub
group by
ub_redeemerid
)
select
b.id ,
sum(b.referbonus) refBonus,
sum(b.redBonus) redbonus,
sum(b.referBonus)+sum(redBonus) total
from bonus b
group by id
order by id ;
id | refBonus | redbonus | total
--: | -------: | -------: | ----:
154 | 300 | 0 | 300
186 | 100 | 60 | 160
189 | 0 | 60 | 60
190 | 300 | 60 | 360
201 | 0 | 60 | 60
211 | 0 | 60 | 60
255 | 0 | 60 | 60
297 | 0 | 60 | 60
db<>fiddle here

You have to use GROUP BY clause based on ub_referrerid and ub_redeemerid with same alias userid. Then use UNION ALL (In this operation, column names with alias must be same).
SELECT userid,SUM(bonus) AS bonus
FROM
(
SELECT ub_referrerid as userid,
(ub_referrer_cashbonus+ub_redeemer_cashbonus) as bonus
FROM YOUR_TABLE_NAME
UNION ALL
SELECT ub_redeemerid as userid,
(ub_referrer_cashbonus+ub_redeemer_cashbonus) as bonus
FROM YOUR_TABLE_NAME
) CASH
GROUP BY userid
ORDER BY userid;

Related

SQL query to find the concurrent sessions based on start and end time

Below is a sample dataset showing TV sessions of each TV set of each household.
Household “111” switch on their TV “1” at 500 and switch it off at 570. However, this has been captured
in the data as 2 separate rows. You will have to write a query to convert this into a single row.
Similar modification needs to be made to all other subsequent occurrences. Please note that a single
valid TV session can be split into more than 2 rows as well (As shown by rows 5-8).
Input :
Table [session]
Household_ID TV_Set_ID Start_time End_time
111 1 500 550
111 1 550 570
111 1 590 620
111 1 650 670
111 2 660 680
111 2 680 700
111 2 700 750
111 2 750 770
112 2 1050 1060
113 1 1060 1080
113 1 1080 1100
113 1 1100 1120
113 1 1500 1520
Expected Output :-
Household_ID TV_Set_ID Start_time End_time
111 1 500 570
111 1 590 620
111 1 650 670
111 2 660 770
112 2 1050 1060
113 1 1060 1120
113 1 1500 1520
I tried to find the lead time and find the difference and calculate the difference between that and the End time and thought I could group by but then that logic wont work since we dont just want the start and end time but even the gaps in the sessions. I'm stuck with the logic. Could someone tell how to proceed further ?
with result as
(
select Household_ID, TV_Set_ID, Start_time, End_time, lead(Start_time)
over (partition by Household_ID, TV_Set_ID order by Household_ID, TV_Set_ID) as lead_start
from session )
select *,lead_start - End_time as diff from result ;
Here is a way to get this done
In the data block i create groups which is defined as any record whose previous end_time doenst match with my start_time and assign a group_number to it if its different, else i keep it same.
After that in the main block i group by this group_number, along with the household_id,tv_set_id to get the results.
with data
as (
select *
,case when lag(end_time) over(partition by household_id,tv_set_id order by end_time)
<> start_time then
sum(1) over(partition by household_id,tv_set_id order by end_time)
else
sum(0) over(partition by household_id,tv_set_id order by end_time)
end as group_number
from t
)
select household_id
,tv_set_id
,min(start_time) as start_time
,max(end_time) as end_time
from data
group by household_id,tv_set_id,group_number
+--------------+-----------+------------+----------+
| household_id | tv_set_id | start_time | end_time |
+--------------+-----------+------------+----------+
| 111 | 1 | 500 | 570 |
| 111 | 1 | 590 | 620 |
| 111 | 1 | 650 | 670 |
| 111 | 2 | 660 | 770 |
| 112 | 2 | 1050 | 1060 |
| 113 | 1 | 1060 | 1120 |
| 113 | 1 | 1500 | 1520 |
+--------------+-----------+------------+----------+
db fiddle link
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=ba5ade186ebc3cf693c505d863691670
You could conditionally increment your data as in this case.
You might need to increment your data in case that the same household and TV set is used later. The column sequence created in the cte is used for that reason. Find the indicative answer.
WITH cte AS (
select t.*,
sum(flag) over (partition by household_id, tv_set_id order by household_id, tv_set_id, start_time) as sequence
from (select t.*,
case when start_time = LAG(end_time,1) OVER (PARTITION BY household_id, tv_set_id ORDER BY household_id, tv_set_id, start_time)
then 0
else 1
end as flag
from t
)t)
SELECT household_id, tv_set_id, MIN(start_time), MAX(end_time)
FROM cte
GROUP BY household_id, tv_set_id, sequence

Conditional Window Functions

I have a sales table that looks like this:
store_id cust_id txn_id txn_date amt industry
200 1 1 20180101 21.01 1000
200 2 2 20200102 20.01 1000
200 2 3 20200103 19 1000
200 3 4 20180103 19 1000
200 4 5 20200103 21.01 1000
300 2 6 20200104 1.39 2000
300 1 7 20200105 12.24 2000
300 1 8 20200105 25.02 2000
400 2 9 20180106 103.1 1000
400 2 10 20200107 21.3 1000
Here's the code to generate this sample table:
CREATE TABLE sales(
store_id INT,
cust_id INT,
txn_id INT,
txn_date bigint,
amt float,
industry INT);
INSERT INTO sales VALUES(200,1,1,20180101,21.01,1000);
INSERT INTO sales VALUES(200,2,2,20200102,20.01,1000);
INSERT INTO sales VALUES(200,2,3,20200103,19.00,1000);
INSERT INTO sales VALUES(200,3,4,20180103,19.00,1000);
INSERT INTO sales VALUES(200,4,5,20200103,21.01,1000);
INSERT INTO sales VALUES(300,2,6,20200104,1.39,2000);
INSERT INTO sales VALUES(300,1,7,20200105,12.24,2000);
INSERT INTO sales VALUES(300,1,8,20200105,25.02,2000);
INSERT INTO sales VALUES(400,2,9,20180106,103.1,1000);
INSERT INTO sales VALUES(400,2,10,20200107,21.3,1000);
What I would like to do is create a new table, results that answers the question: what percentage of my VIP customers have, since January 3rd 2020, shopped i) at my store only; ii) at my store and at other stores in the same industry; iii) at only other stores in the same industry? Define a VIP customer to be someone who has shopped at a given store at least once since 2019.
Here's the target output table:
store industry pct_my_store_only pct_both pct_other_stores_only
200 1000 0.5 0.5 0.0
300 2000 0.5 0.5 0.0
400 1000 0.0 1.0 0.0
I'm trying to use window functions to accomplish this. Here's what I have so far:
CREATE TABLE results as
SELECT s.store_id, s.industry,
COUNT(DISTINCT (CASE WHEN s.txn_date>=20200103 THEN s.cust_id END)) * 1.0 / sum(count(DISTINCT (CASE WHEN s.txn_date>=20200103 THEN s.cust_id END))) OVER (PARTITION BY s.industry) AS pct_my_store_only
...AS pct_both
...AS pct_other_stores_only
FROM sales s
WHERE sales.txn_date>=20190101
GROUP BY s.store_id, s.industry;
The above does not seem to be correct; how can I correct this?
Join the distinct store_ids and industries to the concatenated distinct store_ids and industries for each customer and then use window function avg() with the function find_in_set() to determine if a customer how many customer have shopped or not from each store:
with
stores as (
select distinct store_id, industry
from sales
where txn_date >= 20190103
),
customers as (
select cust_id,
group_concat(distinct store_id) stores,
group_concat(distinct industry) industries
from sales
where txn_date >= 20190103
group by cust_id
),
cte as (
select *,
avg(concat(s.store_id) = concat(c.stores)) over (partition by s.store_id, s.industry) pct_my_store_only,
avg(find_in_set(s.store_id, c.stores) = 0) over (partition by s.industry) pct_other_stores_only
from stores s inner join customers c
on find_in_set(s.industry, c.industries) and find_in_set(s.store_id, c.stores)
)
select distinct store_id, industry,
pct_my_store_only,
1 - pct_my_store_only - pct_other_stores_only pct_both,
pct_other_stores_only
from cte
order by store_id, industry
See the demo.
Results:
> store_id | industry | pct_my_store_only | pct_both | pct_other_stores_only
> -------: | -------: | ----------------: | -------: | --------------------:
> 200 | 1000 | 0.5000 | 0.5000 | 0.0000
> 300 | 2000 | 0.5000 | 0.5000 | 0.0000
> 400 | 1000 | 0.0000 | 1.0000 | 0.0000

Adding Amt1 and Amt2 values to the output column value of previous record

Input:
dated amount Amt1 Amt2
1/1/2017 100 0 10
1/2/2017 100 10 0
1/4/2017 100 0 0
1/6/2017 100 300 10
1/10/2017 100 0 20
1/11/2017 100 350 650
1/12/2017 100 0 234
Output:
dated amount Amt1 Amt2 Output Column
1/1/2017 100 0 10 100
1/2/2017 100 10 0 110
1/4/2017 100 0 0 120
1/6/2017 100 300 10 120
1/10/2017 100 0 20 430
1/11/2017 100 350 650 450
1/12/2017 100 0 234 1450
Output column is calculated with adding Amt1 and Amt2 values to the Output Column value of previous record.
Example: Output Column of
first record is as it is of Amount column,
second record will get from first record value of output column and Amt1 and Amt2 of first record i.e 100+0+10=110,
third record is from 110+10+0=120
fourth record is from 120+0+0=120
fifth record is from 120+300+10=430 ...
There are lots of examples of how to calculate running totals on this site and here's one which uses a variable. I am concerned that the purpose of the amount column is not defined but this solution works with the data provided for installation with mysql lower than version 8 (it will work with version 8 or above but there are better ways of doing it there). #tcadidot0 no hard coding required.
drop table if exists t;
create table t
( dated date, amount int, Amt1 int, Amt2 int);
insert into t values
(str_to_date('1/1/2017','%d/%m/%Y') , 100 , 0 , 10),
(str_to_date('1/2/2017','%d/%m/%Y') , 100 , 10 , 0),
(str_to_date('1/4/2017','%d/%m/%Y') , 100 , 0 , 0),
(str_to_date('1/6/2017','%d/%m/%Y') , 100 , 300 , 10),
(str_to_date('1/10/2017','%d/%m/%Y') , 100 , 0 , 20),
(str_to_date('1/11/2017','%d/%m/%Y') , 100 , 350 , 650),
(str_to_date('1/12/2017','%d/%m/%Y') , 100 , 0 , 234);
select t.dated,t.amount,t.amt1,t.amt2,
if(t.dated = (select min(t1.dated) from t t1),#op:=amount,
#op:=#op +
(select amt1 + amt2 from t t1 where t1.dated < t.dated order by t1.dated desc limit 1)
) op
from t
cross join (select #op:=0) o
order by dated;
+------------+--------+------+------+------+
| dated | amount | amt1 | amt2 | op |
+------------+--------+------+------+------+
| 2017-01-01 | 100 | 0 | 10 | 100 |
| 2017-02-01 | 100 | 10 | 0 | 110 |
| 2017-04-01 | 100 | 0 | 0 | 120 |
| 2017-06-01 | 100 | 300 | 10 | 120 |
| 2017-10-01 | 100 | 0 | 20 | 430 |
| 2017-11-01 | 100 | 350 | 650 | 450 |
| 2017-12-01 | 100 | 0 | 234 | 1450 |
+------------+--------+------+------+------+
7 rows in set (0.00 sec)

How can I club values in MySql

I have two columns coming from my sql query- month, value i.e. values are coming monthwise. My requirement is to club these months in the group of 3 months wise...and the values should come the average of these 3.
Ex.I have following data-
Month Values
Mar-14 50
Apr-14 51
May-14 52
Jun-14 53
Jul-14 54
Aug-14 55
Sep-14 56
Oct-14 57
Nov-14 58
Dec-14 59
Jan-15 60
Feb-15 61
Mar-15 62
Apr-15 63
May-15 64
Jun-15 65
Jul-15 66
Aug-15 67
Sep-15 68
Oct-15 69
Nov-15 70
Dec-15 71
Jan-16 72
Feb-16 73
Mar-16 74
Apr-16 75
May-16 76
Jun-16 77
Jul-16 78
Aug-16 79
Sep-16 80
Oct-16 81
Nov-16 82
Dec-16 83
Jan-17 84
Feb-17 85
Mar-17 86
How can I achieve following output in MySql-
3 Months Clubing Avg of Values
Mar-14 51
Jun-14 54
Sep-14 57
Dec-14 60
Mar-15 63
Jun-15 66
Sep-15 69
Dec-15 72
Mar-16 75
Jun-16 78
Sep-16 81
Thanks in Advance
A bit messy but you could use variables -assuming you have an incrementing id column (or soemthing you can order by)
drop table if exists t;
create table t(id int auto_increment primary key,Month varchar(10), Valus int);
insert into t (month,valus) values
('Mar-14', 50),
('Apr-14', 51),
('May-14', 52),
('Jun-14', 53),
('Jul-14', 54),
('Aug-14', 55),
('Sep-14', 56),
('Oct-14', 57),
('Nov-14', 58),
('Dec-14', 59);
select id,mth,rt
from
(
select id,month,valus,
#count:=#count+1 counter,
if(#count=1,#mth:=month,#mth:=#mth) mth,
if(#count=1,#block:=#block+1,#block:=#block) block,
if(#count<3,#sum:=#sum+valus,#sum:=(#sum+valus) / 3) rt,
if(#count=3,#count:=0,#count:=#count) creset,
if(#count=0,#sum:=0,#sum:=#sum) sumreset
from t
cross join (select #m ='',#count:=0,#sum:=0,#block:=0,#mth:='') s
order by id
)t
where counter = 3;
+----+--------+------+
| id | mth | rt |
+----+--------+------+
| 3 | Mar-14 | 51 |
| 6 | Jun-14 | 54 |
| 9 | Sep-14 | 57 |
+----+--------+------+
3 rows in set (0.03 sec)
Slightly less messy but using sql's avg function and using variables to fill down the first month in a 3 month block
select block,mth,avg(valus)
from
(
select id,month,valus,
#count:=#count+1 counter,
if(#count=1,#mth:=month,#mth:=#mth) mth,
if(#count=1,#block:=#block+1,#block:=#block) block,
if(#count=3,#count:=0,#count:=#count) creset
from t
cross join (select #block:=0,#count:=0,#mth:='') s
order by id
) t
group by block,mth
order by block,mth
+-------+--------+------------+
| block | mth | avg(valus) |
+-------+--------+------------+
| 1 | Mar-14 | 51.0000 |
| 2 | Jun-14 | 54.0000 |
| 3 | Sep-14 | 57.0000 |
| 4 | Dec-14 | 59.0000 |
+-------+--------+------------+
4 rows in set (0.05 sec)
Try this
create temporary table tab (month1 varchar(30), id int);
insert into tab (month1,id)
values('Mar-14' ,50),
('Apr-14' ,51),
('May-14' ,52),
('Jun-14' ,53),
('Jul-14' ,54),
('Aug-14' ,55),
('Sep-14' ,56),
('Oct-14' ,57),
('Nov-14' ,58),
('Dec-14' ,59),
('Jan-15' ,60),
('Feb-15' ,61),
('Mar-14' ,62);
set #row_number = 0;
select *
from tab where (#row_number := #row_number+1)%3= 1;
Result
month1 id
'Mar-14' '50'
'Jun-14' '53'
'Sep-14' '56'
'Dec-14' '59'
'Mar-14' '62'

Select where in() for each id return equal rows count

How to select rows for each user_id equals select numbers of count for each user_id?
My example table:
mp3_id | user_id
--------------------
120 | 840
123 | 840
126 | 840
128 | 455
130 | 840
131 | 840
132 | 840
135 | 840
144 | 840
158 | 840
159 | 455
161 | 455
169 | 455
180 | 840
181 | 455
184 | 455
186 | 455
189 | 455
My simple query:
select mp3_id where user_id IN (840,455) limit 8
Return:
mp3_id | user_id
--------------------
120 | 840
123 | 840
126 | 840
128 | 455
130 | 840
131 | 840
132 | 840
135 | 840
But I want to this select:
mp3_id | user_id
--------------------
120 | 840
123 | 840
126 | 840
130 | 840
128 | 455
159 | 455
161 | 455
169 | 455
I want each user_id to return an equal row count. How to?
You could do this with a UNION:
select mp3_id where user_id = 840 limit 4
union all
select mp3_id where user_id = 455 limit 4
try this query:
select
yt.mp3_id,
e.user_id
from
(
select distinct user_id from your_table
) e
join your_table yt on true
where yt.mp3_id in (select tt.mp3_id from your_table tt where tt.user_id = e.user_id order by tt.mp3_id limit 4)
and tihs same query but with condition
select
yt.mp3_id,
e.user_id
from
(
select distinct user_id from your_table where user_id in (840,455)
) e
join your_table yt on true
where yt.mp3_id in (select tt.mp3_id from your_table tt where tt.user_id = e.user_id order by tt.mp3_id limit 4)
SELECT x.*
FROM my_table x
JOIN my_table y
ON y.user_id = x.user_id
AND y.mp3_id <= x.mp3_id
GROUP
BY x.mp3_id HAVING COUNT(*) <= 4
ORDER
BY user_id DESC
, mp3_id;
or faster
SELECT mp3_id, user_id FROM
(
SELECT x.*, CASE WHEN #prev = user_id THEN #i:=#i+1 ELSE #i:=1 END i, #prev:=user_id FROM my_table x, (SELECT #prev:=null,#i:=1) vars ORDER BY user_id DESC, mp3_id
) a
WHERE i<=4;