I want to count status change by id from to
I have a mysql status table which has a name with pre implementation, implementation and operations states.
- a project contains project id, name, start date fields.
- both status and projects have a many to many relationship called project_status table contains project_id, status_id, date_of_progress.
so i want to count all projects within this month who has update/change their status_id
- from pre-implementation to implementations
- from implementations to operations
- from pre-implementation to operations
—
CREATE TABLE `status` ( `status_id` int(11) NOT NULL, `status_name` varchar(30) NOT NULL, `description` varchar(255) DEFAULT NULL, `status_name_tg` varchar(50) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `status` (`status_id`, `status_name`, `description`, `status_name_tg`) VALUES (1, 'Pre Implementation', 'Operational', 'Pre Implementation'), (2, 'Implementation', NULL, 'Implementation'), (3, 'Operational', NULL, 'Operational'), (4, 'Inactive', NULL, 'Inactive'), (5, 'Cancellation', NULL, 'Cancellation'); CREATE TABLE `project_status` ( `project_status_id` int(11) NOT NULL, `status_id` int(11) NOT NULL, `time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `project_id` int(11) NOT NULL, `reason_for_cancellation` varchar(255) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `project_status` (`project_status_id`, `status_id`, `time`, `project_id`, `reason_for_cancellation`) VALUES (1, 1, '2019-02-24 21:51:50', 1, NULL), (2, 2, '2019-03-26 21:52:57', 1, ' '), (3, 1, '2019-04-30 21:57:57', 2, NULL), (4, 1, '2019-05-26 22:04:08', 3, NULL), (5, 3, '2019-08-24 22:06:36', 1, ' '), (6, 2, '2019-08-11 22:07:05', 3, ' '), (8, 1, '2019-08-01 00:14:41', 6, NULL), (9, 1, '2019-08-09 12:11:22', 7, NULL), (10, 1, '2019-08-09 12:15:22', 8, NULL), (11, 3, '2019-08-14 10:07:49', 7, NULL), (12, 2, '2019-08-14 10:10:45', 8, NULL), (13, 2, '2019-08-26 17:16:02', 6, 'NULL'); (14, 3, '2019-08-26 17:16:02', 6, 'NULL'); CREATE TABLE `projects` ( `project_id` int(11) NOT NULL, `name` varchar(150) NOT NULL, `start_date` datetime NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `projects` (`project_id`, `name`, `start_date`) VALUES (1, 'site A', '2019-02-01 00:00:00'), (2, 'site B', '2019-03-12 00:00:00'), (3, 'Site C', '2019-04-04 00:00:00'), (4, 'Site D', '2019-05-03 00:00:00'), (5, 'site E', '2019-06-01 00:00:00'), (6, 'site F', '2019-08-02 00:00:00'), (7, 'Site J', '2019-08-01 00:00:00'), (8, 'Site H', '2019-08-05 00:00:00');
Desire Output
|pre - implementations|implementations - operations| pre - operations| |2 | 1 |2 |
Advertisement
Answer
I think the key is find what is the last status for each project on each month:
SELECT YEAR(time) as year , MONTH(time) as month , project_id , MAX(status_id) as last_status FROM project_status WHERE status_id < 4 -- ignore (inactive, cancellation) GROUP BY YEAR(time), MONTH(time), project_id;
OUTPUT
+------+-------+------------+-------------+ | year | month | project_id | last_status | +------+-------+------------+-------------+ | 2019 | 2 | 1 | 1 | | 2019 | 3 | 1 | 2 | | 2019 | 4 | 2 | 1 | | 2019 | 5 | 3 | 1 | | 2019 | 8 | 1 | 3 | | 2019 | 8 | 3 | 2 | | 2019 | 8 | 6 | 2 | | 2019 | 8 | 7 | 3 | | 2019 | 8 | 8 | 2 | +------+-------+------------+-------------+
Now using a correlated query you can found out the last status before the current month:
SELECT *, (SELECT MAX(p.status_id) FROM project_status p WHERE p.time < CONCAT(t.year,'/', t.month,'/1') AND p.project_id = t.project_id ) as previous_status FROM ( SELECT YEAR(time) as year , MONTH(time) as month , project_id , MAX(status_id) as last_status FROM project_status WHERE status_id < 4 GROUP BY YEAR(time), MONTH(time), project_id ) t
OUTPUT
+------+-------+------------+-------------+-----------------+ | year | month | project_id | last_status | previous_status | +------+-------+------------+-------------+-----------------+ | 2019 | 2 | 1 | 1 | | | 2019 | 3 | 1 | 2 | 1 | | 2019 | 4 | 2 | 1 | | | 2019 | 5 | 3 | 1 | | | 2019 | 8 | 1 | 3 | 2 | | 2019 | 8 | 3 | 2 | 1 | | 2019 | 8 | 6 | 2 | | | 2019 | 8 | 7 | 3 | | | 2019 | 8 | 8 | 2 | | +------+-------+------------+-------------+-----------------+
Now just do some conditional counts
SELECT q.year , q.month , COUNT(CASE WHEN q.last_status = 2 THEN 1 END) as pre_implementation , COUNT(CASE WHEN q.last_status = 3 AND q.previous_status = 2 THEN 1 END) as implementation_operation , COUNT(CASE WHEN q.last_status = 3 THEN 1 END) as pre_operation FROM ( SELECT *, (SELECT MAX(p.status_id) FROM project_status p WHERE p.time < CONCAT(t.year,'/', t.month,'/1') AND p.project_id = t.project_id ) as previous_status FROM ( SELECT YEAR(time) as year , MONTH(time) as month , project_id , MAX(status_id) as last_status FROM project_status WHERE status_id < 4 GROUP BY YEAR(time), MONTH(time), project_id ) t ) q GROUP BY q.year, q.month
OUTPUT
+------+-------+--------------------+--------------------------+---------------+ | year | month | pre_implementation | implementation_operation | pre_operation | +------+-------+--------------------+--------------------------+---------------+ | 2019 | 2 | 0 | 0 | 0 | | 2019 | 3 | 1 | 0 | 0 | | 2019 | 4 | 0 | 0 | 0 | | 2019 | 5 | 0 | 0 | 0 | | 2019 | 8 | 3 | 1 | 2 | +------+-------+--------------------+--------------------------+---------------+