The pivot logic that reports expenses by month and year works just as well for sales. In the usual order entry system with customers, orders, products, orders, order items and payments (leaving out irrelevant details) ...

-- ---------------------------------------------------------------------------
-- Monthly sales
--
-- from the Artful Common Queries page
-- http://www.artfulsoftware.com/infotree/qrytip.php?id=1222
-- ---------------------------------------------------------------------------

-- The pivot logic that reports expenses by month and year works just as well
-- for sales. In the usual order entry system with customers, orders, products,
-- orders, order items and payments (leaving out irrelevant details) ...

drop table if exists items, payments, products, orders, customers;

create table customers(cust_id int primary key) engine=innodb;

create table orders(
  order_id int primary key,
  cust_id int,
  order_date date,
  foreign key(cust_id) references customers(cust_id)  -- ORDER->CUST FK
) engine=innodb;

create table products(prod_id int primary key) engine=innodb;

create table items(
  item_id int primary key,
  order_id int,
  prod_id int,
  qty int,
  foreign key(order_id) references orders(order_id),  -- ITEM->ORDER FK
  foreign key(prod_id) references products(prod_id)   -- ITEM->PROD FK
) engine=innodb;

create table payments(
  pmt_id int primary key,
  order_id int,
  amount decimal(8,2),
  foreign key(order_id) references orders(order_id)   -- PAYMENT->ORDER FK
) engine=innodb;

-- ... this query pivots to summarise sales by month and year:

select
  IfNull(Year,'Totals') Year,
  Format( `Jan`,2) as Jan,
  Format( `Feb`,2)as Feb,
  Format( `Mar`,2)as Mar,
  Format( `Apr`,2)as Apr,
  Format( `May`,2)as May,
  Format( `Jun`,2)as Jun,
  Format( `Jul`,2)as Jul,
  Format( `Aug`,2)as Aug,
  Format( `Sep`,2)as Sep,
  Format( `Oct`,2)as Oct,
  Format( `Nov`,2)as Nov,
  Format( `Dec`,2)as `Dec`,
  Qty AS Count,
  Format(Yrly,2) as 'Yrly Total'
from (
  select
    year(o.order_date) AS 'Year',
    sum(if(month(order_date)= 1, amount, 0)) as Jan,
    sum(if(month(order_date)= 2, amount, 0)) as Feb,
    sum(if(month(order_date)= 3, amount, 0)) as Mar,
    sum(if(month(order_date)= 4, amount, 0)) as Apr,
    sum(if(month(order_date)= 5, amount, 0)) as May,
    sum(if(month(order_date)= 6, amount, 0)) as Jun,
    sum(if(month(order_date)= 7, amount, 0)) as Jul,
    sum(if(month(order_date)= 8, amount, 0)) as Aug,
    sum(if(month(order_date)= 9, amount, 0)) as Sep,
    sum(if(month(order_date)=10, amount, 0)) as Oct,
    sum(if(month(order_date)=11, amount, 0)) as Nov,
    sum(if(month(order_date)=12, amount, 0)) as `Dec`,
    count(p.prod_id) AS Qty,
    round(sum($.amount),2) AS Yrly
  from orders o
  join items    i on o.order_id = i.order_id
  join products p on i.prod_id = p.prod_id
  join payments $ on o.order_id = $.order_id
  group by year
  with rollup
) sums ;