我还真没注意 Where 的问题……

看你知道不知道之-你注意Where子句的次序了吗? 【转】

我们通常不太注意SQL语句中Where子句的次序问题,但是这个次序往往会影响整个SQL语句的执行性能,举个例子吧。
比如有一个表有3个列,分别是班级、学号、姓名。
表中的数据共100条,其中1班50人,学号从1到50,二班50人,学号从1到50。
那么现在的任务是在表中找到1班学号为10的学生,查询语句就有两种写法。
1:
Select 姓名 From 学生表 Where 班级=1 And 学号=10
2:
Select 姓名 From 学生表 Where 学号=10 And 班级=1
虽然返回的结果一样,但是这两个到底那个好呢?
衡量一个Sql语句好与坏,主要看性能,而影响Select语句的通常是Table Scan,我们来看看到底执行了几次Table Scan。
1:100+50=150,首先扫描全表找到50个1班的,然后早扫描50次找到学号为10的。
2:100+2=102,首先扫描全表找到2个10号的,然后扫描2次找到班级为1班的。
呵呵,这就说明在Where子句中应该先处理查询范围大的,然后处理查询范围小的,就像开车下坡是越来越快的。
当然这个也并不绝对,因为如果优化了索引,Table Scan就会减轻,并根据索引进行Where子句的优化,但是无论是否索引,我们都应该养成这个好习惯,难道不是吗?

越来越多的接触到一些大型站点的数据库设计……
效率是个大问题……
总想着如何从新技术(如AJAX)上面来节省服务器负载……
却忽略优化 SQL 语句本身这一点……
作个笔记……
下次给我注意着点儿!!!

UTF8 下的中文 PHP 编程

== 前言:

说实话,
凉鞋也觉得 UTF8 是好东西,
毕竟同屏显示中日韩对东亚人的吸引力是不小的,
(当然好处不仅是这点啦,)
不仅是网页程式,
很多应用程式的内核都开始使用 Unicode 编码,
目的是显而易见的:支援多语言显示,
微软的所有软体都是 Unicode 内核,
所以日文软体拿到你的中文 XP 上是可以正常显示的,
而中文 98 就会因为 GB 内核安装其他语言软体时造成乱码,

至于 UTF8 ,
可以说是 Unicode 的一个分支,
它用三个字节保存一个汉字,
(Unicode 用四个字节)
应用软体都集体投奔 Unicode 了,
就不允许我们搞网页程式的用 UTF8?

本文中凉鞋就尽量多方位介绍一下 UTF8 编码下的 PHP 编程,
至于为什么单独介绍“中文”,
一来是因为英文这玩意实在不需要考虑 UTF8 ,
除非你准备做多语言系统,
(我要发些牢骚:现在的老外写程式时根本不重视这个问题,)
二来是中日韩等多字节语系在 UTF8 编码下的处理方式其实大同小异,
依葫芦画瓢即可,
好,先从数据库处理部分开始吧,

== 连接数据库

很多人刚升级到 Mysql 4.1 时会发现数据乱掉了,
其实是因为 Mysql 从 4.1 开始支援字符集了,
而且默认字符集正是 UTF8 ,
(充分证明与国际接轨的重要性, 嘿嘿,)
而以前我们大多使用 utf8 或是 GBK 编码,
这样以来输出的数据当然是乱码,
要解决乱码,
就得让程序知道该获取什么编码的数据,

我们假设你以前的数据库是 utf8 编码的,
那么你可以在查询前添加一句

mysql_query('SET CHARACTER SET utf8') or die("Query failed : " . mysql_error());

当然,由于 4.1 以上才需要这样处理,
而且 utf8 编码必须编译到了 mysql 之中,
因此我们可以加上判断:

$version = mysql_get_server_info();
if($version >= '4.1') {
    // Check that if the charset was compiled in
    $existCharsets = mysql_query("SHOW CHARACTER SET like 'utf8'");
    if(mysql_num_rows($existCharsets) >= 0) {
        mysql_query('SET CHARACTER SET utf8') or die("Query failed : " . mysql_error());
    }
}

这样以来不管 Mysql 默认编码是什么都可以正常存取了,
(不论您是存活期,还是存定期,甚至是零存整取都没有问题鸟,)

但是,人家都国际化鸟,
您还在用 UTF8 行么?
如何转码呢?
还有,
数据升级时出现乱码怎么办?
凉拌!
且听下回分解,

== 数据升级至 4.1

要升级,
就得先导出,
要说老外还真不负责,
以前的导出方式总是弄丢一些中文字符,
比如把“我爱你娘”弄成“我爱你”啦,
(通常是丢失一段数据最末尾的字)
整个儿差了一辈儿,
(用石榴姐的话说就是“这么大逆不道的事实在是太刺激了”,)
为了保护您脆弱的心脏,
也为了维护中国传统伦理道义,
您可以把数据包含中文字符的字段改为二进制编码,
具体方法嘛,
可以运行这个语句:

ALTER TABLE `表名` CONVERT TO CHARACTER SET binary;

这样,那些字符类型字段,如:
CHARVARCHARTEXT
将转换为
BINARYVARBINARYBLOB
然后再导出并导入到 4.1 环境中,
当然,最后一项繁琐的工作是:
你需要把它们的类型再改回来,

有往 4.1 升级的,
当然也有往下降级的,
怎么降级???
凉鞋去上个厕所,
而您请翻下页,

== 数据从 4.1 降级

有人发现从4.1导出的 SQL 文件无法导入低版本程序,
问题其实很简单,
而且 Mysql 已经为我们想好了一切,
导出时请添加 --compatible 参数,
我们假设您的数据库是 utf8 编码的,
而且目标数据库版本为 4.0 ,
那么命令行下如此运行:

mysqldump --user=username --password=password --compatible=mysql40 --default-character-set=utf8 database > db.sql

这样导出的 SQL 文件就能够顺利导入低版本数据库了,

数据库部分算是搞定了,
但 PHP 编程方面要如何注意呢?
还得劳您翻下页,

== PHP 文件编码

是否所有 PHP 文件都必须转成 UTF8 编码呢?
凉鞋告诉您是 NO ,

这么说吧,
如果文件中包含需要显示出来的中文字符,
就应该转为 UTF8 编码,
举例子吧:

// 我是凉鞋
echo time();

上面的代码虽有代码,
但是由于存在于注释中,
不会输出,
所以这个页面可以不用转换为 UTF8 格式,

再如:

echo "我是凉鞋";

这个明显有中文字符输出,
您还是老老实实转换为 UTF8 吧,

当然现在很多程式都采用模板(语言包)技术,
程式(非语言包文件)里是看不到任何供输出的字符的,
这样以来我们只需要将语言包文件转成 UTF8 编码即可,
(语言包的优势就在这里啊, 啊哈哈哈哈哈,)

== UTF8 中文截取

由于 UTF8 使用三个字节,
所以传统的 substr() 函数就没辙了,
很多高手都写了 UTF8 中文字符截取函数,
这里送上几种:

=== 先算再取

/**
 * Author : Dummy | Zandy
 * Email : lianxiwoo#gmail.com | hotmail.com
 * Create : 200512
 * Usage : echo join('', String::subString_UTF8('汉字', 0, 1));
 */
ini_set('display_errors', 1);
error_reporting(E_ALL ^ E_NOTICE);

class String
{
    function subString_UTF8($str, $start, $lenth)
    {
        $len = strlen($str);
        $r = array();
        $n = 0;
        $m = 0;
        for ($i = 0; $i < $len; $i++) {
            $x = substr($str, $i, 1);
            $a = base_convert(ord($x), 10, 2);
            $a = substr('00000000' . $a, -8);
            if ($n < $start) {
                if (substr($a, 0, 1) == 0) {
                } elseif (substr($a, 0, 3) == 110) {
                    $i += 1;
                } elseif (substr($a, 0, 4) == 1110) {
                    $i += 2;
                }
                $n++;
            } else {
                if (substr($a, 0, 1) == 0) {
                    $r[] = substr($str, $i, 1);
                } elseif (substr($a, 0, 3) == 110) {
                    $r[] = substr($str, $i, 2);
                    $i += 1;
                } elseif (substr($a, 0, 4) == 1110) {
                    $r[] = substr($str, $i, 3);
                    $i += 2;
                } else {
                    $r[] = '';
                }
                if (++$m >= $lenth) {
                    break;
                }
            }
        }
        return $r;
    } // End subString_UTF8
}// End String
echo join('', String::subString_UTF8('汉字', 0, 1));

2.先截后取
这种方式凉鞋觉得很巧妙,
用传统截取函数先截断,
然后判断中文单个字符是否被分割开,
如果是,则处理之,
要特别注意的是 substr() 函数的第三个参数必须大于 3 ,
至于为什么不用凉鞋解释了吧?

// A trim function to remove the last character of a utf-8 string
// by following instructions on http://en.wikipedia.org/wiki/UTF-8
// dotann
// usage: $str = utf8_trim(substr($str,0,50));
function utf8_trim($str)
{
    $len = strlen($str);
    for ($i = strlen($str) - 1; $i >= 0; $i -= 1) {
        $hex .= ' ' . ord($str[$i]);
        $ch = ord($str[$i]);
        if (($ch & 128) == 0) return (substr($str, 0, $i));
        if (($ch & 192) == 192) return (substr($str, 0, $i));
    }
    return ($str . $hex);
}

$str = '汉字';
echo utf8_trim(substr($str, 0, 3));

3.还有其它方法,
比如 007pig 为我们 vBulletin 中文版里所写的函数,
短小精悍,
源码不便放出,
对不住鸟,

今天就写到这里吧,
还有转码等问题没有写,
最近忙,
有空继续整理,