Friday 30 August 2019

Understanding SQL compatibility modes in MySQL and MariaDB

MySQL and MariaDB have an SQL mode setting which changes how MySQL behaves.

The SQL mode value is comprised of multiple flags like "STRICT_TRANS_TABLES, NO_ZERO_IN_DATE". Each flag activates or disables a particular behavior.
The default SQL mode varies widly between versions of MySQL and MySQL. In general, more recent versions of MySQL and MariaDB have stricter settings than older versions, and MySQL has stricter settings than the more liberal MariaDB.
If your app explodes after changing SQL servers, you might be able to fix it by setting the SQL mode to the value in your previous server version.

Important SQL mode flags

These SQL modes will probably give you the most headaches:

STRICT_TRANS_TABLES, STRICT_ALL_TABLES

Introduced in MySQL 5.7, these enable Strict SQL mode. In SQL Strict mode MySQL is likely to throw an error if an INSERT or UPDATE has invalid or missing values. Without strict mode, MySQL will try more to not throw an error.
For instance, if a STRING exceeds the length of a field, MySQL will throw an error in strict mode. Without strict mode it would truncate the string to the maximum column size.

ONLY_FULL_GROUP_BY

With this enabled (default in MySQL 5.7), you can no longer SELECT values from a group without an aggregate function.
E.g. you will probably find queries like these in old apps:
COPY
SELECT name, address, MAX(age) FROM users GROUP BY name;
With ONLY_FULL_GROUP_BY this now explodes with:
COPY
ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'mydb.t.address' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
You need to make sure that any selected value is either (1) the value you group by or (2) passed to an aggregate function like MIN or MAX. If you don't care about the aggregate function, you can use ANY_VALUE:
COPY
SELECT name, ANY_VALUE(address), MAX(age) FROM t GROUP BY name;

TRADITIONAL

This is a shortcut that activates multiple SQL mode flags.
Which SQL mode flags it activates varies between MySQL, MariaDB and their individual versions (see below).
Note that TRADITIONAL does not necessarily make the SQL server behave more liberally in what it accepts. In particular, it enables strict mode, but disables ONLY_FULL_GROUP_BY in all versions we've seen.
Because of this you might want to list individual flags instead of using TRADITIONAL.

Default SQL mode settings

In general, more recent versions of MySQL and MariaDB have stricter settings than older versions, and MySQL has stricter settings than the more liberal MariaDB.
Maria DB 10 TraditionalMariaDB 10.0 defaultMariaDB 10.2 defaultMySQL 5.7 TraditionalMySQL 5.7 DefaultMySQL 5.6 DefaultMySQL 5.6 Traditional
STRICT_TRANS_TABLESyes-yesyesyes-yes
STRICT_ALL_TABLESyes--yesyes-yes
NO_ZERO_IN_DATEyes--yesyes-yes
NO_ZERO_DATEyes--yesyes-yes
ERROR_FOR_DIVISION_BY_ZEROyes-yesyesyes-yes
NO_AUTO_CREATE_USERyes-yesyesyes-yes
NO_ENGINE_SUBSTITUTION--yesyesyesyesyes
ONLY_FULL_GROUP_BY----yes--

Changing the SQL mode

Globally

To change the SQL for your entire database server, edit /etc/mysql/my.cnf and add this to the [mysqld] section:
COPY
[mysqld] sql_mode=""
Now restart MySQL:
COPY
sudo service mysql restart

No comments:

Post a Comment

The complete list of new features in MySQL 8.0

There are over 250 new features in MySQL 8.0. The MySQL Manual is very good, but verbose. This is a list of new features in short bullet f...