MySQL query to show running total per week - mysql

I have a table where a close date will be updated upon close...until that time the field is null. I need to show a close trend grouped every 7 days from the start. So for example if there are 10 rows and 2 were closed in the first week then the total for that week should be 8. I have been able to create a query that works to show how many were closed in each week but I am struggling to find a way to account for the previous weeks totals.
Table
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `test_data`;
CREATE TABLE `test_data` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`close_date` date DEFAULT NULL,
`location` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8;
INSERT INTO `test_data` VALUES ('1', '2015-02-02', 'one');
INSERT INTO `test_data` VALUES ('2', '2015-02-02', 'one');
INSERT INTO `test_data` VALUES ('3', '2015-02-09', 'one');
INSERT INTO `test_data` VALUES ('4', '2015-02-09', 'one');
INSERT INTO `test_data` VALUES ('5', '2015-02-09', 'one');
INSERT INTO `test_data` VALUES ('6', '2015-02-16', 'one');
INSERT INTO `test_data` VALUES ('7', '2015-02-16', 'one');
INSERT INTO `test_data` VALUES ('8', '2015-02-16', 'one');
INSERT INTO `test_data` VALUES ('9', '2015-02-16', 'one');
INSERT INTO `test_data` VALUES ('10', '2015-02-16', 'one');
INSERT INTO `test_data` VALUES ('11', '2015-02-02', 'two');
INSERT INTO `test_data` VALUES ('12', '2015-02-02', 'two');
INSERT INTO `test_data` VALUES ('13', '2015-02-09', 'two');
INSERT INTO `test_data` VALUES ('14', '2015-02-09', 'two');
INSERT INTO `test_data` VALUES ('15', '2015-02-09', 'two');
INSERT INTO `test_data` VALUES ('16', '2015-02-16', 'two');
INSERT INTO `test_data` VALUES ('17', '2015-02-16', 'two');
INSERT INTO `test_data` VALUES ('18', '2015-02-16', 'two');
INSERT INTO `test_data` VALUES ('19', '2015-02-16', 'two');
INSERT INTO `test_data` VALUES ('20', '2015-02-16', 'two');
Query so far
select
'2015-02-02' + INTERVAL (DATEDIFF(test_data.close_date, '2015-02-02') DIV 7) WEEK as start_week,
(SELECT COUNT(*) FROM test_data WHERE location = 'one') - COUNT(a.id) AS one,
(SELECT COUNT(*) FROM test_data WHERE location = 'two') - COUNT(b.id) AS two
from test_data
left join test_data as a on a.id = test_data.id and a.location = 'one'
left join test_data as b on b.id = test_data.id and b.location = 'two'
where test_data.close_date >= '2015-02-02'
group by DATEDIFF(test_data.close_date,'2015-02-02') DIV 7
Output
start_week one two
2015-02-02 8 8
2015-02-09 7 7
2015-02-16 5 5
Output I am trying to achieve
start_week one two
2015-02-02 8 8
2015-02-09 5 5
2015-02-16 0 0
A push in the right direction is appreciated as I continue to work on this.
EDIT: More explanation of expected results.
As you can see in "Output" it is calculating the number of closed subtracted from the total. For location "one" there are 10 entries and in week 1 there were 2 closed (so 8 were still open in week 1), in week 2 3 more were closed giving the total closed since the beginning as 5 to the total in week 2 should be 5 instead.
So for my desired output.
start_week one
2015-02-02 8 <-- Total 10 - 2 closed in this week = 8
2015-02-09 5 <-- Total 10 - (3 closed in this week + the 2 in week 1) = 5
2015-02-16 0 <-- Total 10 - (5 closed in this week + the previous weeks) = 0

Columns a1 and a2 are the answers you are looking for. I tried this without the derived table did not get the correct answer. There is either a mysql bug or a misunderstanding on my part. But with the derived table it seems to work fine.
SELECT
sums.start_week,sums.*,
sums.tot1-sums.cur1-sums.back1 a1,sums.tot2-sums.cur1-sums.back2 a2
FROM
( /* Derived table cf counts */
SELECT DATEDIFF(test_data.close_date,'2015-02-02') DIV 7 AS sd,
'2015-02-02' + INTERVAL (DATEDIFF(test_data.close_date, '2015-02-02') DIV 7) WEEK as start_week,
(select COUNT(*) from test_data d1 where d1.location='one') tot1,
(select COUNT(*) from test_data d2 where d2.location='two') tot2,
count(CASE WHEN test_data.location='one' then 1 else null end ) cur1,
count(CASE WHEN test_data.location='two' then 1 else null end
) cur2,
(select count(*) from test_data d1 where d1.location='one' AND d1.close_date<
('2015-02-02' + INTERVAL (DATEDIFF(test_data.close_date, '2015-02-02') DIV 7) WEEK) ) as back1,
(select count(*) from test_data d2 where d2.location='two' AND d2.close_date<
('2015-02-02' + INTERVAL (DATEDIFF(test_data.close_date, '2015-02-02') DIV 7) WEEK) ) as back2
from test_data
where test_data.close_date >= '2015-02-02'
group by DATEDIFF(test_data.close_date,'2015-02-02') DIV 7 ,
'2015-02-02' + INTERVAL (DATEDIFF(test_data.close_date, '2015-02-02') DIV 7) WEEK
) sums

Related

Select the oldest record of a certain group until it changes pattern, in SQL

I am trying to get the oldest record for every status update/change in the following table.
Table (status_updates) :
id
entity_id
status
date
7
2
Approved
2022-02-10
6
2
Approved
2022-02-05
5
2
Approved
2022-02-04
4
2
OnHold
2022-02-04
3
2
OnHold
2022-02-03
2
2
Approved
2022-02-02
1
2
Approved
2022-02-01
Result Needed :
id
entity_id
status
date
5
2
Approved
2022-02-04
3
2
OnHold
2022-02-03
1
2
Approved
2022-02-01
Tried :
select
`status`,
`created_at`
from
`status_updates`
left join
(select
`id`,
row_number() over (partition by status_updates.entity_id, status_updates.status order by status_updates.created_at asc) as sequence
from
`status_updates`)
as `oldest_history`
on
`oldest_history`.`id` = `shipper_credit_histories`.`id`
where `sequence` = 1
Result Achived :
id
entity_id
status
date
3
2
OnHold
2022-02-03
1
2
Approved
2022-02-01
Just using lag:
select s.*
from (
select id, status<>coalesce(lag(status) over (partition by entity_id order by id),'') status_change
from status_updates
) ids
join status_updates s using (id)
where status_change
here are the queries:
create table status_updates
(entity_id integer,
status varchar(32),
date date
);
insert into status_updates values (2, 'Approved', '2022-02-05');
insert into status_updates values (2, 'Approved', '2022-02-04');
insert into status_updates values (2, 'On Hold', '2022-02-04');
insert into status_updates values (2, 'On Hold', '2022-02-03');
insert into status_updates values (2, 'Approved', '2022-02-02');
insert into status_updates values (2, 'Approved', '2022-02-01');
select b.*
from status_updates a
right join status_updates b
on a.status=b.status and a.date=(b.date - interval 1 day)
where a.entity_id is null;
or this query(if you prefer left join)
select a.*
from status_updates a
left join status_updates b
on a.status=b.status and a.date=(b.date + interval 1 day)
where b.entity_id is null;
in both you will see the expected result
the second solution is almost the same, but join by id instead of date
create table status_updates
(id integer,
entity_id integer,
status varchar(32),
date date
);
insert into status_updates values (7, 2, 'Approved', '2022-02-10');
insert into status_updates values (6, 2, 'Approved', '2022-02-05');
insert into status_updates values (5, 2, 'Approved', '2022-02-04');
insert into status_updates values (4, 2, 'On Hold', '2022-02-04');
insert into status_updates values (3, 2, 'On Hold', '2022-02-03');
insert into status_updates values (2, 2, 'Approved', '2022-02-02');
insert into status_updates values (1, 2, 'Approved', '2022-02-01');
select a.*
from status_updates a
left join status_updates b
on a.status=b.status and a.id=b.id + 1
where b.entity_id is null;
result is the same what you expected

MySQL-Count consective number

Write a SQL query to find number position as well number and consective number count
CREATE TABLE Logs (
`Id` INTEGER,
`Num` INTEGER
);
INSERT INTO Logs
(`Id`, `Num`)
VALUES
('1', '1'),
('2', '1'),
('3', '1'),
('4', '2'),
('5', '1'),
('6', '2'),
('7', '2');
Prefere Return
StartId Num Count
1 1 3
4 2 1
5 1 1
6 2 2
and also can i get any suggestion which function can be use with case function in MySQL Function
Looking at your data and expected results, I believe your expectations are inconsistent, eg you can either have 1 and 6 or 3 and 7.
What you need to do is group the data by successive num values and aggregate the results.
with gp as (
select *,
Row_Number() over(order by id)
- Row_Number() over(partition by num order by id) g
from logs
)
select Min(id) Id,
num, Count(*) Count
from gp
group by g, num
order by id

Verify migrated records in SQL Server tables

I have a scenario where I transferred data from table t1 to t2. Not every record was transferred. I want to figure out which IDs were completely transferred. For example, I have a subset of data in which both IDs 23 and 25 has 5 records each with different section and subsections. Since only 2 records from ID 23 were transferred to t2, I do not want them in my resultant query. Whereas, for ID 25, all the records were transferred to t2. So, I want it to reflect in my result.
I got this far
select *
from t2
where exists (select * from t1
where t1.id = t2.id
and t1.section = t2.section
and t1.sub = t2.sub
group by id)
Table 1
id section sub
----------------
23 1 9
23 1 10
23 2 2
23 3 2
23 3 3
24 1 9
24 1 10
24 2 2
24 3 2
24 3 3
25 1 9
25 1 10
25 2 2
25 3 2
25 3 3
Table 2
id section sub
----------------
23 1 9
23 1 10
25 1 9
25 1 10
25 2 2
25 3 2
25 3 3
Required result:
id section sub
---------------
25 1 9
25 1 10
25 2 2
25 3 2
25 3 3
Code to create tables
CREATE TABLE t1
(
id varchar(3),
section varchar(4),
sub varchar(2)
)
CREATE TABLE t2
(
id varchar(3),
section varchar(4),
sub varchar(2)
)
INSERT INTO t1 (id, section, sub)
VALUES ('23', '1', '9')
INSERT INTO t1 (id, section, sub)
VALUES ('23', '1', '10')
INSERT INTO t1 (id, section, sub)
VALUES ('23', '2', '2')
INSERT INTO t1 (id, section, sub)
VALUES ('23', '3', '2')
INSERT INTO t1 (id, section, sub)
VALUES ('23', '3', '3')
INSERT INTO t1 (id, section, sub)
VALUES ('24', '1', '9')
INSERT INTO t1 (id, section, sub)
VALUES ('24', '1', '10')
INSERT INTO t1 (id, section, sub)
VALUES ('24', '2', '2')
INSERT INTO t1 (id, section, sub)
VALUES ('24', '3', '2')
INSERT INTO t1 (id, section, sub)
VALUES ('24', '3', '3')
INSERT INTO t1 (id, section, sub)
VALUES ('25', '1', '9')
INSERT INTO t1 (id, section, sub)
VALUES ('25', '1', '10')
INSERT INTO t1 (id, section, sub)
VALUES ('25', '2', '2')
INSERT INTO t1 (id, section, sub)
VALUES ('25', '3', '2')
INSERT INTO t1 (id, section, sub)
VALUES ('25', '3', '3')
INSERT INTO t2 (id, section, sub)
VALUES ('23', '1', '9')
INSERT INTO t2 (id, section, sub)
VALUES ('23', '1', '10')
INSERT INTO t2 (id, section, sub)
VALUES ('25', '1', '9')
INSERT INTO t2 (id, section, sub)
VALUES ('25', '1', '10')
INSERT INTO t2 (id, section, sub)
VALUES ('25', '2', '2')
INSERT INTO t2 (id, section, sub)
VALUES ('25', '3', '2')
INSERT INTO t2 (id, section, sub)
VALUES ('25', '3', '3')
You can try the following, just need to count the number of each ID in t2 and compare that to the same count in t1 where they are the same:
select id,section,sub from (
select *, Count(*) over(partition by id) c
from t2
)t
where c=(
select Count(*)
from t1
where t1.id=t.id
group by t1.id
)

How to get difference or delta of counts entries of each days with window functions?

I have a table with few fields like id, country, ip, created_at. Then I am trying to get the deltas between total entry of one day and total entry of the next day.
CREATE TABLE session (
id int NOT NULL AUTO_INCREMENT,
country varchar(50) NOT NULL,
ip varchar(255),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
);
INSERT INTO `session` (`id`, `country`, `ip`, `created_at`) VALUES
('1', 'IN', '10.100.102.11', '2021-04-05 20:26:02'),
('2', 'IN', '10.100.102.11', '2021-04-05 19:26:02'),
('3', 'US', '10.120.102.11', '2021-04-17 10:26:02'),
('4', 'US', '10.100.112.11', '2021-04-16 12:26:02'),
('5', 'AU', '10.100.102.122', '2021-04-12 19:36:02'),
('6', 'AU', '10.100.102.122', '2021-04-12 18:20:02'),
('7', 'AU', '10.100.102.122', '2021-04-12 23:26:02'),
('8', 'US', '10.100.102.2', '2021-04-16 21:33:01'),
('9', 'AU', '10.100.102.122', '2021-04-18 20:46:02'),
('10', 'AU', '10.100.102.111', '2021-04-04 13:19:12'),
('11', 'US', '10.100.112.11', '2021-04-16 12:26:02'),
('12', 'IN', '10.100.102.11', '2021-04-05 15:26:02'),
('13', 'IN', '10.100.102.11', '2021-04-05 19:26:02');
Now I have written this query to get the delta
SELECT T1.date1 as date, IFNULL(T1.cnt1-T2.cnt2, T1.cnt1) as delta from (
select TA.dateA as date1, MAX(TA.countA) as cnt1 from (
select DATE(created_at) AS dateA, COUNT(*) AS countA
FROM session
GROUP BY DATE(created_at)
UNION
select DISTINCT DATE(DATE(created_at)+1) AS dateA, 0 AS countA
FROM session
) as TA
group by TA.dateA
) as T1
LEFT OUTER JOIN (
select DATE(DATE(created_at)+1) AS date2,
COUNT(*) AS cnt2
FROM session
GROUP BY DATE(created_at)
) as T2
ON T1.date1=T2.date2
ORDER BY date;
http://sqlfiddle.com/#!9/4f5fd26/60
Then I am getting the results as
date delta
2021-04-04 1
2021-04-05 3
2021-04-06 -4
2021-04-12 3
2021-04-13 -3
2021-04-16 3
2021-04-17 -2
2021-04-18 0
2021-04-19 -1
Now, is there any place of improvements/optimizes on it with/or window functions? (I am zero with SQL, still playing around).
Try a shorter version
with grp as (
SELECT t.dateA, SUM(t.cnt) AS countA
FROM session,
LATERAL (
select DATE(created_at) AS dateA, 1 as cnt
union all
select DATE(DATE(created_at)+1), 0 as cnt
) t
GROUP BY dateA
)
select t1.dateA as date, IFNULL(t1.countA-t2.countA, t1.countA) as delta
from grp t1
left join grp t2 on DATE(t2.dateA + 1) = t1.dateA
order by t1.dateA
db<>fiddle

sql update int column based on previous int column

I have a basic table FoodSales in which there is 3 columns SaleForDay decimal (10,2), SalesToDate decimal (10,2), ItemOrder Int
the data is like so
SaleForDay|SalesToDate|ItemOrder
4.99|4.99|1
12.99||2
14.99||3
5.99||4
I'm trying to get the result
SaleForDay|SalesToDate|ItemOrder
4.99|4.99|1
12.99|17.98|2
14.99|32.97|3
5.99|38.96|4
The method here is SalesToDate = the previous itemorder SalesToDate + the Current SaleforDay value.
To help out.
CREATE TABLE #FruitSales
(
SaleForDay DECIMAL (10, 2),
SalesToDate DECIMAL (10, 2),
ItemOrder INT
);
INSERT INTO #FruitSales (SaleForDay, SalesToDate, ItemOrder)
VALUES ('4.99', '4.99', '1'),
('12.99', NULL, '2'),
('14.99', NULL, '3'),
('5.99', NULL, '4');
SELECT * FROM #FruitSales;
DROP TABLE #FruitSales;
Thanks for the help, i realize this is probably simple as pie.
CREATE TABLE #FruitSales
(
SaleForDay DECIMAL (10, 2),
SalesToDate DECIMAL (10, 2),
ItemOrder INT
);
INSERT INTO #FruitSales (SaleForDay, SalesToDate, ItemOrder)
VALUES ('4.99', '4.99', '1'),
('12.99', NULL, '2'),
('14.99', NULL, '3'),
('5.99', NULL, '4');
update f
SET SalesToDate = (select sum(saleforday) from #FruitSales ff where ff.itemorder <= f.itemorder)
from #FruitSales f
SELECT * FROM #FruitSales;
Try this:
;WITH CumulativeData AS
(
SELECT b.ItemOrder, SUM(a.SaleForDay) AS TotalSales
FROM #FruitSales a INNER JOIN #FruitSales b
ON a.ItemOrder <= b.ItemOrder
GROUP BY b.ItemOrder
)
UPDATE a
SET a.SalesToDate = TotalSales
FROM #FruitSales a INNER JOIN CumulativeData b
ON a.ItemOrder = b.ItemOrder;
Another alternative:
UPDATE a
SET SalesToDate = TotalSales
FROM #FruitSales a CROSS APPLY
(SELECT SUM(SaleForDay) TotalSales FROM #FruitSales b WHERE b.ItemOrder <= a.ItemOrder) b