Использовать "SET NAMES"

При чтении "Высокопроизводительной MySQL" от O'Reilly я наткнулся на следующие

Другим распространенным запросом мусора является SET NAMES UTF8, что является неправильным способом делать все равно (он не меняется набор символов клиентской библиотеки; Это влияет только на сервер).

Я немного смущен, потому что я использовал "SET NAMES utf8" в верхней части каждого script, чтобы db знал, что мои запросы закодированы в utf8.

Может кто-нибудь прокомментировать приведенную выше цитату или, если честно сказать, какие ваши предложения/рекомендации, чтобы мой рабочий процесс базы данных был unicode-осведомленным.

Мои целевые языки - это php и python, если это актуально.

Ответ 1

mysql_set_charset() будет вариант, но опция, ограниченная ext/mysql. Для ext/mysqli это mysqli_set_charset и для PDO ::mysql вам нужно указать параметр соединения.

Поскольку использование этой функции приводит к вызову API MySQL, ее следует рассматривать намного быстрее, чем выдача запроса.

В отношении производительности самый быстрый способ обеспечить совместимость с UTF-8 между вашим сервером script и сервером MySQL - правильно настроить сервер MySQL. Поскольку SET NAMES x эквивалент для

SET character_set_client = x;
SET character_set_results = x;
SET character_set_connection = x;

тогда как SET character_set_connection = x внутри также выполняет SET collation_connection = <<default_collation_of_character_set_x>>, вы также можете установить эти переменные сервера статически в my.ini/cnf.

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

Ответ 2

TL;DR

// The key is the "charset=utf8" part.
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8';
$dbh = new PDO($dsn, 'user', 'pass');

Этот ответ имеет особое значение для библиотеки php pdo, потому что он настолько вездесущ.

Краткое напоминание - mysql - это архитектура клиент-сервер. Это важно, потому что не только сервер mysql, где находится фактическая база данных, но и отдельный драйвер клиента mysql, что и говорит с сервером mysql (это отдельные объекты). Вы могли бы сказать, что клиент mysql и pdo смешиваются.

Когда вы используете set names utf8, вы set names utf8 стандартный SQL-запрос в mysql. Хотя sql-запрос проходит через pdo, а затем через клиентскую библиотеку mysql, а затем, наконец, достигает сервера mysql, ТОЛЬКО сервер mysql анализирует и интерпретирует этот SQL-запрос. Это важно, потому что сервер mysql не отправляет какое-либо сообщение обратно в pdo или клиент mysql, позволяя ему знать, что набор символов и кодировка изменились, и поэтому клиент mysql и pdo оба совершенно не осведомлены о том, что это произошло.

Важно не делать этого, потому что клиентская библиотека не может правильно обрабатывать строки, если она не знает текущий набор символов. Большинство обычных операций будут работать правильно, если клиент не знает правильный набор символов, но тот, который не будет сбрасывать строки, например PDO :: quote. Вы можете подумать, что вам не нужно беспокоиться о таком ручном примитивном стирании строки, потому что вы используете подготовленные операторы, но правда в подавляющем большинстве пользователей pdo: mysql неосознанно используют эмулированные подготовленные операторы, потому что это была настройка по умолчанию для pdo: mysql драйвера в течение очень долгого времени. Эмулированный подготовленный оператор не использует реальные родные операторы mysql, предоставленные mysql api; вместо этого php делает эквивалент вызова PDO::quote() для всех ваших значений и str_replacinging всех ваших заполнителей с указанными вами значениями.

Поскольку вы не можете должным образом избегать строки, если не знаете набор символов, который вы используете, эти эмулированные подготовленные операторы уязвимы для SQL-инъекций, если вы изменили на некоторые наборы символов через set names наборов. Независимо от возможности внедрения sql, вы все равно можете сломать свои строки, если используете схему экранирования, предназначенную для другого набора символов.

Для драйвера pdo mysql вы можете указать набор символов при подключении, указав его в DSN. Клиентская библиотека и сервер будут знать о наборе символов, если вы это сделаете, и поэтому все будет работать так, как должно.

// The key is the "charset=utf8" part.
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8';
$dbh = new PDO($dsn, 'user', 'pass');

Но неправильное экранирование строк - не единственная проблема. Например, у вас также могут быть проблемы с использованием PDO :: bindColumn, потому что имена столбцов указаны как строки, и поэтому снова имеет смысл кодирование. Примером может быть имя столбца с именем ütube (обратите внимание на ütube), и вы переключаетесь с latin на utf8 помощью заданных имен, а затем пытаетесь $stmt->bindColumn('ütube', $var); с ütube является кодировкой utf8, потому что ваш php файл закодирован в utf8. Это не сработает, вам нужно будет закодировать строку как вариант latin1... и теперь у вас все виды сумасшедших.

Ответ 3

Не уверен в py, но php теперь mysql_set_charset, в котором говорится, что это "предпочтительный способ изменить кодировку [и ] с использованием mysql_query() для выполнения SET NAMES не рекомендуется." Обратите внимание, что эта функция была введена для MySQL 5.0.7, поэтому она не будет работать с более ранними версиями.

mysql_set_charset('utf8', $link);

Где $link - это соединение, созданное с помощью mysql_connect