Mysql selecting values based on other column value - mysql

Hi i have a table like so it includes a user id, date, amount and active flag
id
date
amount
active
1001
2017-07-12
10
1
1001
2017-07-12
5
0
1001
2017-07-12
12
0
1001
2017-05-05
5
0
1001
2017-06-01
11
0
my requirement is to get the total amount for this particular user for the whole day that he was active, so since the user was active on the date of '2017-07-12' i should be able to get all the amount for that particular date so my amount for this particular user would be 27.
What would be a right query to perform this action in mysql by looking at the active flag and how would i go about to do it?

We can use an aggregation approach here:
SELECT id, SUM(amount) AS total_amount
FROM
(
SELECT *
FROM yourTable t1
WHERE active = 1 OR
EXISTS (SELECT 1
FROM yourTable t2
WHERE t2.date = t1.date AND
t2.active = 1)
) t
GROUP BY id
ORDER BY id;
Demo

Either
SELECT id, `date`, SUM(amount) amount
FROM table
GROUP BY 1, 2
HAVING SUM(active)
or
SELECT id, `date`, SUM(amount) * (SUM(active) > 0) amount
FROM table
GROUP BY 1, 2
depends on desired output (does "non-active" dates must be skipped at all, or they'd be returned with zero amount).

Another solution:
select tbl.id,
tbl.`date`,
sum(amount) as tot_date_amount
from tbl
inner join (select `date`
from tbl
where active = 1
) as t2 on tbl.`date`=t2.`date`
group by id,`date`;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=c173dcc9a72490146ff7c094a10b08b6
The subquery will select only the dates where active = 1 .Using inner join will return only the sum for the active = 1 dates

Related

I want to calculate the sum of last transaction for A&B

Let's say the table looks like this:
user id
date
Amount
123
2022/11/01
5
456
2022/11/02
6
789
2022/11/03
8
123
2022/11/02
9
456
2022/11/04
6
789
2022/11/05
8
I want to calculate the sum of the very last transaction (only one for each user) for A & B FYI I'm using redash and I'm a beginner not sure what other info would you need, I tried MAX but was not sure how to apply it on more than one specific user.
Get the sum of Amount where user is A or B and date is the most recent date for each user
SELECT SUM(AMOUNT) AS total
FROM (
SELECT AMOUNT, ROW_NUMBER() OVER (PARTITION BY USERID ORDER BY DATE DESC) AS RN
FROM tableyoudidnotname
WHERE userid in ('A','B')
) X
WHERE X.RN = 1
You can try this, where we first calculate the maximum date by user in a common-table expression, then join that result-set to the table to sum the associated values.
WITH dat
AS
(
SELECT user_id, MAX(date) AS max_date
FROM credit.card
WHERE user_id IN ('A','B','ETC')
GROUP BY user_id
)
SELECT SUM(value) AS sum_on_max_dates
FROM credit.card t
INNER JOIN dat d ON t.user_id = d.user_id AND t.date = m.max_date;
You can try this, Used join with the subquery I mention below.
SELECT
SUM(t1.amount) AS count
FROM
transaction t1
JOIN
(SELECT
user_id, MAX(date) AS max_date
FROM
transaction
WHERE
user_id IN ('A', 'B')
GROUP BY user_id) t2 ON t1.user_id = t2.user_id
AND t2.max_date = t1.date;

How to create list of names of customers who have exceed the number of transactions in day and transaction limit

table 1
'name' 'amount' 'day'
---------------------------------------------
hemanth 10000 2019-06-21
hemanth 1000 2019-06-21
hemanth 5000 2019-06-21
hemanth 10000 2019-07-21
kumar 100 2019-06-21
kumar 5000 2019-06-21
kumar 1000 2019-07-21
kiranmai 10000 2019-06-21
kiranmai 500 2019-07-21
kiranmai 10000 2019-06-21
table 2 contains transcation limit per day & transcation amount limit
tranlimperday transamontlim
--------------------------------------------
3 10000
I am already convert the Date column into Day and month wise but after that we need find the count of transactions in day and sum of amount for thta we need to capmare the month column with month(date) but Im unable to find the query
expect oputput
Name
---------
Hemanth
kiranmai
I would suggest two separate queries and union:
select name
from (select name, day,
sum(amount) as total_amount, count(*) as cnt
from table1
group by name, day
) nd join
table2 t2
on t1.cnt >= t2.tranlimperday
union -- on purpose to remove duplicates
select name
from (select name, year(day) as yyyy, month(day) as mm,
sum(amount) as total_amount, count(*) as cnt
from table1
group by name, year(day), month(day)
) nym join
table2 t2
on t1.total_amount >= t2.transamontlim;
In MySQL 8+, you could also use window functions:
select distinct name
from (select name, day,
sum(amount) as daily_total_amount, count(*) as daily_cnt,
sum(sum(amount)) over (partition by name, year(day), month(day)) as monthly_total_amount,
sum(count(*)) over (partition by name, year(day), month(day)) as monthly_cnt
from table1
group by name, day
) nd join
table2 t2
on t1.daily_cnt >= t2.tranlimperday or
t1.monthly_total_amount >= t2.transamontlim;
If I understand correctly, you are looking for the aggregated results with either crossed daily transaction limit or crossed limit of daily translation amount limit. You can try this below option as well-
SELECT name,Day,
COUNT(1) num_tran,
SUM(amount) tran_amt
FROM Table1
CROSS JOIN Table2
GROUP BY name,Day,YEAR(day),MONTH(Day),DAY(Day)
HAVING COUNT(1) >= MAX(Table2.tranlimperday) -- Considering Single row in the table2
OR SUM(amount) > MAX(Table2.transamontlim) -- Considering Single row in the table2

Need help on MySQL query, i need to get the starting balance and the end balance by date group by stock_id

I need to get the starting balance from the earliest date and the ending balance from month end and group by stock_id.
My table:
id stock_id balance transact_at
1 1 100 2018-06-15
2 1 70 2018-06-16
3 1 30 2018-06-31
4 2 50 2018-06-01
5 2 10 2018-03-31
I want output:
stock_id start_balance ending_balance
1 100 30
2 50 10
Try this one. In this one two inner queries are fetching starting balance and closing balance by getting minimum and maximum transact_at corresponding to a stock_id and then the parent query is combing the two queries to get starting and closing balance in an single row. I have also shared fiddle link below to try.
select
tabledata1.stock_id,
startBalance,
closingBalance
from (
select
table1.stock_id,
balance as startBalance
from table1 join
(
select stock_id,
min(transact_at) as transact_at
from Table1 group by stock_id
) startTransaction
on Table1.stock_id = startTransaction.stock_id and
Table1.transact_at = startTransaction.transact_at
) tabledata1
join (
select
table1.stock_id,
balance as closingBalance
from table1 join
(
select stock_id,
max(transact_at) as transact_at
from Table1 group by stock_id
) endTransaction
on Table1.stock_id = endTransaction.stock_id
and Table1.transact_at = endTransaction.transact_at
) tabledata2
on tabledata1.stock_id = tabledata2.stock_id;
Demo
One approach in MySQL would be to aggregate by stock_id once and find the opening and closing dates. Then, self-join twice to pull in the actual balances which occurred on those opening and closing dates.
SELECT
t1.stock_id,
t2.balance AS start_balance,
t3.balance AS ending_balance
FROM
(
SELECT
stock_id,
MIN(transact_at) AS min_transact_at,
MAX(transact_at) AS max_transact_at
FROM my_table
GROUP BY stock_id
) t1
INNER JOIN my_table t2
ON t1.stock_id = t2.stock_id AND t2.transact_at = t1.min_transact_at
INNER JOIN my_table t3
ON t1.stock_id = t3.stock_id AND t3.transact_at = t1.max_transact_at;
Demo
Note: For posterity's sake, when MySQL 8+ becomes the norm, we could make use of things like ROW_NUMBER here, which might make it easier to get the result we want.
Try This One.
SELECT stock_id,MAX(balance) as start_balance, MIN(balance) as ending_balance FROM tbl_balance GROUP BY stock_id

Cumulative sum with mysql

I have the following query:
set #cumulativeSum := 0;
select
(#cumulativeSum:= #cumulativeSum + (count(distinct `ce`.URL, `ce`.`IP`))) as `uniqueClicks`,
cast(`ce`.`dt` as date) as `createdAt`
from (SELECT DISTINCT min((date(CODE_EVENTS.CREATED_AT))) dt, CODE_EVENTS.IP, CODE_EVENTS.URL
FROM CODE_EVENTS
GROUP BY CODE_EVENTS.IP, CODE_EVENTS.URL) as ce
join ATTACHMENT on `ce`.URL = ATTACHMENT.`ID`
where ATTACHMENT.`USER_ID` = 6
group by cast(`ce`.`dt` as date)
ORDER BY ce.URL;
It works almost ok, I would like to have as result set a date and amount of cumulative sum as uniqueClicks, the problem is that in my result set it is not added up together.
uniqueClicks createdAt
1 2018-02-01
3 2018-02-03
1 2018-02-04
and I'd like to have
uniqueClicks createdAt
1 2018-02-01
4 2018-02-03
5 2018-02-04
I believe you can obtain a rolling sum of the unique clicks without needing to resort to dynamic SQL:
SELECT
t1.CREATED_AT,
(SELECT SUM(t2.uniqueClicks) FROM
(
SELECT CREATED_AT, COUNT(DISTINCT IP, URL) uniqueClicks
FROM CODE_EVENTS
GROUP BY CREATED_AT
) t2
WHERE t2.CREATED_AT <= t1.CREATED_AT) uniqueClicksRolling
FROM
(
SELECT DISTINCT CREATED_AT
FROM CODE_EVENTS
) t1
ORDER BY t1.CREATED_AT;
The subquery aliased as t2 computes the number of unique clicks on each given day which appears in your table. The distinct count of IP and URL is what determines the number of clicks. We can then subquery this intermediate table and sum clicks for all days up and including the current date. This is essentially cursor style action, and can replace your use of session variables.

How to wirte an extensible SQL to find the users who continuously login for n days

If I have a table(Oracle or MySQL), which stores the date user logins.
So how can I write a SQL(or something else) to find the users who have continuously login for n days.
For example:
userID | logindate
1000 2014-01-10
1000 2014-01-11
1000 2014-02-01
1000 2014-02-02
1001 2014-02-01
1001 2014-02-02
1001 2014-02-03
1001 2014-02-04
1001 2014-02-05
1002 2014-02-01
1002 2014-02-03
1002 2014-02-05
.....
We can see that user 1000 has continually logined for two days in 2014, and user 1001 has continually logined for 5 days. and user 1002 never continuously logins.
The SQL should be extensible , which means I can pick every number of n, and modify a little or pass a new parameter, and the results is as expected.
Thank you!
As we don't know what dbms you are using (you named both MySQL and Oracle), here are are two solutions, both doing the same: Order the rows and subtract rownumber days from the login date (so if the 6th record is 2014-02-12 and the 7th is 2014-02-13 they both result in 2014-02-06). So we group by user and that groupday and count the days. Then we group by user to find the longest series.
Here is a solution for a dbms with analytic window functions (e.g. Oracle):
select userid, max(days)
from
(
select userid, groupday, count(*) as days
from
(
select
userid, logindate - row_number() over (partition by userid order by logindate) as groupday
from mytable
)
group by userid, groupday
)
group by userid
--having max(days) >= 3
And here is a MySQL query (untested, because I don't have MySQL available):
select
userid, max(days)
from
(
select
userid, date_add(logindate, interval -row_number day) as groupday, count(*) as days
from
(
select
userid, logindate,
#row_num := #row_num + 1 as row_number
from mytable
cross join (select #row_num := 0) r
order by userid, logindate
)
group by userid, groupday
)
group by userid
-- having max(days) >= 3
I think the following query will give you a very extensible parametrization:
select z.userid, count(*) continuous_login_days
from
(
with max_dates as
( -- Get max date for every user ID
select t.userid, max(t.logindate) max_date
from test t
group by t.userid
),
ranks as
( -- Get ranks for login dates per user
select t.*,
row_number() over
(partition by t.userid order by t.logindate desc) rnk
from test t
)
-- So here, we select continuous days by checking if rank inside group
-- (per user ID) matches login date compared to max date
select r.userid, r.logindate, r.rnk, m.max_date
from ranks r, max_dates m
where m.userid = r.userid
and r.logindate + r.rnk - 1 = m.max_date -- here is the key
) z
-- Then we only group by user ID to get the number of continuous days
group by z.userid
;
Here is the result:
USERID CONTINUOUS_LOGIN_DAYS
1 1000 2
2 1001 5
3 1002 1
So you can just choose by querying field CONTINUOUS_LOGIN_DAYS.
EDIT : If you want to choose from all ranges (not only the last one), my query structure no longer works because it relied on the last range. But here is a workaround:
with w as
( -- Parameter
select 2 nb_cont_days from dual
)
select *
from
(
select t.*,
-- Get number of days around
(select count(*) from test t2
where t2.userid = t.userid
and t2.logindate between t.logindate - nb_cont_days + 1
and t.logindate) m1,
-- Get also number of days more in the past, and in the future
(select count(*) from test t2
where t2.userid = t.userid
and t2.logindate between t.logindate - nb_cont_days
and t.logindate + 1) m2,
w.nb_cont_days
from w, test t
) x
-- If these 2 fields match, then we have what we want
where x.m1 = x.nb_cont_days
and x.m2 = x.nb_cont_days
order by 1, 2
You just have to change the parameter in the WITH clause, so you can even create a function from this query to call it with this parameter.
SELECT userID,count(userID) as numOfDays FROM LOGINTABLE WHERE logindate between '2014-01-01' AND '2014-02-28'
GROUP BY userID
In this case you can check the login days per user, in a specific period