Mysql режимы работы

Очень часто в интернете встречаюсь со статьями, в которых приводят кучу примеров с якобы странным поведением MySQL по сравнению с другими БД. Чтобы стало понятно...
article placeholder

Время на прочтение
15 мин

Количество просмотров 128K

Очень часто в интернете встречаюсь со статьями, в которых приводят кучу примеров с якобы странным поведением MySQL по сравнению с другими БД. Чтобы стало понятно, о чём я говорю, приведу несколько примеров:
1. Деление на ноль возвращает NULL вместо ошибки
2. Выход за диапазон допустимых значений неявно приводит число к допустимому значению, а не к ошибке и откату транзакции
3. Вставка неверного типа данных также приводит к неявному преобразованию и успешному выполнению операции
Таких примеров я могу привести огромное число, но цель статьи не сделать очередное собрание высосанных из пальца примеров, а объяснить, почему происходит то или иное действие. Вся эта мистика MySQL давно описана в документации и легко объяснима, в чём вы сможете убедиться сами, прочитав статью до конца.
Для меня эта первая статья на хабре, поэтому я старался писать дотошно подробно. Уверен, что она будет полезна всем, кто работает с MySQL. Большую помощь в написании статьи оказала подготовка к сдаче на сертификат разработчика MySQL, а точнее книга «MySQL Certification Study Guide».
Итак, мой друг, начнём!

SQL Modes

SQL modes – это настройка поведения работы сервера MySQL, состоящая из режимов, каждый из которых контролирует какой-либо один аспект обработки запроса.

Возможности SQL mode:
1. Устанавливает строгую или мягкую проверку входных данных
2. Включает или отключает следование SQL стандарту
3. Обеспечивает лучшую синтаксическую совместимость с другими БД
По сути, SQL mode очень мощный механизм тюнинга БД, позволяющий гибко манипулировать обработкой запросов и уведомлениями MySQL.

Прежде чем мы перейдём к последующей теории, вы должны строго-настрого уяснить, что изменение режима SQL mode после создания и вставки данных в партиционные таблицы (partitioning tables) может привести к существенным изменениям в поведении таких таблиц, что, в свою очередь, может привести к потере или повреждению данных. Настоятельно рекомендуется, чтобы вы никогда не изменяли SQL режим после создания партиционных таблиц.
При репликации партиционных таблиц, отличающиеся параметры SQL mode на Primary и Slave MySQL серверах также может привести к проблемам. Для стабильной работы репликации между серверами, настройки SQL mode должны быть идентичными.

Теперь, после того, как вы осознали всю ответственность в использовании SQL режимов, перейдём к его сути.

Контроль текущего SQL режима происходит через системную переменную sql_mode. Для задания значения используется команда SET. Ниже представлены возможные варианты установки данного режима.

1. Соответствует значению по умолчанию для только что установленной БД (никаких специальных режимов не установлено). Кавычки являются обязательными.

SET sql_mode = '';

2. Установка одного режима sql_mode. Возможно два варианта – с кавычками и без них.

SET sql_mode = ANSI_QUOTES;
SET sql_mode = 'TRADITIONAL';

3. Установка нескольких режимов 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 существует ещё два способа задания режима работы БД:
1. Запустить сервер с опцией —sql-mode=»<режимы>»
2. Установить в файле 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. Этот режим возвращает ошибку, вместо предупреждения в следующих случаях:
1. Тип входных данных не соответствует заданному типу. Например, вставка строки в колонку c числовым типом
2. Число или дата находится вне допустимого диапазона. Диапазон определяется типом данных. Например, для типа unsigned tinyint допустимым диапазоном являются числа от 0 до 255
3. При вставке данных пропущено значение колонки, для которой не задано значение по умолчанию и имеет атрибут NOT NULL
4. Длина значения выходит за пределы заданного диапазона. Например, для колонки типа CHAR(5) вы не сможете вставить строку более 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_modeG
*************************** 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

Если вы желаете узнать обо всех SQL mode режимах и окунуться глубже в проблему, то милости прошу в официальную документацию http://dev.mysql.com/doc/refman/5.5/en/server-sql-mode.html

Работа с SQL mode в PHP

По правде сказать, данную главу вряд ли можно назвать прикладной, потому что в реальных проектах конфигурировать нужно непосредственно на сервере MySQL, а не средствами языка программирования, поэтому глава скорее теоретическая, но для общего развития неплохо иметь ввиду и такой способ.
Чаще всего соединение с БД происходит через экземпляр класса PDO, поэтому рассмотрим его в деталях.
Есть два способа передать в БД специальные инструкции. Первый способ – передача в конструкторе. Посмотрим на полное описание конструктора:

PDO::__construct() ( string $dsn [, string $username [, string $password [, array $driver_options ]]] )

Думаю, с первыми тремя параметрами всё и так понятно, а вот $driver_options как раз таки предоставляет нам возможность выполнять запросы непосредственно в БД. Для задания системной переменной sql_mode режимом TRADITIONAL, будем делать так:

$db = new PDO('mysql:host=myhost;dbname=mydb', 'login', 'password', array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET sql_mode = 'TRADITIONAL'"));

Второй способ – конфигурирование на лету, через метод setAttribute;

$db->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, "SET sql_mode = 'TRADITIONAL'");

Конечно, некоторые из вас могут возразить, что можно выполнять запросы методом query или exec, но поскольку изначально глава теоретическая, то не буду заострять внимание на таком способе.
Дополнительно о PDO можно прочитать в официальной документации php.net/manual/ru/book.pdo.php
Предопределённые PDO константы для работы с MySQL php.net/manual/ru/ref.pdo-mysql.php

Строгий режим

Мы уже немного познакомились со строгим режимом в главе SQL Mode, когда изучали режимы STRICT_TRANS_TABLES, STRICT_ALL_TABLES и композитный TRADITIONAL. Уже из самого названия легко догадаться, что все входные данные проверяются с особой тщательностью и в случае нарушений любых ограничений, вас неминуемо будет ждать ошибка.
Ошибка в транзакционных таблицах вызывает откат транзакции (rollback). Даже если ваши запросы не предварены командой start transaction, то неявно каждый запрос в отдельности по-любому будет обёрнут командами start transaction и commit. Так работают все традиционные БД, что в равной степени относится и к транзакционным таблицам MySQL. Из этого следует, что нарушив ограничение, вызывается rollback, который откатывает все изменения.
Для не транзакционных таблиц всё немного сложнее. Так, при вставке, обновлении или удалении нескольких строк, в случае ошибки отменяется только последнее действие, вместо полного отката. Проиллюстрирую это на примере.

mysql> SET sql_mode = 'STRICT_ALL_TABLES';
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE test20 (
    -> x TINYINT NOT NULL,
    -> y TINYINT NOT NULL
    -> ) ENGINE = MyISAM;
Query OK, 0 rows affected (0.08 sec)

mysql> INSERT INTO test20 VALUES (1, 1), (2, 2), (3, 3), (NULL, 4), (5, 5);
ERROR 1048 (23000): Column 'x' cannot be null

mysql> SELECT * FROM test20;
+---+---+
| x | y |
+---+---+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+---+---+
3 rows in set (0.01 sec)

mysql> UPDATE test20 SET x = x + 125, y = y + 1;
ERROR 1264 (22003): Out of range value for column 'x' at row 3

mysql> SELECT * FROM test20;
+-----+---+
| x   | y |
+-----+---+
| 126 | 2 |
| 127 | 3 |
|   3 | 3 |
+-----+---+
3 rows in set (0.00 sec)

Генерация ошибки происходит в следующих случаях:
1. Тип вставляемых данных отличается от заданного типа столбца
2. Опущено значение для столбца, которому не задано значение по умолчанию и имеет атрибут NOT NULL
3. Для чисел и дат – данные находятся вне диапазона допустимых значений
4. Для строк – превышение допустимой длины
5. Для типов ENUM и SET – значение не является допустимым для заданного перечисления
6. Для столбца, определённого как NOT NULL — вставка NULL

Дефолтные значения для типов данных

Если в insert запросе не указаны данные для одной из колонок, то MySQL будет обрабатывать эту ситуацию в следующем порядке:
1. Если столбец имеет значение по умолчанию, то используется это значение и на этом всё заканчивается, в противном случае происходит переход к следующему шагу
2. Если столбец не имеет параметр NOT NULL, то присваивается NULL и на этом всё заканчивается, в противном случае поведение зависит от переменной sql_mode, точнее от строгости самого режима.

Как можно было догадаться из предыдущей главы, строгий режим сразу вернёт ошибку, которая в свою очередь откатит транзакцию для транзакционных таблиц или отменит последнее действие для не транзакционных таблиц.

Для нестрого режима будет вставлено значение по умолчанию для заданного типа данных.
Значения по умолчанию для каждого типа данных:

  • Для типов int и float значение по умолчанию ноль (0)
  • Для всех строк, кроме типа enum – пустая строка (»)
  • Для enum – первый нумерованный член
  • Для даты и времени – нулевые значения ‘0000-00-00 00:00:00’
  • Для timestamp — текущая дата и время, если колонка была автоматически инициализирована, либо нулевыми значениями ‘0000-00-00 00:00:00’

Нестрогий режим

Ура! Наконец-то, мы добрались до самой «загадочной» части статьи, которую некоторые освещают как некую магию MySQL, но, увы, это лишь фокусы на потеху детей. И так, поехали!
Возможно, нужно было ранее описать все случаи, для которых применяются правила проверки данных, но я решил сделать это только сейчас. Их всего три, но каждый из них требует отдельного рассмотрения.
Запросы на модификация данных: INSERT, UPDATE, REPLACE, LOAD DATA INFILE
Обновление описания схем таблиц: ALTER TABLE
Задание значения по умолчанию (DEFAULT) в описании колонки

Напоминаю, что в строгом режиме некорректные данные приведут к генерации ошибки и откат данных, а в нестрогом – значение будет не явно приведено к корректному значению и сгенерировано предупреждение. Для просмотра ошибок используйте SHOW WARNINGS.
Ниже будут подробно рассмотрены все случаи обработки некорректных значений и их разрешений на уровне БД.

Выход из диапазона допустимых значений

Если число меньше минимального значения допустимого диапазона, то присваивается минимально-допустимое число. Если больше максимального – максимально-допустимое.

mysql> CREATE TABLE test31 (
    -> i TINYINT NOT NULL,
    -> j TINYINT NOT NULL,
    -> k TINYINT NOT NULL
    -> ) ENGINE = InnoDB;
Query OK, 0 rows affected (0.09 sec)

mysql> INSERT INTO test31 (i, j, k) VALUES (-500, 10, 500);
Query OK, 1 row affected, 2 warnings (0.06 sec)

mysql> SELECT * FROM test30;
+------+----+-----+
| i    | j  | k   |
+------+----+-----+
| -128 | 10 | 127 |
+------+----+-----+
1 row in set (0.00 sec)
Обработка строк

Строки длиннее заданной длины — усекаются.

mysql> CREATE TABLE test32 ( col1 VARCHAR(10) ) ENGINE = InnoDB;
Query OK, 0 rows affected (0.11 sec)

mysql> INSERT INTO test32 VALUES ('Im really the biggest string');
Query OK, 1 row affected, 1 warning (0.03 sec)

mysql> SHOW WARNINGS;
+---------+------+-------------------------------------------+
| Level   | Code | Message                                   |
+---------+------+-------------------------------------------+
| Warning | 1265 | Data truncated for column 'col1' at row 1 |
+---------+------+-------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT * FROM test32;
+------------+
| col1       |
+------------+
| Im really  |
+------------+
1 row in set (0.00 sec)
ENUM и SET типы данных

Если присваиваемое значение колонке с типом ENUM не перечислено в определении ENUM, то MySQL преобразует его в пустую строку.
Если значение, которое присваивается SET столбцу содержит элементы, которые не перечислены в определении SET, то MySQL отбрасывает эти элементы, сохраняя значения только легальным элементам.

mysql> CREATE TABLE test33 (
    -> col1 ENUM('One', 'Two', 'Three'),
    -> col2 SET('One', 'Two', 'Three')
    -> ) ENGINE = InnoDB;
Query OK, 0 rows affected (0.12 sec)

mysql> INSERT INTO test33 VALUES ('Five', 'One,Three,Five');
Query OK, 1 row affected, 2 warnings (0.04 sec)

mysql> SHOW WARNINGS;
+---------+------+-------------------------------------------+
| Level   | Code | Message                                   |
+---------+------+-------------------------------------------+
| Warning | 1265 | Data truncated for column 'col1' at row 1 |
| Warning | 1265 | Data truncated for column 'col2' at row 1 |
+---------+------+-------------------------------------------+
2 rows in set (0.00 sec)

mysql> SELECT * FROM test33;
+------+-----------+
| col1 | col2      |
+------+-----------+
|      | One,Three |
+------+-----------+
2 rows in set (0.00 sec)
Преобразование в тип даты

При попытке сохранения значения, которое не может быть преобразовано в тип данных столбца, MySQL неявно преобразует его в значение по умолчанию для данного типа.

mysql> CREATE TABLE test34 (
    -> col1 DATETIME,
    -> col2 DATETIME
    -> ) ENGINE = InnoDB;
Query OK, 0 rows affected (0.14 sec)

mysql> INSERT INTO test34 VALUES ('string', 123);
Query OK, 1 row affected, 1 warning (0.12 sec)

mysql> SHOW WARNINGS;
+---------+------+-----------------------------------------------+
| Level   | Code | Message                                       |
+---------+------+-----------------------------------------------+
| Warning | 1264 | Out of range value for column 'col1' at row 1 |
+---------+------+-----------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT * FROM test34;
+---------------------+---------------------+
| col1                | col2                |
+---------------------+---------------------+
| 0000-00-00 00:00:00 | 2000-01-23 00:00:00 |
+---------------------+---------------------+
1 row in set (0.00 sec)
Таблица преобразований

STRING DATE INT
‘2010-03-12’ ‘2010-03-12’ 2010
’03-12-2010′ ‘0000-00-00’ 3
‘0017’ ‘0000-00-00’ 17
‘500 hats’ ‘0000-00-00’ 500
‘bartholomew’ ‘0000-00-00’ 0
Присвоение NULL для колонки с NOT NULL

Результат зависит от того, будет вставлена одна строка или множество в INSERT запросе.
При вставке одной строки возникает ошибка и изменения не применяются. При множественной вставке — MySQL неявно преобразует значение по умолчанию для этого типа данных.

Обновление описания схем таблиц: ALTER TABLE

При изменении типа данных на колонку налагаются ограничения нового типа, что может привести к неожиданному изменению самих данных по правилам описанных выше.

Если на колонку налагается ограничение NOT NULL, то все NULL значения конвертируются в значения по умолчанию для заданного типа данных текущей колонки. Значения по умолчанию описаны в главе «Дефолтные значения для типов данных»

mysql> CREATE TABLE test35 (
    -> i INT NULL,
    -> i2 INT NOT NULL,
    -> j VARCHAR(20) NOT NULL,
    -> k DATETIME
    -> ) ENGINE = InnoDB;
Query OK, 0 rows affected (0.15 sec)

mysql> INSERT INTO test35 VALUES (NULL, 9999, 'very cool string', '1910-01-01 12:10:00');
Query OK, 1 row affected (0.05 sec)

mysql> SELECT * FROM test35;
+------+------+------------------+---------------------+
| i    | i2   | j                | k                   |
+------+------+------------------+---------------------+
| NULL | 9999 | very cool string | 1910-01-01 12:10:00 |
+------+------+------------------+---------------------+
1 row in set (0.00 sec)

mysql> ALTER TABLE test35
    -> CHANGE COLUMN `i` `i` INT(11) NOT NULL,
    -> CHANGE COLUMN `i2` `i2` TINYINT(1) NOT NULL,
    -> CHANGE COLUMN `j` `j` VARCHAR(2) NOT NULL,
    -> CHANGE `k` `k` TIMESTAMP NULL DEFAULT NULL;
Query OK, 1 row affected, 4 warnings (0.42 sec)
Records: 1  Duplicates: 0  Warnings: 4

mysql> SHOW WARNINGS;
+---------+------+---------------------------------------------+
| Level   | Code | Message                                     |
+---------+------+---------------------------------------------+
| Warning | 1265 | Data truncated for column 'i' at row 1      |
| Warning | 1264 | Out of range value for column 'i2' at row 1 |
| Warning | 1265 | Data truncated for column 'j' at row 1      |
| Warning | 1264 | Out of range value for column 'k' at row 1  |
+---------+------+---------------------------------------------+
4 rows in set (0.00 sec)

mysql> SELECT * FROM test35;
+---+-----+----+---------------------+
| i | i2  | j  | k                   |
+---+-----+----+---------------------+
| 0 | 127 | ve | 0000-00-00 00:00:00 |
+---+-----+----+---------------------+
1 row in set (0.00 sec)
Задание значения по умолчанию (DEFAULT) в описании колонки

Вообще, всё уже было сказано в прошлой главе, поэтому здесь нечего добавить.

Ну что ж, мой дорогой читатель. Теперь ты можешь по-праву назваться настоящим джедаем и получить чёрный пояс)))

Извлекаем плюсы

IGNORE

Ключевое слово IGNORE заставляет MySQL включать для такого запроса нестрогий режим. Также его можно использовать для генерации предупреждения вместо ошибки, при нарушении целостности первичного ключа (PRIMARY KEY) или уникальности (UNIQUE).

mysql> CREATE TABLE test40 (
    -> x INT NOT NULL PRIMARY KEY
    -> ) ENGINE = InnoDB;
Query OK, 0 rows affected (0.14 sec)

mysql> INSERT IGNORE INTO test40 VALUES (1), (2), (2), (3), (4);
Query OK, 4 rows affected (0.02 sec)
Records: 5  Duplicates: 1  Warnings: 0

mysql> SELECT * FROM test40;
+---+
| x |
+---+
| 1 |
| 2 |
| 3 |
| 4 |
+---+
4 rows in set (0.02 sec)

mysql> INSERT INTO test40 VALUES (1), (2), (2), (3), (4);
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'

mysql> UPDATE IGNORE test40 SET x = 3 WHERE x = 2;
Query OK, 0 rows affected (0.07 sec)
Rows matched: 1  Changed: 0  Warnings: 0

mysql> UPDATE test40 SET x = 3 WHERE x = 2;
ERROR 1062 (23000): Duplicate entry '3' for key 'PRIMARY'
ON DUPLICATE KEY UPDATE

Такой вид запроса не совсем идеальный пример, но включил, чтобы лишний раз вспомнить, что такое вообще существует. Используется для выполнения вставки данных, либо обновлении при нарушении ограничения целостности первичного ключа (PRIMARY KEY) или уникальности (UNIQUE).

mysql> INSERT INTO test40 VALUES (10) ON DUPLICATE KEY UPDATE x = x + 10;
Query OK, 1 row affected (0.25 sec)

mysql> SELECT * FROM test40;
+----+
| x  |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
| 10 |
+----+
5 rows in set (0.06 sec)

mysql> INSERT INTO test40 VALUES (10) ON DUPLICATE KEY UPDATE x = x + 10;
Query OK, 2 rows affected (0.07 sec)

mysql> SELECT * FROM test40;
+----+
| x  |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
| 20 |
+----+
5 rows in set (0.00 sec)

Главная / Web / MySQL / SQL Mode в MySQL

SQL Mode определяет синтаксис комманд MySQL и то, какие проверки данных необходимо проводить. Эта возможность заметно облегчает использование баз данных MySQL в различных средах и совместимость MySQL c другими базами данных.

MySQL сервер может работать в различных режимах SQL. Каждому клиенту можно применить свой режим. Это делает возможным любому приложению приспособить режим работы MySQL сервера для решения своих задач.

Настроить SQL Mode для всей БД можно при запуске MySQL, запустив mysqld c опцией —sql-mode=»моды через запятую». Также, эти настройки можно внести в главный конфигурационный файл MySQL my.ini (Windows) или my.cnf (Unix) при помощи строки sql-mode=»моды через запятую». Значение по умолчанию SQL Mode — отсутствие любых настроек. Чтобы запустить MySQL без модов нужно указать пустой набор модов, например записав в my.ini sql-mode=»».

SQL Mode можно настраивать и после запуска MySQL, отправив запрос серверу: SET [GLOBAL|SESSION] sql_mode=‘моды через запятую’. Для изменения глобальных sql-модов (GLOBAL), распространяющихся на всех пользователей с момента запроса, необходимо обладать привелегией SUPER. Любой другой пользователь может использовать запрос SET SESSION sql_mode=‘моды через запятую’, для изменения модов, чтобы применить свои настройки в любое время.

attention safety

SQL mode и таблицы вида partitioning. Изменение режима сервера SQL после создания и вставки данных в таблицах «partitioning» может привести к существенным изменениям в поведении таких таблиц, что в свою очередь может привести к потере или повреждению данных. Настоятельно рекомендуется, чтобы вы никогда не изменяли SQL режим после создания таблиц вида «partitioning».

При репликации таблиц «partitioning», отличающиеся парметры SQL mod на Primary и Slave MySQL сервере также может привести к проблемам. Для стабильной работы репликации между главным и подчиненным сервером, настройки SQL mod на них должны быть установлены идентично.

Просмотреть настройки SQL mod можно такими запросами:

Просмотр настроек SQL-mod

SELECT @@GLOBAL.sql_mode;
SELECT @@SESSION.sql_mode;

STRICT_TRANS_TABLES и STRICT_ALL_TABLES

Режим STRICT_TRANS_TABLES контролирует вхождение всех недопустимых или отсутствующих значений, и в случае нахождения таких данных, MySQL вернет сообщение об ошибке. Например, вносимые данные могут иметь неправильный тип для столбца, в который они записываются, или их значения находятся за пределами диапазона, определенными для столбца. Также, если колонке не присвоено значение по умолчанию (нет DEFAULT в определении столбца), и в записываемой строке не определены данные для такого столбца — будет отображено сообщение об ошибке. Столбцам, для которых значения NULL допустимы, NULL вставляется, даже если значение отсутствует.

При вставке несоответствующих или отсутствующих данных в таблицы поддерживающие транзакции, будет сгенерировано сообщение об ошибке при включенном любом из режимов: STRICT_ALL_TABLES или STRICT_TRANS_TABLES. При этом такие запросы прерываются и ранее внесенные изменения отменяются.

В отношении нетранзакционных таблиц, например MyISAM, MySQL будет вести себя так же, как для поддерживающих транзакции, если не верное значение найдено в первом ряду при Insert или Update. Запрос прекращается и таблица остается неизменной. Если запрос вставляет или обновляет несколько строк и ошибка в значениях обнаруживается во второй или более строке, результат зависит от того, какой из режимов включен — STRICT_TRANS_TABLES или STRICT_ALL_TABLES:

  • Если включен STRICT_ALL_TABLES, MySQL вернет ошибку, игнорируя вставку или обновление последующих строк. Тем не менее, в данном случае, ранее вставленные или обновленные строки такими и останутся. Таким образом можно обновить только часть данных, что может быть не приемлимо. Чтобы этого избежать, лучше всего проводить обновления и вставки построчно, так как такие запросы могут быть прерваны без внесения изменений в таблицы.
  • Используя режим STRICT_TRANS_TABLES, MySQL будет преобразовывать недопустимые значения до ближайших приемлимых для конкретного столбца и записывать их в таблицу. Если значение отсутствует, MySQL запрос будет использовать значения по умолчанию для столбца, например, для определенного как INT, это будет 0, даже если не указано DEFAULT 0. В любом случае, MySQL сгенерирует предупреждение, а не ошибку и продолжит обработку запроса.

STRICT_ALL_TABLES и STRICT_TRANS_TABLES запрещают внесение в БД недопустимых значений дат, таких как ‘2004-04-31’. Но даты с нулевым месяцем или днем, например ‘2011-04-00’, эти режимы не отфильтровуют. Чтобы запретить внесение дат такого вида, следует использовать NO_ZERO_IN_DATE и NO_ZERO_DATE SQL mod в дополнение к STRICT_ режимам.

Если ни один из STRICT_ режимов MySQL не используется, происходит запись скорректированных или отсутствующих значений. В STRICT_TRANS_TABLES или STRICT_ALL_TABLES режиме можно добиться тех же результатов, с помощью INSERT IGNORE или UPDATE IGNORE, детальные сведения по этим запросам можно посмотреть в оригинальной документации (eng): «SHOW WARNINGS Syntax».

Строгий режим не влияет на ограничения проверки внешнего ключа. Foreign_key_checks может быть для этого использован. (См. раздел 5.1.3 оригинальной документации MySQL (eng): «Server System Variables».)

Режим STRICT_TRANS_TABLES является рекомендуемым для использования, так как не позволяет производить незаметную корректировку данных сервером MySQL в запросах к добавлению или изменению одной строки, и в случае отправки запроса затрагивающего более одного ряда данных не нарушает целостности информации в БД. Также, стоит прочесть статьи: «К чему может привести усечение данных в SQL» и «Строгий режим MySQL и почему он должен быть включен».

TRADITIONAL

Мод TRADITIONAL вынуждает MySQL вести себя как «традиционные» системы баз данных SQL. Вместо предупреждения, при установке некорректного значения в столбце выдается сообщение об ошибке.

Запросы Inseret и Update будут прерваны, как только обнаружится любая ошибка. При использовании таблиц, без поддержки транзакций, возможно нарушение целосности данных, так как «откат» изменений при возникновении ошибки в таких видах таблиц не предусмотрен.

Режим TRADITIONAL — это набор таких режимов, как: STRICT_TRANS_TABLES, STRICT_ALL_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_AUTO_CREATE_USER и NO_ENGINE_SUBSTITUTION.

С полным списком SQL-режимов для MySQL 5.5 можно ознакомится в официальной документации по MySQL, на странице: «Официальное руководство MySQL 5.5 по SQL mod (eng)».

Опубликовано: 2011/08/31

HTML-код ссылки на эту страницу:

<a href=»https://petrenco.com/mysql.php?txt=89″ target=»_blank»>SQL Mode в MySQL</a>

96911

MySQL sql_mode option defines supported SQL syntax, and data validation performed by MySQL.

Syntax SET [GLOBAL|SESSION] sql_mode=’mode1,mode2, …
Quick Example SET sql_mode = ‘ANSI_QUOTES,PIPES_AS_CONCAT’;
Strict Mode When STRICT_TRANS_TABLES or STRICT_ALL_TABLES is specified
MySQL Configuration You can set sql_mode in my.cnf (Unix), my.ini (Windows), or —sql-mode (command line)

Get and Set sql_mode

Retrieving the current value of sql_mode:

   -- Get the current value that could be already modified by SET sql_mode statement
   -- executed in the current session
   SELECT @@sql_mode;
 
   -- Get the global value, not affected by SET sql_mode
   SELECT @@GLOBAL.sql_mode;

Change the current sql_mode:

   SET sql_mode='STRICT_TRANS_TABLES,ANSI_QUOTES';

Resources

I am trying to set sql_mode in MySQL but it throws an error.

Command:

set global sql_mode='NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLE','NO_AUTO_CREATE_USER','NO_ENGINE_SUBSTITUTION'
  • Is this not the proper way to set multiple modes?
  • What are the advantages of setting session and global modes?
  • Which is preferred?

I have different users trying to update the database with different UNC values and instead of setting the session mode to NO_BACKSLASH_ESCAPES, I though it would make sense to set a global mode for this. Does this make sense?

Please let me know.

Thanks.

informatik01's user avatar

informatik01

15.8k10 gold badges74 silver badges103 bronze badges

asked Feb 23, 2010 at 11:27

JPro's user avatar

1

BTW, if you set globals in MySQL:

SET GLOBAL sql_mode = 'NO_ENGINE_SUBSTITUTION';
SET SESSION sql_mode = 'NO_ENGINE_SUBSTITUTION';

This will not set it PERMANENTLY, and it will revert after every restart.

So you should set this in your config file (e.g. /etc/mysql/my.cnf in the [mysqld] section), so that the changes remain in effect after MySQL restart:

Config File: /etc/mysql/my.cnf

[mysqld] 
sql_mode = NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

UPDATE: Newer versions of Mysql (e.g. 5.7.8 or above) may require slightly different syntax:

[mysqld]
sql-mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION"

Make sure that there is a dash between sql-mode not an underscore, and that modes are in double quotes.

Always reference the MySQL Docs for your version to see the sql-mode options.

answered Sep 29, 2014 at 16:00

Chadwick Meyer's user avatar

Chadwick MeyerChadwick Meyer

6,8916 gold badges44 silver badges64 bronze badges

4

I resolved it.

the correct mode is :

set global sql_mode="NO_BACKSLASH_ESCAPES,STRICT_TRANS_TABLE,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"

answered Feb 23, 2010 at 12:33

JPro's user avatar

JProJPro

6,08212 gold badges57 silver badges82 bronze badges

3

Setting sql mode permanently using mysql config file.

In my case i have to change file /etc/mysql/mysql.conf.d/mysqld.cnf as mysql.conf.d is included in /etc/mysql/my.cnf. i change this under [mysqld]

[mysqld]
sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"

just removed ONLY_FULL_GROUP_BY sql mode cause it was causing issue.

I am using ubuntu 16.04, php 7 and mysql —version give me this mysql Ver 14.14 Distrib 5.7.13, for Linux (x86_64) using EditLine wrapper

After this change run below commands

sudo service mysql stop
sudo service mysql start

Now check sql modes by this query SELECT @@sql_mode and you should get modes that you have just set.

answered Aug 27, 2016 at 23:44

Yashrajsinh Jadeja's user avatar

For someone who googling this error for MySQL 8.

MySQL 8.0.11 remove the ‘NO_AUTO_CREATE_USER’ from sql-mode.

MySQL 5.7: Using GRANT to create users. Instead, use CREATE USER.
Following this practice makes the NO_AUTO_CREATE_USER SQL mode
immaterial for GRANT statements, so it too is deprecated. MySQL
8.0.11: Using GRANT to create users. Instead, use CREATE USER. Following this practice makes the NO_AUTO_CREATE_USER SQL mode
immaterial for GRANT statements, so it too is removed.

Taken from here

So, your sql_mode can be like this:

sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION

Or if you’re using Docker you can add next command to docker-compose.yml

  mysql:
    image: mysql:8.0.13
    command: --sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
    ports:
      - 13306:${MYSQL_PORT}

answered Jan 23, 2019 at 13:45

Serhii Popov's user avatar

Serhii PopovSerhii Popov

3,0322 gold badges23 silver badges35 bronze badges

1

Copy to Config File: /etc/mysql/my.cnf OR /bin/mysql/my.ini

[mysqld]
port = 3306
sql-mode=""

MySQL restart.

Or you can also do

[mysqld]
port = 3306
SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";

MySQL restart.

answered Jul 25, 2016 at 10:27

Devraj Gupta's user avatar

1

Access the database as the administrator user (root maybe).

Check current SQL_mode

mysql> SELECT @@sql_mode;

To set a new sql_mode, exit the database, create a file

nano /etc/mysql/conf.d/<filename>.cnf 

with your sql_mode content

[mysqld]
sql_mode=NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

Restart Mysql

mysql> sudo service mysql stop
mysql> sudo service mysql start

We create a file in the folder /etc/mysql/conf.d/
because in the main config file /etc/mysql/my.cnf
the command is written to include all the settings files from the folder /etc/mysql/conf.d/

answered Apr 22, 2020 at 9:51

Eric Korolev's user avatar

For Temporary change use following command

set global sql_mode="NO_BACKSLASH_ESCAPES,STRICT_TRANS_TABLE,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" 

For permanent change : go to config file /etc/my.cnf or /etc/mysql/mysql.conf.d/mysqld.cnf and add following lines then restart mysql service

[mysqld]
sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"

answered Apr 8, 2020 at 9:10

Vineet Kumar's user avatar

Check the documentation of sql_mode

Method 1:

Check default value of sql_mode:

SELECT @@sql_mode //check current value for sql_mode

SET GLOBAL sql_mode = "NO_BACKSLASH_ESCAPES,STRICT_TRANS_TABLE,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION";

Method 2:

Access phpmyadmin for editing your sql_mode

  • Login on phpmyadmin and open localhost
  • Top on Variables present on the top in menu items and search out for sql mode
  • Click on edit button to modify sql_mode based on your requirements
  • Save the changes

sql mode settings in phpmyadmin

Restart server after executing above things

answered Aug 11, 2020 at 5:24

Ankit Jindal's user avatar

Ankit JindalAnkit Jindal

3,3913 gold badges23 silver badges35 bronze badges

In my case mysql and ubuntu 18.04

I set it permanently using this command

sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

Append the line after the configuration. See example highlighted in the image below.

sql_mode = ""

Note :You can also add different modes here, it depends on your need
NO_BACKSLASH_ESCAPES,STRICT_TRANS_TABLE,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

See Available sql modes reference and Documentation

adding sql mode

Then save.
After saving you need to restart your mysql service, follow the command below:

sudo service mysql restart

Hope this helps :-)

answered Aug 12, 2020 at 7:12

Cristiana Chavez's user avatar

Cristiana ChavezCristiana Chavez

11.3k5 gold badges54 silver badges54 bronze badges

In my case i have to change file /etc/mysql/mysql.conf.d/mysqld.cnf change this under [mysqld]

Paste this line on [mysqld] portion

sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"

alexander.polomodov's user avatar

answered Oct 9, 2017 at 21:11

shashikant parmar's user avatar

I just had a similar problem where MySQL (5.6.45) wouldn’t accept sql_mode from any config file.

The solution was to add init_file = /etc/mysql/mysql-init.sql to the config file and then execute SET GLOBAL sql_mode = ''; in there.

answered May 21, 2020 at 18:52

Cobra_Fast's user avatar

Cobra_FastCobra_Fast

15.5k8 gold badges58 silver badges102 bronze badges

If someone want to set it only for the current session then use the following command

set session sql_mode="NO_BACKSLASH_ESCAPES,STRICT_TRANS_TABLE,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

answered Jul 7, 2019 at 8:50

ASHOK MANGHAT's user avatar

Updating this for users Using MAMP PRO {works with MAMP users too}. Because I seem to have got stuck on finding a solution for this, but people recommended I should edit the my.cnf file in the /Applications/MAMP/tmp/mysql/my.cnf folder which does not work because it gets reset after every restart of mysql server.

Referring this document:

The configuration file “my.cnf” of MySQL can be found here:
“/Applications/MAMP/tmp/mysql/my.cnf”. Please note: Editing this file
does NOT work as it will be overwritten every time MySQL is restarted
by MAMP PRO with a “my.cnf” file that is created from the MySQL
template. You must edit this template (menu File > Open Template >
MySQL (my.cnf) > 5.7.30) to modify the MySQL configuration. Manually
adding “my.cnf” files to other locations is not recommended. Every
configuration aspect can be handled with the MySQL template.

Once this is done, add the following in the my.cnf file:

[mysqld]
sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

Restart the Mysql Server. That should do the trick.

answered Sep 14, 2021 at 6:59

cyberrspiritt's user avatar

set global sql_mode="NO_BACKSLASH_ESCAPES,STRICT_TRANS_TABLE,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"

answered Mar 28, 2018 at 4:31

Shadab Khan Zed's user avatar

Понравилась статья? Поделить с друзьями:
  • Как сделать успешный бизнес на ритуальных услугах
  • Выездной кейтеринг в России
  • Mybox суши брянск режим работы
  • Mybox режим работы 31 декабря
  • Mybox лобня режим работы