是否使用"SET NAMES"

use*_*291 60 php python mysql internationalization

在阅读O'Reilly的"高性能MySQL"时,我偶然发现了以下情况

另一个常见的垃圾查询是SET NAMES UTF8,这是一种错误的做事方式(它不会改变客户端库的字符集;它只影响服务器).

我有点困惑,因为我曾经在每个脚本的顶部放置"SET NAMES utf8"让db知道我的查询是utf8编码的.

任何人都可以评论上述引用,或者,更正式地说,您的建议/最佳实践是什么,以确保我的数据库工作流是unicode意识.

如果这是相关的,我的目标语言是php和python.

Ste*_*rig 30

mysql_set_charset()将是一个选择 - 但一个选项仅限于ext/mysql.对于ext/mysqli它是mysqli_set_charset和你需要指定一个连接参数.PDO::mysql

由于使用此函数会导致MySQL API调用,因此应该将其视为比发出查询快得多.

在性能方面,确保脚本和MySQL服务器之间基于UTF-8的通信的最快方法是正确设置MySQL服务器.由于SET NAMES x等同

SET character_set_client = x;
SET character_set_results = x;
SET character_set_connection = x;
Run Code Online (Sandbox Code Playgroud)

SET character_set_connection = x内部也执行,SET collation_connection = <<default_collation_of_character_set_x>>您也可以静态设置这些服务器变量my.ini/cnf.

请注意在同一MySQL服务器实例上运行的其他应用程序可能存在的问题,并且需要其他一些字符集.

  • 从PHP 5.0.5开始,mysqli中有一个方法:http://php.net/mysqli_set_charset (3认同)

goa*_*oat 26

TLDR

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

这个答案强调php的pdo库,因为它无处不在.

简短提醒 - mysql是一种客户端 - 服务器架构.这很重要,因为不仅有实际数据库所在的mysql服务器,而且还有单独的mysql客户端驱动程序,它与mysql服务器通信(它们是独立的实体).你可以说有点说mysql客户端和pdo混合在一起.

使用时set names utf8,向mysql发出标准的sql查询.虽然sql查询确实通过pdo,然后通过mysql客户端库,然后最终它到达mysql服务器,只有mysql服务器解析并解释该sql查询.这很重要,因为mysql服务器不会将任何消息发送回pdo或mysql客户端,让它知道字符集和编码已经改变,因此mysql客户端和pdo都完全无视它发生的事实.

重要的是不要这样做,因为如果客户端库不知道当前字符集,则无法正确处理字符串.如果客户端不知道正确的字符集,那么大多数常见操作都能正常工作,但不会出现字符串转义,例如PDO :: quote.您可能认为您不需要担心这样的手动原始字符串转义,因为您使用预准备语句,但事实是绝大多数pdo:mysql用户在不知不觉中使用模拟预准备语句,因为它是pdo的默认设置:mysql司机很长一段时间了.模拟的预处理语句不使用mysql api提供的真正的本机mysql预处理语句; 相反,php相当于调用PDO::quote()所有值,并使用引用的值str_replacing'ing所有占位符.

由于除非您知道正在使用的字符集,否则无法正确转义字符串,如果您已通过更改为某些字符集,则这些模拟的预处理语句很容易被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');
Run Code Online (Sandbox Code Playgroud)

但不正确的字符串转义不是唯一的问题.例如,您也可能在使用PDO :: bindColumn时遇到问题,因为列名被指定为字符串,因此编码也很重要.一个例子可以是一个名为ütube(注意变音符号)的列名,然后从切换latinutf8通过集名称,然后你尝试$stmt->bindColumn('ütube', $var);使用ütubeutf8编码的字符串,因为你的php文件是utf8编码的.它不起作用,你需要将字符串编码为latin1变体......现在你有各种各样的疯狂.

  • 如今(2014年9月)PDO是将PHP与数据库连接起来的最新,最强大的方法,我认为这个答案应该被视为已被接受. (2认同)

typ*_*ror 9

不确定py,但是php mysql_set_charset现在已经声明这是"改变charset [和]使用mysql_query()来执行SET NAMES的首选方法." 请注意,此功能是为MySQL 5.0.7引入的,因此它不适用于早期版本.

mysql_set_charset('utf8', $link);
Run Code Online (Sandbox Code Playgroud)

其中$ link是使用创建的连接 mysql_connect