(MySQL) converging & summing two tables by period - mysql

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`;

Related

Retrieving data from the row containing the closest date to today with whereDate

I want to sort by price after filtering and grouping by date. However, because there are more than one relation, I cannot get the result I want.
The result I want is to get the price of the relation that is the closest to the end_date and sort it accordingly.
For this, the query, sql output, tables and demo page are as follows.
Thanks in advance ..
demo sqlfiddle
$query->join('tableB', 'tableA.id', '=', 'tableB.pro_id')
->select('tableA.*', 'tableB.start_date', 'tableB.end_date', 'tableB.old_daily')
->where(function($sq) {
$today = Carbon::now()->format('Y-m-d');
$sq->whereDate('end_date', '>=', $today);
})
->groupBy('tableA.id')
->orderBy('price', desc);
Query:
select `tableA`.*, `tableB`.`start_date`, `tableB`.`end_date`, `tableB`.`price`
from `tableA`
inner join `tableB` on `tableA`.`id` = `tableB`.`pro_id`
where (date(`end_date`) >= 2021-03-07)
group by `tableA`.`id`
order by `price` desc
tableA
| id | title |
|----|-------|
| 1 | pro1 |
| 2 | pro2 |
| 3 | pro3 |
tableB
| id | start_date | end_date | price | pro_id |
|----|------------|------------|-------|--------|
| 1 | 2021-06-01 | 2021-06-05 | 750 | 2 |
| 2 | 2021-05-01 | 2021-05-05 | 850 | 2 |
| 3 | 2021-04-01 | 2021-04-05 | 650 | 2 |
| 4 | 2021-06-01 | 2021-06-05 | 2750 | 1 |
| 5 | 2021-05-01 | 2021-05-05 | 2850 | 1 |
| 6 | 2021-04-01 | 2021-04-05 | 2650 | 1 |
| 7 | 2021-06-01 | 2021-06-05 | 1750 | 3 |
| 8 | 2021-05-01 | 2021-05-05 | 1850 | 3 |
| 9 | 2021-04-01 | 2021-04-05 | 1650 | 3 |
this query gives the result you want.
It would be a good choice to use "right join" in this step.
sqlfiddle
select `tableA`.*, `tableB`.`start_date`, `tableB`.`end_date`, `tableB`.`price`
from `tableA`
right join(
SELECT id, start_date, end_date, pro_id, price, DATEDIFF(`tableB`.`end_date`, '2021-03-07') diff
FROM `tableB`
GROUP BY id order by diff asc
) `tableB` on `tableA`.`id` = `tableB`.`pro_id`
where (date(`end_date`) >= '2021-03-07')
group by `tableA`.`id`
order by `price` desc
the closest to the end_date and sort it accordingly.
you should find the difference between the given date and end date then sort ascendingly.
ORDER BY DATEDIFF(end_date, '2021-03-07') ASC

Mysql Sum column of overlap dates between 2 tables

Table A stores each stock amounts by days :
+---+------------+----------+-------------+
|id | Stock_id | amount | Date |
+---+------------+----------+-------------+
| 1 | 1 | 100 | 2017-09-05 |
| 2 | 1 | 200 | 2017-09-06 |
| 3 | 1 | 300 | 2017-09-07 |
| 4 | 1 | 200 | 2017-09-08 |
| 5 | 1 | 200 | 2017-09-09 |
| 6 | 1 | 200 | 2017-09-10 |
| 7 | 2 | 300 | 2017-09-06 |
+---+------------+----------+-------------+
Table B contains relationship between stock id and category. Add date indicates when the stock was added to the category and remove date means the day which the stock was removed. if remove date is null , it means the stock is still in the category.
+---+----------+ ------- +------------+------------+
|id | Stock_id |Category | Add Date | Remove Date|
+---+----------+-------- +------------+------------+
| 1 | 1 | Category1| 2017-09-03 | 2017-09-07 |
| 2 | 1 | Category1|2017-09-09 | null |
+---+----------+-------- +------------+------------+
My questions is give a time range, like from 2017-09-05 to 2017-09-08. first for the Stock1 and Category1, I want calculate the time overlap with table b, which is <2017-09-05 to 2017-09-06>. Then sum the amount in table A from 2017-09-05 to 2017-09-06. the result is (100+200) = 300. if time range is 2017-09-06 to 2017-09-10, the overlap is <2017-09-06, 2017-09-09 to 2017-09-10>. sum result is (200+200+200)=600.
How can I do it?Thank you all!
If I understand correctly, you want want the amount of stock in each category for a given range of dates. If so, this is a matter of joining the tables correctly and aggregating:
select a.stock_id, b.category, sum(a.amount)
from a join
b
on a.stock_id = b.stock_id and a.date >= a.add_date and
(a.date <= b.remove_date or b.remove_date is null)
where a.date >= '2017-09-05' and b.date <= '2017-09-08'
group by a.stock_id, b.category;

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

+-----------+------------+------------+
| 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;

In MySQL, set value in each row to a DATEDIFF computation on the same rows

It was very tricky to figure out what to title this question, so if anyone has any ideas for improvements feel free to edit :-).
Here's the deal. I have a MySQL table that includes a bunch of donations, and there's a date for each donation. I also have a years_active column. I need to run a query that will SET the years active for every row to the difference (in years) from the first date to the last date for each unique user.
So this is my starting table:
------------------------------------------------------------
| user_id | donation | date | years_active |
------------------------------------------------------------
| 1 | $10 | 2002-01-01 | null |
| 1 | $15 | 2005-01-01 | null |
| 1 | $20 | 2009-01-01 | null |
| 2 | $10 | 2003-01-01 | null |
| 2 | $5 | 2006-01-01 | null |
| 3 | $15 | 2001-01-01 | null |
------------------------------------------------------------
And this is the table I'd like to achieve:
------------------------------------------------------------
| user_id | donation | date | years_active |
------------------------------------------------------------
| 1 | $10 | 2002-01-01 | 8 |
| 1 | $15 | 2005-01-01 | 8 |
| 1 | $20 | 2009-01-01 | 8 |
| 2 | $10 | 2003-01-01 | 4 |
| 2 | $5 | 2006-01-01 | 4 |
| 3 | $15 | 2001-01-01 | 1 |
------------------------------------------------------------
I know that it's far from ideal to be storing the years_active redundantly in multiple rows like this. Unfortunately this table is for data visualizations and with my software I have absolutely no ability to restructure the data altogether; the years_active MUST be in every row.
In my research it seems like I would use subqueries to get the MIN value for each user id and the MAX value for each unique user id, and then do a DATEDIFF on those, and set the result to the column. But I don't really understand how I would run all these queries over and over again for every unique user.
Can someone point me in the right direction? Is this possible?
SELECT t1.user_id, t1.donation, t1.date, t2.years_active
FROM yourTable t1
INNER JOIN
(
SELECT user_id, MAX(YEAR(date)) - MIN(YEAR(date)) + 1 AS years_active
FROM yourTable
GROUP BY user_id
) t2
ON t1.user_id = t2.user_id
Follow the link below for a running demo:
SQLFiddle
Update:
Here is an UPDATE statement which will assign the years_active column the correct values:
UPDATE yourTable t1
INNER JOIN
(
SELECT user_id, MAX(YEAR(date)) - MIN(YEAR(date)) + 1 AS years_active
FROM yourTable
GROUP BY user_id
) t2
ON t1.user_id = t2.user_id
SET t1.years_active = t2.years_active

MySQL query order by date desc, ignore other records

I have a MySql table with the following data:
| ID | House | Date |
| 1 | A | 2015-03-13 15:56:59 |
| 2 | A | 2015-03-11 12:19:45 |
| 3 | A | 2015-03-06 00:00:00 |
| 4 | B | 2015-03-13 16:07:21 |
| 5 | B | 2015-03-11 13:02:22 |
I'm trying to get the following results:
| ID | House | Date |
| 1 | A | 2015-03-13 15:56:59 |
| 4 | B | 2015-03-13 16:07:21 |
I've tried using subqueries and other types of things. Any ideas of what I could use?
Query:
select h1.id, h1.house, h1.date
from house h1 left join house h2
on h1.house = h2.house and h1.date < h2.date
where h2.date is null
This query makes no assumptions about the ordering of the data.
Demo: http://sqlfiddle.com/#!9/2da11/1
Assuming the table name is tableName
Select tbl.id, temp.house,
temp.datecol
from tableName tbl,
(
select max(tbl2.datecol),
tbl2.house
from tableName tbl2
group by tbl2.house
) temp
where tbl.house=temp.house
order by temp.datecol asc;
This can be done in a simpler way.
SELECT id, house, date FROM house WHERE date > '2015-03-13'