Get a difference in dates and aggregate results over a third column - mysql

+-----------+------------+------------+
| ACCOUNT | PAID_DATE | DUE_DATE |
+-----------+------------+------------+
| 103240005 | 2010-07-22 | 2009-11-30 |
| 103240005 | 2010-07-22 | 2007-09-30 |
| 103240005 | 2010-07-22 | 2008-09-30 |
| 103240006 | 2010-07-22 | 2009-09-30 |
| 103240006 | 2010-07-22 | 2007-07-22 |
| 103240007 | 2010-07-22 | 2008-07-22 |
| 103240008 | 2010-07-22 | 2009-08-31 |
| 103240009 | 2010-07-22 | 2007-12-31 |
| 103240009 | 2010-07-22 | 2008-12-31 |
| 103240005 | 2010-07-22 | 2009-12-31 |
+-----------+------------+------------+
The above sample dataset is from a banking application I am building.
I would like to get per account, the amount of records where the payments were made on time, i.e DATEDIFF(DUE_DATE, PAID_DATE) = 0. Please note that there are multiple entries per account.
Here is my problematic query:
select ACCOUNT_NUMBER, count(DATEDIFF(PAID_DATE, DUE_DATE) as diff) as diff_count
from TRANSACTIONS
where diff=0 group by ACCOUNT_NUMBER;

Assuming the paid_date and due_date are of date type, you can use:
select account_number,
sum(paid_date = due_date) as diff_count
from transactions
group by account_number;
if the two dates are equal, the result will be true which is taken as 1 by mysql and 0 for false.
EDIT:
You can further add more aggregates as needed. For e.g. - count where the overdue is 10 or more days, you can use:
select account_number,
sum(paid_date = due_date) as diff_count,
sum(datediff(paid_date, due_date) >= 10) as overdue,
from transactions
group by account_number;

Related

How to get Total Overdraft amounts from a particular Date in SQL

I'm trying to get the total amount of overdraft accounts from an old Date, the goal is to get the total amount it was on the 31st of January.
I have the following tables Users and Transactions.
USERS (currently)
| user_id | name | account_balance |
|---------|---------|------------------|
| 1 | Wells | 1.00 |
| 2 | John | -10.00 |
| 3 | Sahar | -5.00 |
| 4 | Peter | 1.00 |
TRANSACTIONS (daily transition can go back in time)
| trans_id | user_id | amount_tendered | trans_datetime |
|------------|---------|-------------------|---------------------|
| 1 | 1 | 2 | 2021-02-16 |
| 2 | 2 | 3 | 2021-02-16 |
| 3 | 3 | 5 | 2021-02-16 |
| 4 | 4 | 2 | 2021-02-16 |
| 5 | 1 | 10 | 2021-02-15 |
so the current total overdraft amount is
SELECT sum(account_balance) AS O_D_Amount
FROM users
WHERE account_balance < 0;
| O_D_Amount |
|------------|
| -15 |
I need Help to reverse this amount to a date in history.
Assuming overdrafts are based on the sum of transactions up to a point, you can use a subquery:
select sum(total) as total_overdraft
from (select user_id, sum(amount_tendered) as total
from transactions t
where t.trans_datetime <= ?
group by user_id
) t
where total < 0;
The ? is a parameter placeholder for the date/time you care about.

Select only values that match the conditions

I have the following (example) table:
+----------+--------------+------------+------------+------------+
| Customer | Subscription | Status | Start Date | End Date |
+----------+--------------+------------+------------+------------+
| 1 | 1a001 | Pending | 02.02.2020 | |
| 1 | 1b002 | Pending | 02.02.2020 | 05.02.2020 |
| 1 | 1c003 | Rejected | 02.02.2020 | |
| 1 | 1d004 | Incomplete | 02.02.2020 | |
| 1 | 1e005 | Pending | 07.02.2020 | |
| 1 | 1f006 | Active | 07.02.2020 | |
| 2 | 2a001 | Pending | 02.02.2020 | |
| 2 | 2b002 | Pending | 02.02.2020 | |
| 2 | 3c003 | Rejected | 02.02.2020 | |
| 2 | 4d004 | Incomplete | 02.02.2020 | |
| 2 | 5e005 | Pending | 07.02.2020 | 07.02.2020 |
| 2 | 6f006 | Active | 07.02.2020 | |
+----------+--------------+------------+------------+------------+
The aim is to select only Active Customers. Assumptions and conditions are:
One Customer_no can have many Subscriptions.
Active Customer = Customer that has at least one Active Subscription.
Active Subscription is when: Status = Active, the start_date is >= sysdate() and there is no end_date.
I am going to have very long list of Statuses what can be mapped using CASE statement. Is there any idea how to select distinct Active customers basing on that example? I am a begginer and do not know how to start with this task.
Thanks, Pawel
Your condition for an active customer can be written like this:
status = 'Active' and start_date >= curdate() and end_date is null
So group by customer and use the condition in the having clause:
select customer
from tablename
group by customer
having sum(status = 'Active' and start_date >= curdate() and end_date is null)
If the sum() returns 0 then it is evaluated to false.
In any other case it is evaluated to true.

How to sum values of two tables and group by date

I am building a trading system where users need to know their running account balance by date for a specific user (uid) including how much they made from trading (results table) and how much they deposited or withdrew from their accounts (adjustments table).
Here is the sqlfiddle and tables: http://sqlfiddle.com/#!9/6bc9e4/1
Adjustments table:
+-------+-----+-----+--------+------------+
| adjid | aid | uid | amount | date |
+-------+-----+-----+--------+------------+
| 1 | 1 | 1 | 20 | 2019-08-18 |
| 2 | 1 | 1 | 50 | 2019-08-21 |
| 3 | 1 | 1 | 40 | 2019-08-21 |
| 4 | 1 | 1 | 10 | 2019-08-19 |
+-------+-----+-----+--------+------------+
Results table:
+-----+-----+-----+--------+-------+------------+
| tid | uid | aid | amount | taxes | date |
+-----+-----+-----+--------+-------+------------+
| 1 | 1 | 1 | 100 | 3 | 2019-08-19 |
| 2 | 1 | 1 | -50 | 1 | 2019-08-20 |
| 3 | 1 | 1 | 100 | 2 | 2019-08-21 |
| 4 | 1 | 1 | 100 | 2 | 2019-08-21 |
+-----+-----+-----+--------+-------+------------+
How do I get the below results for uid (1)
+--------------+------------+------------------+----------------+------------+
| ResultsTotal | TaxesTotal | AdjustmentsTotal | RunningBalance | Date |
+--------------+------------+------------------+----------------+------------+
| - | - | 20 | 20 | 2019-08-18 |
| 100 | 3 | 10 | 133 | 2019-08-19 |
| -50 | 1 | - | 84 | 2019-08-20 |
| 200 | 4 | 90 | 378 | 2019-08-21 |
+--------------+------------+------------------+----------------+------------+
Where RunningBalance is the current account balance for the particular user (uid).
Based on #Gabriel's answer, I came up with something like, but it gives me empty balance and duplicate records
SELECT SUM(ResultsTotal), SUM(TaxesTotal), SUM(AdjustmentsTotal), #runningtotal:= #runningtotal+SUM(ResultsTotal)+SUM(TaxesTotal)+SUM(AdjustmentsTotal) as Balance, date
FROM (
SELECT 0 AS ResultsTotal, 0 AS TaxesTotal, adjustments.amount AS AdjustmentsTotal, adjustments.date
FROM adjustments LEFT JOIN results ON (results.uid=adjustments.uid) WHERE adjustments.uid='1'
UNION ALL
SELECT results.amount AS ResultsTotal, taxes AS TaxesTotal, 0 as AdjustmentsTotal, results.date
FROM results LEFT JOIN adjustments ON (results.uid=adjustments.uid) WHERE results.uid='1'
) unionTable
GROUP BY DATE ORDER BY date
For what you are asking you would want to union then group the results from both tables, this should give the results you want. However, I recommend calculating the running balance outside of MySQL since this adds some complexity to our query.
Weird things could start to happen, for example, if someone already defined the #runningBalance variable as part of the queries scope.
SELECT aggregateTable.*, #runningBalance := ifNULL(#runningBalance, 0) + TOTAL
FROM (
SELECT SUM(ResultsTotal), SUM(TaxesTotal), SUM(AdjustmentsTotal)
, SUM(ResultsTotal) + SUM(TaxesTotal) + SUM(AdjustmentsTotal) as TOTAL
, date
FROM (
SELECT 0 AS ResultsTotal, 0 AS TaxesTotal, amount AS AdjustmentsTotal, date
FROM adjustments
UNION ALL
SELECT amount AS ResultsTotal, taxes AS TaxesTotal, 0 as AdjustmentsTotal, date
FROM results
) unionTable
GROUP BY date
) aggregateTable

Group subscription duration by month and year in mysql

I have a table of subscriptions, storing user id, subscription end date, program id. One user can be subscribed to many programs, but for the scope of the problem the latest date is considered as the end date of the subscription. The goal is to find the number of users whose subscription is ending for each month of each year. To illustrate it:
-------------------------------------------
| user_id | program_id | end_date |
-------------------------------------------
| 1 | 1 | 2015-12-10 |
| 1 | 2 | 2017-08-27 |
| 2 | 1 | 2017-09-20 |
| 3 | 2 | 2017-10-01 |
| 2 | 3 | 2017-09-18 |
| 5 | 12 | 2017-10-22 |
| 4 | 3 | 2017-10-10 |
| 3 | 8 | 2018-11-15 |
-------------------------------------------
Intermediate result show when will the subscription end for each user (only month is needed):
------------------------------
| user_id | enddate |
------------------------------
| 1 | 2017-08 |
| 2 | 2017-09 |
| 3 | 2018-11 |
| 4 | 2017-10 |
| 5 | 2017-10 |
------------------------------
This was achieved with the query:
Select user_id, DATE_FORMAT(max(end_date), '%Y-%m') AS as enddate
From subscription
Group by user_id
Order by end_date desc;
The final result must further filter the list, showing only how many users will be left with no subscription in each month, like this:
------------------------------
| count | month, year |
------------------------------
| 1 | 2017-08 |
| 1 | 2017-09 |
| 2 | 2017-10 |
| 1 | 2018-11 |
------------------------------
This is where I am stuck with no mysql ideas. Iterating through the results and counting is out of the question.
You could try arranging the results by the enddate, like this:
select count(user_id), DATE_FORMAT(max_end_date, '%Y-%m')as enddate
from (
select user_id, max(end_date) as max_end_date
From subscription
Group by user_id
) n
group by enddate
Order by enddate desc;
Try this -
Select COUNT(*), DATE_FORMAT(MAX(end_date), '%Y-%m') AS as enddate
From subscription
Group by user_id
Order by end_date desc;

(MySQL) converging & summing two tables by period

I have two tables.
+----+------------+------------+
| id | pay_date | payment |
+----+------------+------------+
| 1 | 2010-01-01 | 20000 |
| 1 | 2010-01-02 | 30000 |
| 1 | 2010-01-03 | 30000 |
| 1 | 2010-01-06 | 40000 |
| 2 | 2010-01-01 | 10000 |
| 2 | 2010-01-03 | 30000 |
| 2 | 2010-01-06 | 70000 |
+----+------------+------------+
+----+------------+------------+
| id | start_date | end_date |
+----+------------+------------+
| 1 | 2010-01-01 | 2010-01-05 |
| 1 | 2010-01-06 | 2010-01-30 |
| 2 | 2010-01-01 | 2010-01-05 |
| 2 | 2010-01-06 | 2010-01-10 |
+----+------------+------------+
And by converging two tables I want to make below table.
+----+------------+------------+------------+
| id | start_date | end_date | payment |
+----+------------+------------+------------+
| 1 | 2010-01-01 | 2010-01-05 | 80000 |
| 1 | 2010-01-06 | 2010-01-30 | 40000 |
| 2 | 2010-01-01 | 2010-01-05 | 40000 |
| 2 | 2010-01-06 | 2010-01-10 | 70000 |
+----+------------+------------+------------+
This table is sum of payment within fixed period by same id.
How can I make this table?
For the exact data you gave us, we can just join the second table to the first one on the condition that the pay date be between the start and end date in the first table, and that the id values match. But immediately there is an obvious edge case problem here. What happens if a pay date happens to overlap both the start and end dates in the second table? Then it is not clear to which range we should assign that payment. If we just use BETWEEN we will end up double counting the payment.
So in my query below, I make the assumption that a pay date gets assigned if it be greater than or equal to the start date, but strictly less than the end date. This may not be the logic you intend, but you're probably going to have make an assumption similar to this if you ever have overlapping data.
SELECT
t2.id, t2.start_date, t2.end_date, SUM(t1.payment) AS payment
FROM table2 t2
LEFT JOIN table1 t1
ON t1.id = t2.id AND
t1.pay_date >= t2.start_date AND
t1.pay_date < t2.end_date
GROUP BY
t2.id, t2.start_date, t2.end_date
ORDER BY
t2.id, t2.start_date;
Output:
Demo here:
Rextester
Sum up the payment column having pay_date between start_date and end_date in the other table.
Query
select t1.`id`, t1.`start_date`, t1.`end_date`,
sum(t2.`payment`) as `payment`
from `table2` as t1
join `table1` as t2
on t1.`id` = t2.`id`
and t2.`pay_date` between t1.`start_date` and t1.`end_date`
group by t1.`id`, t1.`start_date`, t1.`end_date`;