7.12.1 Problem
You want to group rows into subgroups based on values calculated from an expression.
7.12.2 Solution
Put the expression in the GROUP BY clause. For older versions of MySQL that don't support GROUP BY expressions, use a workaround.
7.12.3 Discussion
GROUP BY shares the property with ORDER BY that as of MySQL 3.23.2 it can refer to expressions. This means you can use calculations as the basis for grouping. For example, to find the distribution of the length of state names, group by LENGTH(name):
mysql> SELECT LENGTH(name), COUNT(*) -> FROM states GROUP BY LENGTH(name); +--------------+----------+ | LENGTH(name) | COUNT(*) | +--------------+----------+ | 4 | 3 | | 5 | 3 | | 6 | 5 | | 7 | 8 | | 8 | 12 | | 9 | 4 | | 10 | 4 | | 11 | 2 | | 12 | 4 | | 13 | 3 | | 14 | 2 | +--------------+----------+
Prior to MySQL 3.23.2, you cannot use expressions in GROUP BY clauses, so the preceding query would fail. In Recipe 6.4, workarounds for this problem were given with regard to ORDER BY, and the same methods apply to GROUP BY. One workaround is to give the expression an alias in the output column list and refer to the alias in the GROUP BY clause:
mysql> SELECT LENGTH(name) AS len, COUNT(*) -> FROM states GROUP BY len; +------+----------+ | len | COUNT(*) | +------+----------+ | 4 | 3 | | 5 | 3 | | 6 | 5 | | 7 | 8 | | 8 | 12 | | 9 | 4 | | 10 | 4 | | 11 | 2 | | 12 | 4 | | 13 | 3 | | 14 | 2 | +------+----------+
Another is to write the GROUP BY clause to refer to the output column position:
mysql> SELECT LENGTH(name), COUNT(*) -> FROM states GROUP BY 1; +--------------+----------+ | LENGTH(name) | COUNT(*) | +--------------+----------+ | 4 | 3 | | 5 | 3 | | 6 | 5 | | 7 | 8 | | 8 | 12 | | 9 | 4 | | 10 | 4 | | 11 | 2 | | 12 | 4 | | 13 | 3 | | 14 | 2 | +--------------+----------+
Of course, these alternative ways of writing the query work in MySQL 3.23.2 and up as welland you may find them more readable.
You can group by multiple expressions if you like. To find days of the year on which more than one state joined the Union, group by statehood month and day, then use HAVING and COUNT( ) to find the non-unique combinations:
mysql> SELECT MONTHNAME(statehood), DAYOFMONTH(statehood), COUNT(*) -> FROM states GROUP BY 1, 2 HAVING COUNT(*) > 1; +----------------------+-----------------------+----------+ | MONTHNAME(statehood) | DAYOFMONTH(statehood) | COUNT(*) | +----------------------+-----------------------+----------+ | February | 14 | 2 | | June | 1 | 2 | | March | 1 | 2 | | May | 29 | 2 | | November | 2 | 2 | +----------------------+-----------------------+----------+
Using the mysql Client Program
Writing MySQL-Based Programs
Record Selection Techniques
Working with Strings
Working with Dates and Times
Sorting Query Results
Generating Summaries
Modifying Tables with ALTER TABLE
Obtaining and Using Metadata
Importing and Exporting Data
Generating and Using Sequences
Using Multiple Tables
Statistical Techniques
Handling Duplicates
Performing Transactions
Introduction to MySQL on the Web
Incorporating Query Resultsinto Web Pages
Processing Web Input with MySQL
Using MySQL-Based Web Session Management
Appendix A. Obtaining MySQL Software
Appendix B. JSP and Tomcat Primer
Appendix C. References