Если вы хотите, чтобы это было в одном запросе, вы можете попробовать что-то вроде этого:
SELECT
status_id
, COUNT(*) AS `Total overall`
, SUM(IF(YEAR(appointment_date) = YEAR(CURDATE()), 1, 0)) AS `Total this year`
, SUM(IF(YEAR(appointment_date) = YEAR(CURDATE()) - 1, 1, 0)) AS `Total last year`
FROM
appointment
WHERE
contact_id = 1
GROUP BY
status_id
=>
status_id total total_this_year total_last_year
Attended 2 1 1
Cancelled 1 0 1
Not Complete 1 1 0
Приведенный выше запрос не вернет информацию для «Не показывать». Если вам действительно нужны все статусы, вы можете сделать что-то вроде этого:
SELECT
s.status_id
, IFNULL(a.total, 0) AS `Total overall`
, IFNULL(a.total_this_year, 0) AS `Total this year`
, IFNULL(a.total_last_year, 0) AS `Total last year`
FROM
(
SELECT
DISTINCT status_id
FROM
appointment
) AS s
LEFT JOIN (
SELECT
status_id
, COUNT(*) AS `total`
, SUM(IF(YEAR(appointment_date) = YEAR(CURDATE()), 1, 0)) AS `total_this_year`
, SUM(IF(YEAR(appointment_date) = YEAR(CURDATE()) - 1, 1, 0)) AS `total_last_year`
FROM
appointment
WHERE
contact_id = 1
GROUP BY
status_id
) AS `a` ON (
a.status_id = s.status_id
)
=>
status_id Total overall Total this year Total last year
Attended 2 1 1
Cancelled 1 0 1
Not Complete 1 1 0
No Show 0 0 0
P.S. Мне не нравятся вычисления по годам, которые я использовал в запросе, однако, для простоты я оставляю их здесь.
В реальной жизни я бы использовал сравнение диапазона appointment_date BETWEEN CONCAT(YEAR(CURDATE()), '-01-01') AND CONCAT(YEAR(CURDATE()), '-12-31')
вместо YEAR(appointment_date) = YEAR(CURDATE())
.