2009-10-30 14 views
53

En la lectura de "MySQL alto rendimiento" de O'Reilly He topé con la siguiente consultasi se debe usar "SET nombres"

Otra basura común está establecido NOMBRES UTF8, que es la manera incorrecta de hacer las cosas de todos modos (no cambia conjunto de caracteres de la biblioteca del cliente; afecta solo al servidor).

Estoy un poco confundido, porque solía poner "SET NAMES utf8" en la parte superior de cada script para que el db sepa que mis consultas están codificadas en utf8.

¿Alguien puede comentar la cita anterior o, para decirlo de manera más formal, cuáles son sus sugerencias/mejores prácticas para garantizar que mi flujo de trabajo de la base de datos sea compatible con Unicode.

Mis idiomas de destino son php y python si esto es relevante.

+2

¿Qué técnica terminaste implementando? –

Respuesta

28

mysql_set_charset() sería una opción - pero una opción limitada a la ext/mysql. Para ext/mysqli es mysqli_set_charset y para PDO::mysql debe especificar un parámetro de conexión.

Como el uso de esta función da como resultado una llamada a la API MySQL, se debe considerar mucho más rápido que la emisión de una consulta.

Con respecto al rendimiento, la forma más rápida de garantizar una comunicación basada en UTF-8 entre su secuencia de comandos y el servidor MySQL es configurar el servidor MySQL correctamente. Como SET NAMES x es equivalent a

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

mientras que SET character_set_connection = x internamente también ejecuta SET collation_connection = <<default_collation_of_character_set_x>> también se puede configurar de forma estática en su these server variablesmy.ini/cnf.

Tenga en cuenta los posibles problemas con otras aplicaciones que se ejecutan en la misma instancia del servidor MySQL y que requieren algún otro conjunto de caracteres.

+3

A partir de PHP 5.0.5, hay un método en mysqli: http://php.net/mysqli_set_charset – xofer

+0

Mencioné 'mysql_set_charset()' - esa es una función incluida en el antiguo 'ext/mysql'. Como se indicó anteriormente, ni 'PDO' ni' ext/mysqli' proporcionan ningún soporte para esta operación directamente. –

+1

Parece que el enlace que publiqué no es confiable. Aquí hay uno mejor: http://php.net/manual/en/mysqli.set-charset.php No estoy seguro de cómo quiere decir que mysqli no admite esta operación. – xofer

9

No estoy seguro acerca de py, pero php tiene mysql_set_charset ahora, que establece que esta es la "forma preferida de cambiar el juego de caracteres [y] usar mysql_query() para ejecutar SET NAMES no se recomienda." Tenga en cuenta que esta función se introdujo para MySQL 5.0.7, por lo que no funcionará con versiones anteriores.

mysql_set_charset('utf8', $link); 

Donde $ enlace es una conexión creada con mysql_connect

21

TLDR

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

Esta respuesta tiene un énfasis en la biblioteca de PDO de PHP porque es tan ubicua.

Un breve recordatorio: mysql es una arquitectura cliente-servidor. Esto es significativo porque no solo existe el servidor mysql donde está la base de datos real, sino también el controlador de cliente mysql separado, que es lo que habla con el servidor mysql (son entidades separadas). Podría decirse que el cliente mysql y el pdo están mezclados.

Cuando usa set names utf8, emite una consulta SQL estándar a mysql.Mientras que la consulta sql pasa a través de pdo, y luego a través de la biblioteca del cliente mysql, y finalmente llega al servidor mysql, SOLAMENTE el servidor mysql analiza e interpreta esa consulta sql. Esto es significativo porque el servidor mysql no envía ningún mensaje a pdo o al cliente mysql, haciéndole saber que el conjunto de caracteres y la codificación han cambiado, por lo que el pdo ignora por completo el hecho de que sucedió.

Es importante no hacer esto porque la biblioteca del cliente no puede manejar cadenas adecuadamente si no conoce el conjunto de caracteres actual. La mayoría de las operaciones comunes funcionarán correctamente sin que el cliente conozca el juego de caracteres correcto, pero el que no lo hará es el de escape de cadenas, como PDO::quote. Puede pensar que no necesita preocuparse por el escape de cadenas primitivas manuales porque usa sentencias preparadas, pero la verdad es que la gran mayoría de los usuarios de pdo: mysql usan sin saberlo emulated prepared statements porque ha sido la configuración predeterminada para el controlador pdo: mysql para un tiempo muy largo ahora. Una declaración preparada emulada no utiliza declaraciones preparadas de mysql nativas reales provistas por la API de mysql; en su lugar, php hace el equivalente de llamar al PDO::quote() en todos sus valores, y str_replacing'ing todos sus marcadores de posición con los valores cotizados para usted.

Dado que no puede escapar correctamente de una cadena a menos que conozca el juego de caracteres que está utilizando, estas instrucciones preparadas emuladas son vulnerables a la inyección sql si ha cambiado a ciertos conjuntos de caracteres a través de nombres de conjuntos. Independientemente de la posibilidad de inyección sql, aún puede romper sus cadenas si utiliza un esquema de escape destinado a un conjunto de caracteres diferente.

Para el controlador pdo mysql, puede especificar el juego de caracteres cuando se conecta, por specifying it in the DSN. Tanto la biblioteca del cliente como el servidor conocerán el juego de caracteres si lo hace.

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

Pero el escape de cadenas incorrecto no es el único problema. Por ejemplo, también puede tener problemas con el uso de PDO::bindColumn porque los nombres de las columnas se especifican como cadenas, por lo que también importa la codificación. Un ejemplo podría ser un nombre de columna denominado ütube (observe la diéresis), y cambie de latin a utf8 a través de nombres de conjuntos, y luego intente con $stmt->bindColumn('ütube', $var); con ütube siendo una cadena codificada utf8 porque su archivo php está codificado para utf8. No funcionará, necesitarás codificar la cadena como una variante de latin1 ... y ahora tienes todo tipo de locuras sucediendo.

+2

Como hoy en día (septiembre de 2014) PDO es la forma más nueva y robusta de conectar PHP con una base de datos, creo que esta respuesta es la que se debe tomar como aceptada. – rogeriopradoj

Cuestiones relacionadas