Pivot Table Using MySQL – A Complete Guide
Today, we will discuss how to create a pivot table using MySQL.
The term pivot tables usually used in Microsoft Excel. This technique typically used to make a summary of the data by converting rows into columns, this is what we will discuss here using MySQL.
In MySQL, we can not directly create a pivot table like in any other database that has a function named PIVOT, instead, we have to define the columns one by one.
This is in line with the concept that SQL is not a dynamic language that can perform loop such as for, while, foreach statement that exists in other programming languages such as PHP. As a static language, we have to define the query manually.
However, for dynamic data, we can still build a query dynamically. But it still not possible to write directly, we need to do some workaround, the discussion of this topic is quite long, so we do not discuss here.
I. The Preparation
To be able to create a pivot table using MySQL, of course, we need data, to make it easier, we will use just one table instead of multiple tables, named
sales_table
The table consists of five fields:
trx_id
, name
, trx_date
, team
, and amount
. The contents of the table looks like the following:trx_id | nama | trx_date | team | amout |
---|---|---|---|---|
1 | Alfa | 2016-01-10 | 1 | 250 |
2 | Charlie | 2016-01-02 | 2 | 175 |
3 | Bravo | 2016-01-01 | 1 | 310 |
4 | Bravo | 2016-02-04 | 1 | 250 |
5 | Alfa | 2016-01-15 | 1 | 300 |
6 | Charlie | 2016-01-13 | 2 | 325 |
7 | Bravo | 2016-02-07 | 1 | 275 |
8 | Bravo | 2016-03-06 | 1 | 150 |
9 | Alfa | 2016-02-05 | 1 | 215 |
10 | Alfa | 2016-02-22 | 1 | 350 |
11 | Alfa | 2016-02-02 | 1 | 450 |
12 | Alfa | 2016-03-12 | 1 | 150 |
13 | Alfa | 2016-03-17 | 1 | 225 |
14 | Bravo | 2016-03-11 | 1 | 150 |
15 | Bravo | 2016-03-18 | 1 | 150 |
16 | Charlie | 2016-01-23 | 2 | 350 |
17 | Charlie | 2016-02-22 | 2 | 300 |
18 | Charlie | 2016-03-21 | 2 | 275 |
19 | Charlie | 2016-03-12 | 2 | 450 |
20 | Delta | 2016-02-11 | 2 | 450 |
21 | Delta | 2016-02-17 | 2 | 550 |
22 | Delta | 2016-03-11 | 2 | 370 |
The above table quite informative, but we can make it more informative by converting rows into columns, which very useful when we need comparing the data horizontally.
II. Pivot Table Using MySQL
This time, we’ll create a pivot table by converting date transaction into columns, so the resulted table will provide information about the sales performance of each officer, each month. The SQL query looks like follows:
SELECT name,
SUM( IF( MONTH(trx_date) = 1, amount, 0) ) AS january,
SUM( IF( MONTH(trx_date) = 2, amount, 0) ) AS february,
SUM( IF( MONTH(trx_date) = 3, amount, 0) ) AS march,
SUM( amount ) AS total
FROM sales_table
GROUP BY name
The result:
+---------+---------+----------+-------+-------+
| name | january | february | march | total |
+---------+---------+----------+-------+-------+
| Alfa | 375 | 815 | 375 | 1565 |
| Bravo | 310 | 525 | 825 | 1660 |
| Charlie | 850 | 300 | 725 | 1875 |
| Delta | 0 | 1000 | 370 | 1370 |
+---------+---------+----------+-------+-------+
The resulted table is much more informative than the original table, we can easily compare sales data per month.
In the above query, we use the
SUM
function to calculate the data for each column, more about this topic can be read in the article: Using IF SUM to add up the value of the field / column of a table in MySQL
In addition, we also use the
MONTH
function to get the month of the date in the trx_date
column, if the result is 1, then we placed it into the January column, if 2 then to February column, and if 3 then to March column, last, we use SUM(nilai_trx)
to create the total column.Completing The Displayed Data
To be more informative, let’s add columns that contain a number of transactions. We change the query to be something like this:
SELECT
name,
COUNT( IF( MONTH(trx_date) = 1, amount, NULL) ) AS trx_1,
SUM( IF( MONTH(trx_date) = 1, amount, 0) ) AS january,
COUNT( IF( MONTH(trx_date) = 2, amount, NULL) ) AS trx_2,
SUM( IF( MONTH(trx_date) = 2, amount, 0) ) AS februay,
COUNT( IF( MONTH(trx_date) = 3, amount, NULL) ) AS trx_3,
SUM( IF( MONTH(trx_date) = 3, amount, 0) ) AS march,
COUNT(trx_date) AS trx_num,
SUM( amount ) AS total_trx
FROM sales_table
GROUP BY name
The result:
+---------+-------+---------+-------+---------+-------+-------+---------+-----------+
| name | trx_1 | january | trx_2 | februay | trx_3 | march | trx_num | total_trx |
+---------+-------+---------+-------+---------+-------+-------+---------+-----------+
| Alfa | 2 | 375 | 3 | 815 | 2 | 375 | 7 | 1565 |
| Bravo | 1 | 310 | 2 | 525 | 3 | 825 | 6 | 1660 |
| Charlie | 3 | 850 | 1 | 300 | 2 | 725 | 6 | 1875 |
| Delta | 0 | 0 | 2 | 1000 | 1 | 370 | 3 | 1370 |
+---------+-------+---------+-------+---------+-------+-------+---------+-----------+
In the query above, I add a blank line for readability purpose.
In the query above, we use the
COUNT
function to count the number of rows in the amount column, more about this function can be read in the article: Calculate Column on MySQL Table With Specific Conditions Using COUNT IF
Note that in the
COUNT
function, COUNT(IF(MONTH(tgl_trx) = 1, id_trx, NULL)) AS trx_1
, we use NULL
not 0
as we use on the SUM
function.
This is because the
COUNT
function will still count the rows with a value of 0
which in some condition will make inaccurate results, so we use NULL
instead of 0
III. Pivot Tables Using MySQL – Adding Total Row Using WITH ROLLUP Clause
We can still improve the above table by adding rows that contain total value of transactions per month, so let’s do it by running the following query:
SELECT
IFNULL(name, 'TOTAL') AS officer_name,
COUNT( IF( MONTH(trx_date) = 1, amount, NULL) ) AS trx_1,
SUM( IF( MONTH(trx_date) = 1, amount, 0) ) AS january,
COUNT( IF( MONTH(trx_date) = 2, amount, NULL) ) AS trx_2,
SUM( IF( MONTH(trx_date) = 2, amount, 0) ) AS februay,
COUNT( IF( MONTH(trx_date) = 3, amount, NULL) ) AS trx_3,
SUM( IF( MONTH(trx_date) = 3, amount, 0) ) AS march,
COUNT(trx_date) AS trx_num,
SUM( amount ) AS total
FROM sales_table
GROUP BY name
WITH ROLLUP
The result is:
+--------------+-------+---------+-------+---------+-------+-------+---------+-------+
| officer_name | trx_1 | january | trx_2 | februay | trx_3 | march | trx_num | total |
+--------------+-------+---------+-------+---------+-------+-------+---------+-------+
| Alfa | 2 | 375 | 3 | 815 | 2 | 375 | 7 | 1565 |
| Bravo | 1 | 310 | 2 | 525 | 3 | 825 | 6 | 1660 |
| Charlie | 3 | 850 | 1 | 300 | 2 | 725 | 6 | 1875 |
| Delta | 0 | 0 | 2 | 1000 | 1 | 370 | 3 | 1370 |
| TOTAL | 6 | 1535 | 8 | 2640 | 8 | 2295 | 22 | 6470 |
+--------------+-------+---------+-------+---------+-------+-------+---------+-------+
In the example above, there is a new row named TOTAL, so now we’ve been able to compare the number of sales and total of sales per month.
In the above query,
WITH ROLLUP
clause will add the total row, that clause will calculate any rows resulted by aggregate function. All of the columns of the resulted table are aggregated except officer_name column.
Furthermore, in the total row, MySQL will provide a
NULL
value in the column used in the GROUP BY
clause, which in the example above, the name
column.
To be meaningful, we replace the
NULL
value to a word of TOTAL, to do so, we use the IFNULL
function – IFNULL(name, 'TOTAL') AS officer_name
, which means if the value of the column is NULL
, then replace it with the word of TOTAL.Adding Subtotal Row at Pivot Table
Furthermore, in certain circumstances, we need to display a subtotal row. Related to the above example, in order to know the performance of each team, now, we will create subtotals based on the team value.
Query that we used:
SELECT
IFNULL( name, 'SUB TOTAL' ) AS officer_name,
IFNULL( team, 'TOTAL' ) AS officer_team,
COUNT( IF( MONTH(trx_date) = 1, amount, NULL) ) AS trx_1,
SUM( IF( MONTH(trx_date) = 1, amount, 0) ) AS january,
COUNT( IF( MONTH(trx_date) = 2, amount, NULL) ) AS trx_2,
SUM( IF( MONTH(trx_date) = 2, amount, 0) ) AS februay,
COUNT( IF( MONTH(trx_date) = 3, amount, NULL) ) AS trx_3,
SUM( IF( MONTH(trx_date) = 3, amount, 0) ) AS march,
COUNT(trx_date) AS trx_num,
SUM( amount ) AS total_trx
FROM sales_table
GROUP BY team, name
WITH ROLLUP
The result is:
+--------------+--------------+-------+---------+-------+---------+-------+-------+---------+-----------+
| officer_name | officer_team | trx_1 | january | trx_2 | februay | trx_3 | march | trx_num | total_trx |
+--------------+--------------+-------+---------+-------+---------+-------+-------+---------+-----------+
| Alfa | 1 | 2 | 375 | 3 | 815 | 2 | 375 | 7 | 1565 |
| Bravo | 1 | 1 | 310 | 2 | 525 | 3 | 825 | 6 | 1660 |
| SUB TOTAL | 1 | 3 | 685 | 5 | 1340 | 5 | 1200 | 13 | 3225 |
| Charlie | 2 | 3 | 850 | 1 | 300 | 2 | 725 | 6 | 1875 |
| Delta | 2 | 0 | 0 | 2 | 1000 | 1 | 370 | 3 | 1370 |
| SUB TOTAL | 2 | 3 | 850 | 3 | 1300 | 3 | 1095 | 9 | 3245 |
| SUB TOTAL | TOTAL | 6 | 1535 | 8 | 2640 | 8 | 2295 | 22 | 6470 |
+--------------+--------------+-------+---------+-------+---------+-------+-------+---------+-----------+
Note that there are new rows that represent SUB TOTAL, that rows added because we are adding a new column to the GROUP BY clause that is
team
, so that when the WITH ROLLUP
clause is executed, MySQL will calculate subtotals according to team
+ name
columns.
Notice that in the
GROUP BY
clause, the different order of the column will give a different result, you can try it by changing the order of the column from team, name
into name, team
and then see the result.Changing The Order of The Pivot Table Data
Notice that any data generated by the GROUP BY clause will be displayed sequentially, the data will be sorted ascending, started from the smallest to the largest value.
For example, in the previous query, the data in the officer_name column sorted alphabetically from smallest to the largest, as well as the
team
column.
Why be like that? this is because when running the
GROUP BY
clause, MySQL will also sort the data in ascending order (from smallest to largest) which is often called the implicit order.
We can change this behavior by adding
ASC
or DESC
option to the GROUP BY
clause. Suppose we want to change the order of the names and teams column into descending order.
Run the following query:
SELECT
IFNULL( name, 'SUB TOTAL' ) AS officer_name,
IFNULL( team, 'TOTAL' ) AS officer_team,
COUNT( IF( MONTH(trx_date) = 1, amount, NULL) ) AS trx_1,
SUM( IF( MONTH(trx_date) = 1, amount, 0) ) AS january,
COUNT( IF( MONTH(trx_date) = 2, amount, NULL) ) AS trx_2,
SUM( IF( MONTH(trx_date) = 2, amount, 0) ) AS februay,
COUNT( IF( MONTH(trx_date) = 3, amount, NULL) ) AS trx_3,
SUM( IF( MONTH(trx_date) = 3, amount, 0) ) AS march,
COUNT(trx_date) AS trx_num,
SUM( amount ) AS total_trx
FROM sales_table
GROUP BY team DESC, name DESC
WITH ROLLUP
The result:
+--------------+--------------+-------+---------+-------+---------+-------+-------+---------+-----------+
| officer_name | officer_team | trx_1 | january | trx_2 | februay | trx_3 | march | trx_num | total_trx |
+--------------+--------------+-------+---------+-------+---------+-------+-------+---------+-----------+
| Delta | 2 | 0 | 0 | 2 | 1000 | 1 | 370 | 3 | 1370 |
| Charlie | 2 | 3 | 850 | 1 | 300 | 2 | 725 | 6 | 1875 |
| SUB TOTAL | 2 | 3 | 850 | 3 | 1300 | 3 | 1095 | 9 | 3245 |
| Bravo | 1 | 1 | 310 | 2 | 525 | 3 | 825 | 6 | 1660 |
| Alfa | 1 | 2 | 375 | 3 | 815 | 2 | 375 | 7 | 1565 |
| SUB TOTAL | 1 | 3 | 685 | 5 | 1340 | 5 | 1200 | 13 | 3225 |
| SUB TOTAL | TOTAL | 6 | 1535 | 8 | 2640 | 8 | 2295 | 22 | 6470 |
+--------------+--------------+-------+---------+-------+---------+-------+-------+---------+-----------+
In the table above, the officer name and officer_team are sorted descendingly.
This method is the easiest way to change the order of the data, however, on MySQL version 5.7, this feature has been deprecated, meaning that it not recommended for use, because, in the future version this feature will be removed.
Because of that reason, It is recommended to use an explicit order by using the ORDER BY clause. In a query that contains the
WITH ROLLUP
clause, using the ORDER BY
clause is not an easy task.
This is because we must define the value of the column resulted by
WITH ROLLUP
clause such a way so that it will be placed at the bottom of the row. Consider the following example:SELECT * FROM
(
SELECT
IFNULL( name, '0-SUB TOTAL' ) AS officer_name,
IFNULL( team, '0-TOTAL' ) AS officer_team,
COUNT( IF( MONTH(trx_date) = 1, amount, NULL) ) AS trx_1,
SUM( IF( MONTH(trx_date) = 1, amount, 0) ) AS january,
COUNT( IF( MONTH(trx_date) = 2, amount, NULL) ) AS trx_2,
SUM( IF( MONTH(trx_date) = 2, amount, 0) ) AS februay,
COUNT( IF( MONTH(trx_date) = 3, amount, NULL) ) AS trx_3,
SUM( IF( MONTH(trx_date) = 3, amount, 0) ) AS march,
COUNT(trx_date) AS trx_num,
SUM( amount ) AS total_trx
FROM sales_table
GROUP BY team, name
WITH ROLLUP
) AS sales_table
ORDER BY officer_team DESC, officer_name DESC
The result is:
+--------------+--------------+-------+---------+-------+---------+-------+-------+---------+-----------+
| officer_name | officer_team | trx_1 | january | trx_2 | februay | trx_3 | march | trx_num | total_trx |
+--------------+--------------+-------+---------+-------+---------+-------+-------+---------+-----------+
| Delta | 2 | 0 | 0 | 2 | 1000 | 1 | 370 | 3 | 1370 |
| Charlie | 2 | 3 | 850 | 1 | 300 | 2 | 725 | 6 | 1875 |
| 0-SUB TOTAL | 2 | 3 | 850 | 3 | 1300 | 3 | 1095 | 9 | 3245 |
| Bravo | 1 | 1 | 310 | 2 | 525 | 3 | 825 | 6 | 1660 |
| Alfa | 1 | 2 | 375 | 3 | 815 | 2 | 375 | 7 | 1565 |
| 0-SUB TOTAL | 1 | 3 | 685 | 5 | 1340 | 5 | 1200 | 13 | 3225 |
| 0-SUB TOTAL | 0-TOTAL | 6 | 1535 | 8 | 2640 | 8 | 2295 | 22 | 6470 |
+--------------+--------------+-------+---------+-------+---------+-------+-------+---------+-----------+
In the above query, the subquery will create a temporary table in exact form with the previous example, then we sort that table in a descending order based on officer_team and officer_name column .
Note that in the above query, we give a prefix
0-
to the SUB TOTAL value, so that when the column sorted descendingly, that row will be placed at the bottom, this is same as the TOTAL value on the officer_team column.
You can learn more about calculating total and grand total in MySQL in the article: Calculating Total and Subtotal Using MySQL
IV. Pivot Table Using MySQL – Adding Total Row Using Independent Query
In the previous example, we can easily add total and subtotal row using the
WITH ROLLUP
clause, in certain circumstances, we can not rely on that clause, for example, when we want to display data based on the amount column in a descending order.
If we use the
WITH ROLLUP
clause, then the TOTAL row will always be placed on the top, this is because that row will always have the biggest value. To solve this, we can add a TOTAL row using an additional query with UNION ALL
clause like the following:SELECT * FROM (
SELECT
name,
team,
SUM( IF( MONTH(trx_date) = 1, amount, 0) ) AS january,
COUNT( IF( MONTH(trx_date) = 1, trx_date, NULL) ) AS trx_1,
SUM( IF( MONTH(trx_date) = 2, amount, 0) ) AS february,
COUNT( IF( MONTH(trx_date) = 2, amount, NULL) ) AS trx_2,
SUM( IF( MONTH(trx_date) = 3, amount, 0) ) AS march,
COUNT( IF( MONTH(trx_date) = 3, amount, NULL) ) AS trx_3,
COUNT(trx_date) AS jml_trx,
SUM( amount ) AS total
FROM sales_table
GROUP BY team, name
ORDER BY total DESC
) AS sales_table
UNION ALL
SELECT 'TOTAL' AS name, '' AS team,
SUM( IF( MONTH(trx_date) = 1, amount, 0) ) AS january,
COUNT( IF( MONTH(trx_date) = 1, trx_date, NULL) ) AS trx_1,
SUM( IF( MONTH(trx_date) = 2, amount, 0) ) AS february,
COUNT( IF( MONTH(trx_date) = 2, amount, NULL) ) AS trx_2,
SUM( IF( MONTH(trx_date) = 3, amount, 0) ) AS march,
COUNT( IF( MONTH(trx_date) = 3, amount, NULL) ) AS trx_3,
COUNT(trx_date) AS trx_num,
SUM( amount ) AS total
FROM sales_table
The result:
+---------+------+---------+-------+----------+-------+-------+-------+---------+-------+
| name | team | january | trx_1 | february | trx_2 | march | trx_3 | jml_trx | total |
+---------+------+---------+-------+----------+-------+-------+-------+---------+-------+
| Charlie | 2 | 850 | 3 | 300 | 1 | 725 | 2 | 6 | 1875 |
| Bravo | 1 | 310 | 1 | 525 | 2 | 825 | 3 | 6 | 1660 |
| Alfa | 1 | 375 | 2 | 815 | 3 | 375 | 2 | 7 | 1565 |
| Delta | 2 | 0 | 0 | 1000 | 2 | 370 | 1 | 3 | 1370 |
| TOTAL | | 1535 | 6 | 2640 | 8 | 2295 | 8 | 22 | 6470 |
+---------+------+---------+-------+----------+-------+-------+-------+---------+-------+
Note that in the above query, we put the first query in a subquery, this is because when using
UNION
, the ORDER BY
clause must be placed on the bottom, so, in order to sort the first data, we use subquery.V. Dynamic Pivot Table Using MySQL
In the above discussion, we define the columns of pivot table manually. In many circumstances, it is sufficient, however, on certain conditions, the number of columns are dynamics according to the number of rows.
In such conditions, the SQL syntax always changes, so the static method can not be used anymore, to solve this, we can use a technique known as dynamic pivot table. A discussion of this topic can be followed on Dynamic Pivot Table Using MySQL
VI. Wrap Up
Creating a pivot table using MySQL is a quite easy task, the main obstacle to this process is when we want to sort the data that contain subtotal and total rows generated by
WITH ROLLUP
clause.
This is because the total and subtotal row also sorted, so that rows has a lot of possibilities to be placed on the top, as an alternative, we can add a total row using independent query.
If you want to create a pivot table using PHP, you can follow the tutorial: Pivot Table Using PHP and MySQL
0 comments:
Post a Comment