mysql - filter IN statement - mysql

I need to refine this sql select:
SELECT ch_num, payment_type, Total FROM transactions WHERE ( ch_num )
IN (
SELECT ch_num FROM transactions GROUP BY ch_num HAVING count('ch_num') > 1
)
ORDER BY `ch_num`
Result i get is:
ch_num payment_type Total
20001 visa 36.60
20001 visa 36.60
20001 mc 30.60
50019 cash 9.00
50019 mc 18.95
50023 cash 2.70
50023 visa 7.00
But i need results rows only where there is 'cash' payment_type so 'ch_no' 20001 should be omited.
Correct result would be then:
ch_num payment_type Total
50019 cash 9.00
50019 mc 18.95
50023 cash 2.70
50023 visa 7.00

SELECT ch_num, payment_type, Total
FROM transactions
WHERE ch_num IN
(
SELECT ch_num
FROM transactions
GROUP BY ch_no
HAVING count('ch_num') > 1
and sum(payment_type='cash') >= 1
)
ORDER BY `ch_num`

Here's a complete, proven code example, with test results at the end. I used Oracle, but syntax should be the same for the SQL SELECT.
create table transactions (ch_num int, payment_type varchar2(100), total float);
insert into transactions values(20001,'visa',36.60);
insert into transactions values(20001,'mc',30.60);
insert into transactions values(50019,'cash',9.00);
insert into transactions values(50019,'mc',18.95);
insert into transactions values(50023,'cash',2.70);
insert into transactions values(50023,'visa',7.00);
SELECT ch_num, payment_type, Total FROM transactions a WHERE ( ch_num )
IN (
SELECT ch_num FROM transactions GROUP BY ch_num HAVING count(ch_num) > 1
)
AND EXISTS
(SELECT ch_num FROM transactions b where payment_type = 'cash' and a.ch_num = b.ch_num)
ORDER BY ch_num
Results:
CH_NUM PAYMENT_TYPE TOTAL
1 50019 cash 9
2 50019 mc 18.95
3 50023 cash 2.7
4 50023 visa 7

Related

how to get ALL amount_left with ALL partyname checking that ALL partyname same as in another table with in MYSQL

Here is the First Table:
Sales:
Restaurant Teliabagh 80000
Restaurant Teliabagh 20000
SHRI BALAJI 50000
Bhola ji 50000
Ajay 50000
Second Tables:
Cash:
Restaurant Teliabagh 20000
Restaurant Teliabagh 20000
Ajay 25000
I was able to get money_left for one party AJAY only amount not with his name
here is the command:
SELECT (select sum(totalamount) from acc.sales where Partyname='Ajay')-(select sum(Amountrecevied) from acc.cash where Party_name='Ajay') as money_left;
and output as
money_left
25000
BUT my problem is that Here I want to display the ALL Partyname with Money_left After subtraction (totalamount) of the partynames in sales table form (amount received) in cash tables.
IT will be fine if it displays partyname and amount as NUll after subtraction them.
SELECT Partyname, sales.totalamount - COALESCE(cash.totalamount, 0) as money_left
FROM ( SELECT Partyname, SUM(totalamount) totalamount
FROM acc.sales
GROUP BY 1 ) sales
LEFT JOIN ( SELECT Partyname, SUM(totalamount) totalamount
FROM acc.cash
GROUP BY 1 ) cash USING (Partyname);
If there exists Partyname value which is present in cash but is absent in sales then additional subquery with UNION DISTINCT which gathers all Partyname values needed as a base.

How to filter the result where >80% of the transaction coming from the same customer?

I have a transaction table like this:
tr_id merchant customer amount
-------------------------------------
00001 2005 3002 20
00002 2006 3002 11.16
00003 2001 3007 17.91
00004 2005 3002 20
00005 2003 3003 13.23
00005 2006 3007 14.61
00006 2005 3002 20
Etc.
I want to know the query to find which merchant that have >80% of the transaction coming from the same customer.
The result should contain list of merchant, customer in question, and each with sum of amount.
Try this:
select c.merchant, customer, 100 * customer_total / merchant_total as percentage
from (select merchant, customer, sum(amount) as customer_total
from mytable
group by merchant, customer) c
join (select merchant, sum(amount) as merchant_total
from mytable
group by merchant) m
on m.merchant = c.merchant
and customer_total > .8 * merchant_total
See live demo.
You could change and to where if you feel it improves readability, at a possible small cost to performance.
Group the table once by merchant to calculate the total records of
each merchant
Once again, group the table by merchant and customer to calculate
the total records of each merchant and customer
Join the result and calculate the percentage
query :
SELECT
t2.merchant,
t2.customer,
CONCAT((CAST(t2.merchant_Customer_CuontItem AS FLOAT) / CAST(t1.merchant_CuontItem AS FLOAT)) * 100, '%') AS newCol
FROM
(SELECT merchant,COUNT(*) AS merchant_CuontItem FROM tb GROUP BY merchant) t1
JOIN
(SELECT merchant,customer ,COUNT(*) AS merchant_Customer_CuontItem FROM tb GROUP BY merchant,customer) t2
ON t1.merchant = t2.merchant
Demo in db<>fiddle

Creating an overdraft statement

I'm currently stuck on how to create a statement that shows daily overdraft statements for a particular council.
I have the following, councils, users, markets, market_transactions, user_deposits.
market_transaction run daily reducing user's account balance. When the account_balance is 0 the users go into overdraft (negative). When users make a deposit their account balance increases.
I Have put the following tables to show how transactions and deposits are stored.
if I reverse today's transactions I'm able to get what account balance a user had yesterday but to formulate a query to get the daily OD amount is where the problem is.
USERS
user_id
name
account_bal
1
Wells
-5
2
James
100
3
Joy
10
4
Mumbi
-300
DEPOSITS
id
user_id
amount
date
1
1
5
2021-04-26
2
3
10
2021-04-26
3
3
5
2021-04-25
4
4
5
2021-04-25
TRANSACTIONS
id
user_id
amount_tendered
date
1
1
5
2021-04-27
2
2
10
2021-04-26
3
3
15
2021-04-26
4
4
50
2021-04-25
The Relationships are as follows,
COUNCILS
council_id
name
1
a
2
b
3
c
MARKETS
market_id
name
council_id
1
x
3
2
y
1
3
z
2
MARTKET_USER_LINK
id
market_id
user_id
1
1
3
2
2
2
3
3
1
I'm running this SQL query to get the total amount users have spent and subtracting with the current user account balance.
Don't know If I can use this to figure out the account_balance for each day.
SELECT u.user_id, total_spent, total_deposits,m.council_id
FROM users u
JOIN market_user_link ul ON ul.user_id= u.user_id
LEFT JOIN markets m ON ul.market_id =m.market_id
LEFT JOIN councils c ON m.council_id =c.council_id
LEFT JOIN (
SELECT user_id, SUM(amount_tendered) AS total_spent
FROM transactions
WHERE DATE(date) BETWEEN DATE('2021-02-01') AND DATE(NOW())
GROUP BY user_id
) t ON t.user_id= u.user_id
ORDER BY user_id, total_spent ASC
// looks like this when run
| user_id | total_spent | council_id |
|-------------|----------------|------------|
| 1 | 50.00 | 1 |
| 2 | 2.00 | 3 |
I was hoping to reverse transactions and deposits done to get the account balance for a day then get the sum of users with an account balance < 0... But this has just failed to work.
The goal is to produce a query that shows daily overdraft (Only SUM the total account balance of users with account balance below 0 ) for a particular council.
Expected Result
date
council_id
o_d_amount
2021-04-24
1
-300.00
2021-04-24
2
-60.00
2021-04-24
3
-900.00
2021-04-25
1
-600.00
2021-04-25
2
-100.00
2021-04-25
3
-1200.00
This is actually not that hard, but the way you asked makes it hard to follow.
Also, your expected result should match the data you provided.
Edited: Previous solution was wrong - It counted withdraws and deposits more than once if you have more than one event for each user/date.
Start by having the total exchanged on each day, like
select user_id, date, sum(amount) exchanged_on_day from (
select user_id, date, amount amount from deposits
union all select user_id, date, -amount_tendered amount from transactions
) d
group by user_id, date
order by user_id, date;
What follows gets the state of the account only on days that had any deposits or withdraws.
To get the results of all days (and not just those with account movement) you just have to change the cross join part to get a table with all dates you want (like Get all dates between two dates in SQL Server) but I digress...
select dates.date, c.council_id, u.name username
, u.account_bal - sum(case when e.date >= dates.date then e.exchanged_on_day else 0 end) as amount_on_start_of_day
, u.account_bal - sum(case when e.date > dates.date then e.exchanged_on_day else 0 end) as amount_on_end_of_day
from councils c
inner join markets m on c.council_id=m.council_id
inner join market_user_link mul on m.market_id=mul.market_id
inner join users u on mul.user_id=u.user_id
left join (
select user_id, date, sum(amount) exchanged_on_day from (
select user_id, date, amount amount from deposits
union all select user_id, date, -amount_tendered amount from transactions
) d group by user_id, date
) e on u.user_id=e.user_id --exchange on each Day
cross join (select distinct date from (select date from deposits union select date from transactions) datesInternal) dates --all days that had a transaction
group by dates.date, c.council_id, u.name, u.account_bal
order by dates.date desc, c.council_id, u.name;
From there you can rearrange to get the result you want.
select date, council_id
, sum(case when amount_on_start_of_day<0 then amount_on_start_of_day else 0 end) o_d_amount_start
, sum(case when amount_on_end_of_day<0 then amount_on_end_of_day else 0 end) o_d_amount_end
from (
select dates.date, c.council_id, u.name username
, u.account_bal - sum(case when e.date >= dates.date then e.exchanged_on_day else 0 end) as amount_on_start_of_day
, u.account_bal - sum(case when e.date > dates.date then e.exchanged_on_day else 0 end) as amount_on_end_of_day
from councils c
inner join markets m on c.council_id=m.council_id
inner join market_user_link mul on m.market_id=mul.market_id
inner join users u on mul.user_id=u.user_id
left join (
select user_id, date, sum(amount) exchanged_on_day from (
select user_id, date, amount amount from deposits
union all select user_id, date, -amount_tendered amount from transactions
) d group by user_id, date
) e on u.user_id=e.user_id --exchange on each Day
cross join (select distinct date from (select date from deposits union select date from transactions) datesInternal) dates --all days that had a transaction
group by dates.date, c.council_id, u.name, u.account_bal
) result
group by date, council_id
order by date;
You can check it on https://www.db-fiddle.com/f/msScT6B5F7FjU2aQXVr2da/6
Basically the query maps users to councils, caculates periods of overdrafts for users, them aggregates over councils. I assume that starting balance is dated start of the month '2021-04-01' (it could be ending balance as well, see below), change it as needed. Also that negative starting balance counts as an overdraft. For simplicity and debugging the query is divided into a number of steps.
with uc as (
select distinct m.council_id, mul.user_id
from markets m
join market_user_link mul on m.market_id = mul.market_id
),
user_running_total as (
select user_id, date,
coalesce(lead(date) over(partition by user_id order by date) - interval 1 day, date) nxt,
sum(sum(s)) over(partition by user_id order by date) rt
from (
select user_id, date, -amount_tendered s
from transactions
union all
select user_id, date, amount
from deposits
union all
select user_id, se.d, se.s
from users
cross join lateral (
select date(NOW() + interval 1 day) d, 0 s
union all
select '2021-04-01' d, account_bal
) se
) t
group by user_id, date
),
user_overdraft as (
select user_id, date, nxt, least(rt, 0) ovd
from user_running_total
where date <= date(NOW())
),
dates as (
select date
from user_overdraft
union
select nxt
from user_overdraft
),
council__overdraft as (
select uc.council_id, d.date, sum(uo.ovd) total_overdraft, lag(sum(uo.ovd), 1, sum(uo.ovd) - 1) over(partition by uc.council_id order by d.date) prev_ovd
from uc
cross join dates d
join user_overdraft uo on uc.user_id = uo.user_id and d.date between uo.date and uo.nxt
group by uc.council_id, d.date
)
select council_id, date, total_overdraft
from council__overdraft
where total_overdraft <> prev_ovd
order by date, council_id
Really council__overdraft is quite usable, the last step just compacts output excluding intermidiate dates when overdraft is not changed.
With following sample data:
users
user_id name account_bal
1 Wells -5
2 James 100
3 Joy 10
4 Mumbi -300
deposits, odered by date, extra row added for the last date
id user_id amount date
3 3 5 2021-04-25
4 4 5 2021-04-25
1 1 5 2021-04-26
2 3 10 2021-04-26
5 3 73 2021-05-06
transactions, odered by date (note the added row, to illustrate running total in action)
id user_id amount_tendered date
5 4 50 2021-04-25
2 2 10 2021-04-26
3 3 15 2021-04-26
1 1 5 2021-04-27
4 3 17 2021-04-27
councils
council_id name
1 a
2 b
3 c
markets
market_id name council_id
1 x 3
2 y 1
3 z 2
market_user_link
id market_id user_id
1 1 3
2 2 2
3 3 1
4 3 4
the query ouput is
council_id
date
overdraft
1
2021-04-01
0
2
2021-04-01
-305
3
2021-04-01
0
2
2021-04-25
-350
2
2021-04-26
-345
2
2021-04-27
-350
3
2021-04-27
-7
3
2021-05-06
0
Alternatively, provided the users table is holding a closing (NOW()) balance, replace user_running_total CTE with the following code
user_running_total as (
select user_id, date,
coalesce(lead(date) over(partition by user_id order by date) - interval 1 day, date) nxt,
coalesce(sum(sum(s)) over(partition by user_id order by date desc
rows between unbounded preceding and 1 preceding), sum(s)) rt
from (
select user_id, date, amount_tendered s
from transactions
union all
select user_id, date, -amount
from deposits
union all
select user_id, se.d, se.s
from users
cross join lateral (
select date(NOW() + interval 1 day) d, account_bal s
union all
select '2021-04-01' d, 0
) se
) t
where DATE(date) between date '2021-04-01' and date(NOW() + interval 1 day)
group by user_id, date
),
This way the query starts with closing balance dated next date after now and rollouts a running total in the reverse order till '2021-04-01' as a starting date.
Output
council_id
date
overdraft
1
2021-04-01
0
2
2021-04-01
-260
3
2021-04-01
-46
2
2021-04-25
-305
3
2021-04-25
-41
2
2021-04-26
-300
3
2021-04-26
-46
2
2021-04-27
-305
3
2021-04-27
-63
3
2021-05-06
0
db-fiddle both versions

How to calculate price average while grouping by date in MySQL?

In MySQL 5.7 I need to calculate the average discount per merchant and day.
There are 2 tables:
produts_manufacturers:
PROD_ID | MANUFACTURER_ID | PRICE_RECOM
1 1 10
2 1 20
merchants_prices
PROD_ID | MERCHANT_ID | PRICE_ACTUAL | DATE
1 10 9.00 21-01-20
1 11 8.80 21-01-20
1 11 9.00 22-01-19
My goal is a chart that gets its metric value from group by DATE, merchant_id
Now how is it possible to calculate the average discount of all products from a manufacturer at a particular merchant without also grouping by PROD_ID?
SELECT
date,
merchant_id,
1- (t.PRICE_ACTUAL / p.PRICE_RECOM)*100 AS discount2
-- (1 - ROUND(AVG(PRICE_ACTUAL),2) / p.PRICE_RECOM)*100 AS discount
FROM
merchants_prices t
INNER JOIN produts_manufacturers p ON t.PROD_ID = p.PROD_ID
WHERE
p.MANUFACTURER_ID = 1
AND PRICE_ACTUAL IS NOT NULL
GROUP by
date,
MERCHANT_ID
ORDER BY
date desc, merchant_id
To calculate average discount per merchant and day:
SELECT `DATE`, MERCHANT_ID, ROUND(AVG(discount), 2) as discount
FROM (
SELECT mp.`DATE`, mp.MERCHANT_ID, 1- (mp.PRICE_ACTUAL / pm.PRICE_RECOM)*100 AS discount
FROM produts_manufacturers pm
JOIN merchants_prices mp ON mp.PROD_ID = pm.PROD_ID
-- WHERE pm.MANUFACTURER_ID = 1 -- If you want to restrict to a specific manufacturer
) as x
GROUP BY `DATE`, MERCHANT_ID;
With your dataset ->
DATE MERCHANT_ID discount
2021-01-20 10 -89
2021-01-20 11 -87
2022-01-19 11 -89
To help you I made a fiddle here

MySQL query for date range transactions statement

I have a transactions table.
id account_id trx_date trx_type amount remarks
------ ---------- ---------- -------- ------ ---------
1 1 2017-12-10 DEPOSIT 500 test
2 1 2017-12-11 DEPOSIT 500 test
3 1 2017-12-12 DEPOSIT 6000 test
4 1 2017-12-13 WITHDRAW 300 test
5 1 2017-12-13 DEPOSIT 200 test
I want to result in this format but I can't figure out how will be the query for fetching data like as below. Here all fields are same as transactions table. op_bal, cl_bal are dynamic field.
date trx_type op_bal amount cl_bal
2017-12-12 DEPOSIT 1000 6000 7000
2017-12-13 WITHDRAW 7000 300 6700
2017-12-13 DEPOSIT 6700 200 6900
hello for the given output I have created this sqlfiddle please check for the answer
CREATE TABLE transactions (
id bigint(12) PRIMARY KEY auto_increment,
account_id bigint(12),
trx_date date,
trx_type enum('DEPOSIT','WITHDRAW'),
amount float(10,2),
remarks varchar(50)
);
INSERT INTO transactions (account_id, trx_date, trx_type, amount, remarks)
VALUES ('1','2017-12-10','DEPOSIT',500,'test'),
('1','2017-12-11','DEPOSIT',500,'test'),
('1','2017-12-12','DEPOSIT',6000,'test'),
('1','2017-12-13','WITHDRAW',300,'test'),
('1','2017-12-13','DEPOSIT',200,'test');
and query to achieve result is
SELECT
trx_date,
trx_type,
(SELECT SUM(CASE tr.trx_type WHEN 'DEPOSIT' THEN CONCAT('+',tr.amount) ELSE CONCAT('-', tr.amount) END) as amount FROM transactions as tr WHERE tr.id < (SELECT MAX(t.id) FROM transactions as t WHERE t.id <= transactions.id LIMIT 1) ) as op_bal,
amount AS trx_amount,
(SELECT SUM(CASE tr.trx_type WHEN 'DEPOSIT' THEN CONCAT('+',tr.amount) ELSE CONCAT('-', tr.amount) END) as amount FROM transactions as tr WHERE tr.id <= (SELECT MAX(t.id) FROM transactions as t WHERE t.id <= transactions.id LIMIT 1) ) as cl_bal
FROM transactions
WHERE DATE(trx_date) BETWEEN '2017-12-12' AND '2017-12-13'
Hope it will work for you.