======SQL modes======
====описание====
SQL modes – это настройка поведения работы сервера MySQL, состоящая из режимов, каждый из которых контролирует какой-либо один аспект обработки запроса.
Возможности SQL mode:
- Устанавливает строгую или мягкую проверку входных данных
- Включает или отключает следование SQL стандарту
- Обеспечивает лучшую синтаксическую совместимость с другими БД
По сути, SQL mode очень мощный механизм тюнинга БД, позволяющий гибко манипулировать обработкой запросов и уведомлениями MySQL.
Настоятельно рекомендуется, чтобы вы никогда не изменяли SQL режим после создания партиционных таблиц.
При репликации партиционных таблиц, отличающиеся параметры SQL mode на Primary и Slave MySQL серверах также может привести к проблемам. Для стабильной работы репликации между серверами, настройки SQL mode должны быть идентичными.
Контроль текущего SQL режима происходит через системную переменную sql_mode. Для задания значения используется команда SET. Ниже представлены возможные варианты установки данного режима.
- Соответствует значению по умолчанию для только что установленной БД (никаких специальных режимов не установлено). Кавычки являются обязательными.
SET sql_mode = '';
- Элемент нумерованного спискаУстановка одного режима sql_mode. Возможно два варианта – с кавычками и без них.
SET sql_mode = ANSI_QUOTES;
SET sql_mode = 'TRADITIONAL';
- Элемент нумерованного спискаУстановка нескольких режимов sql_mode. Указание кавычек является обязательным!
SET sql_mode = 'IGNORE_SPACE,ANSI_QUOTES';
SET sql_mode = 'STRICT_ALL_TABLES,ERROR_FOR_DIVIZION_BY_ZERO'
Несмотря на то, что названия режимов регистронезависимые, я для удобства прочтения буду везде в статье писать их в верхнем регистре.
В примерах выше мы устанавливали режимы для текущей сессии, но если вы обладаете привилегиями суперпользователя, то можно задать глобальный режим для всего сервера и всех текущих коннектов, указав параметр GLOBAL. Полный синтаксис установки sql_mode выглядит так:
SET [GLOBAL|SESSION] sql_mode='параметры';
Для просмотра текущих значений глобального и сессионного режима сервера используйте следующие запросы:
SELECT @@global.sql_mode;
SELECT @@session.sql_mode;
Помимо команды SET существует ещё два способа задания режима работы БД:
- Запустить сервер с опцией --sql-mode="<режимы>"
- Установить в файле my.cnf (для unix подобных систем) или my.ini (для windows) параметр sql-mode="<режимы>"
====Краткий справочник режимов====
===ANSI_QUOTES===
Заставляет сервер интерпретировать двойную кавычку ( " ) точно также, как и обратную кавычку ( ` ), при этом она теряет способность обрамлять строки. Как можно было догадаться, этот режим заставляет MySQL приблизиться к SQL стандарту.
mysql> CREATE TABLE test11 (`order` INT NULL) ENGINE = InnoDB;
Query OK, 0 rows affected (0.28 sec)
mysql> CREATE TABLE test12 ("order" INT NULL) ENGINE = InnoDB;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"order" INT NOT NULL) ENGINE = InnoDB' at line 1
mysql> SET sql_mode = 'ANSI_QUOTES';
Query OK, 0 rows affected (0.00 sec)
mysql> CREATE TABLE test12 ("order" INT NULL) ENGINE = InnoDB;
Query OK, 0 rows affected (0.08 sec)
----
===IGNORE_SPACE===
По умолчанию, между функцией и открывающейся круглой скобкой нельзя устанавливать пробелы. Включение этого режима разрешает серверу игнорировать пробелы, но платой за такую вольность станет то, что все функции станут зарезервированными словами, а значит, при совпадении имени столбца с именем функции придётся в обязательном порядке экранировать такой столбец.
mysql> SELECT COUNT (*) FROM test12;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '*) FROM test12' at line 1
mysql> SET sql_mode = 'IGNORE_SPACE';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT COUNT (*) FROM test12;
+-----------+
| COUNT (*) |
+-----------+
| 0 |
+-----------+
1 row in set (0.01 sec)
----
===ERROR_FOR_DIVISION_BY_ZERO===
При делении на ноль в строгом режиме генерируется ошибка, а нестрогом — предупреждение при выполнении операторов INSERT или UPDATE. Без этого параметра деление на ноль возвращает предупреждение и вставляет в таблицу NULL. Про строгость будет сказано в следующем режиме, пока постарайтесь абстрагироваться.
mysql> SELECT 1 / 0;
+-------+
| 1 / 0 |
+-------+
| NULL |
+-------+
1 row in set (0.00 sec)
mysql> SHOW WARNINGS;
Empty set (0.00 sec)
mysql> INSERT INTO test12 VALUES (1/0);
Query OK, 1 row affected (0.02 sec)
mysql> SHOW WARNINGS;
Empty set (0.00 sec)
mysql> SET sql_mode = 'ERROR_FOR_DIVISION_BY_ZERO';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT 1 / 0;
+-------+
| 1 / 0 |
+-------+
| NULL |
+-------+
1 row in set, 1 warning (0.00 sec)
mysql> SHOW WARNINGS;
+---------+------+---------------+
| Level | Code | Message |
+---------+------+---------------+
| Warning | 1365 | Division by 0 |
+---------+------+---------------+
1 row in set (0.00 sec)
mysql> INSERT INTO test12 VALUES (1/0);
Query OK, 1 row affected, 1 warning (0.02 sec)
mysql> SHOW WARNINGS;
+---------+------+---------------+
| Level | Code | Message |
+---------+------+---------------+
| Warning | 1365 | Division by 0 |
+---------+------+---------------+
1 row in set (0.00 sec)
mysql> INSERT INTO test12 VALUES ('some string'/0);
Query OK, 1 row affected, 2 warnings (0.02 sec)
mysql> SHOW WARNINGS;
+---------+------+-------------------------------------------------+
| Level | Code | Message |
+---------+------+-------------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'some string' |
| Warning | 1365 | Division by 0 |
+---------+------+-------------------------------------------------+
2 rows in set (0.00 sec)
В приведённых примерах мы получали исключительно предупреждения, потому что строгий режим был выключен. Понимание строгости очень важное понятие для БД MySQL, потому что в классических базах данных такого нет. Забегая вперёд, скажу, что все БД изначально строгие и не позволяют тех вольностей, которые есть в MySQL. Мягкость MySQL сложилась исторически, когда ещё не было InnoDB. Посудите сами, в нетранзакционных таблицах действуют совершенно другие правила, нежели чем в транзакционных, поэтому следование жестким правилам зачастую приводило бы к нежелательному результату.
----
===STRICT_TRANS_TABLES===
Включает «строгий режим» для всех таблиц, поддерживающих транзакции, т.е. на InnoDB и BDB. Этот режим возвращает ошибку, вместо предупреждения в следующих случаях:
- Тип входных данных не соответствует заданному типу. Например, вставка строки в колонку c числовым типом
- Число или дата находится вне допустимого диапазона. Диапазон определяется типом данных. Например, для типа unsigned tinyint допустимым диапазоном являются числа от 0 до 255
- При вставке данных пропущено значение колонки, для которой не задано значение по умолчанию и имеет атрибут NOT NULL
- Длина значения выходит за пределы заданного диапазона. Например, для колонки типа CHAR(5) вы не сможете вставить строку более 5 символов
- Для типов ENUM и SET отсутствует вставляемое или обновляемое значение
Более подробно об особенностях работы данного режима будет рассказано отдельно в последующей ниже главе.
----
===STRICT_ALL_TABLES===
**STRICT_ALL_TABLES** полностью идентично STRICT_TRANS_TABLES, но действие режима уже распространяется на все таблицы MySQL, а не только на транзакционные.
Из-за разницы подходов к работе транзакционных и не транзакционных таблиц не всегда есть смысл использовать данный режим. Если это вам ещё не очевидно, то в главах о строгом и нестрогом режимах вы поймёте разницу.
----
===TRADITIONAL===
Композитный режим, включает в себя целый набор режимов, в который входит «строгий режим», а также ряд других режимов, налагающих ограничения на входные данные.
Заставляет MySQL вести себя как большинство «традиционных» баз данных SQL.
Посмотрим на полный перечень режимов, который содержит в себе данный режим.
mysql> SET sql_mode = 'TRADITIONAL';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @@sql_mode\G
*************************** 1. row ***************************
@@sql_mode: STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,
TRADITIONAL,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
1 row in set (0.00 sec)
----
===ANSI===
Другой композитный режим, делающий MySQL «ANSI-подобным», т.е. приближенным к стандарту SQL.
Включает в себя следующие режимы: **REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE.**
Последние два режима были обсуждены ранее, поэтому кратко опишу первые два:
**REAL_AS_FLOAT** – тип данных real является синонимом float, а не double.
**PIPES_AS_CONCAT** – разрешает использовать для конкатенации строк ( || ), вместо логического ИЛИ.
----
===ONLY_FULL_GROUP_BY===
Генерирует ошибку в запросах, в которых GROUP BY имеет не полный список не агрегированных параметров из SELECT и HAVING.
mysql> SELECT name, address, MAX(age) FROM test GROUP BY name;
ERROR 1055 (42000): 't.address' isn't in GROUP BY
mysql> SELECT name, MAX(age) as max_age FROM test GROUP BY name HAVING max_age < 30;
Empty set (0.00 sec)
ERROR 1463 (42000): Non-grouping field 'max_age' is used in HAVING clause
Обойти ошибку можно с помощью костылей
пример запроса вызывающего ошибку
SELECT
ss_catalog_vendors._id,
ss_catalog_vendors._disabled,
ss_catalog_vendors.pop_vendors,
ss_catalog_vendors.filename,
ss_catalog_vendors.`name`
FROM ss_catalog_vendors
INNER JOIN ss_catalog_goods ON ss_catalog_goods.vendor = ss_catalog_vendors._id AND ss_catalog_goods._disabled = 0
WHERE ss_catalog_vendors._disabled=0 AND ss_catalog_vendors.filename is not null
group by ss_catalog_vendors.`name
Ошибка
Ошибка SQL (1055): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'muser_mv_new.ss_catalog_vendors._id'
which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
решение проблемы с помощью костылей
SELECT
ANY_VALUE(ss_catalog_vendors._id) _id,
ANY_VALUE(ss_catalog_vendors._disabled) _disabled,
ANY_VALUE(ss_catalog_vendors.pop_vendors) pop_vendors,
ANY_VALUE(ss_catalog_vendors.filename) `filename`,
ss_catalog_vendors.`name`
FROM ss_catalog_vendors
INNER JOIN ss_catalog_goods ON ss_catalog_goods.vendor = ss_catalog_vendors._id AND ss_catalog_goods._disabled = 0
WHERE ss_catalog_vendors._disabled=0
group by ss_catalog_vendors.`name`
Если вы желаете узнать обо всех SQL mode режимах и окунуться глубже в проблему, то милости прошу в официальную документацию http://dev.mysql.com/doc/refman/5.5/en/server-sql-mode.html