<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>ZY&#039;s WordPress</title>
	<atom:link href="http://zyxhome.org/wp/feed/" rel="self" type="application/rss+xml" />
	<link>http://zyxhome.org/wp</link>
	<description>&#34;I code, therefore I am&#34;</description>
	<lastBuildDate>Sat, 12 Jun 2010 19:21:36 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>VC 运行时库中的 new/delete 使用</title>
		<link>http://zyxhome.org/wp/win-dev/vc-crt-new-delete-usage/</link>
		<comments>http://zyxhome.org/wp/win-dev/vc-crt-new-delete-usage/#comments</comments>
		<pubDate>Sat, 05 Jun 2010 00:12:21 +0000</pubDate>
		<dc:creator>zy</dc:creator>
				<category><![CDATA[C/C++ 标准库]]></category>
		<category><![CDATA[Windows 程序开发]]></category>

		<guid isPermaLink="false">/wp/?=485</guid>
		<description><![CDATA[目录

缘起
CRT 的动态链接模块

	使用 msvcr[ver].dll 导出的 new

头文件和模块 msvcr80[d].dll 中 new/delete 的对应
标量 new 与矢量 new[]
放置式 new
no-throw 的 new
调试版的 new
new 申请内存失败
std::set_new_handler 和 _set_new_handler



	使用 msvcr[ver].dll 导出的 delete

delete 和 delete[]
两次重复 delete
delete 空指针
调试版的 delete


msvcp80[d].dll 导出的 new/delete
总结
参考


缘起&#160;&#160;
我用 dependency walker（简称 depends）跟了一下，发现 operator new/delete 函数是从 msvcr[ver].dll 中导出的（如图），其中 ver 是 VC 运行时库（CRT）的版本，例如：VC 2005（简称 VC8）环境下，Release 版本为 80，Debug 版本为 80d。本以为 operator new/delete 是从另一个 msvcp[ver].dll 导出的，其实不是，msvcp[ver].dll 有自己导出的 operator new/delete，但并不是我们编程常规用的 [...]]]></description>
			<content:encoded><![CDATA[<p><a name="post-485-menu">目录</a></p>
<ul class="trangle_img">
<li><a href="#缘起">缘起</a></li>
<li><a href="#CRT 的动态链接模块">CRT 的动态链接模块</a></li>
<li>
<p>	<a href="#使用 msvcr[ver].dll 导出的 new">使用 msvcr[ver].dll 导出的 new</a></p>
<ul class="trangle_img">
<li><a href="#头文件和模块 msvcr80[d].dll 中 new/delete 的对应">头文件和模块 msvcr80[d].dll 中 new/delete 的对应</a></li>
<li><a href="#标量 new 与矢量 new[]">标量 new 与矢量 new[]</a></li>
<li><a href="#放置式 new">放置式 new</a></li>
<li><a href="#no-throw 的 new">no-throw 的 new</a></li>
<li><a href="#调试版的 new">调试版的 new</a></li>
<li><a href="#new 申请内存失败">new 申请内存失败</a></li>
<li><a href="#std::set_new_handler 和 _set_new_handler">std::set_new_handler 和 _set_new_handler</a></li>
</ul>
</li>
<li>
<p>	<a href="#使用 msvcr[ver].dll 导出的 delete">使用 msvcr[ver].dll 导出的 delete</a></p>
<ul class="trangle_img">
<li><a href="#delete 和 delete[]">delete 和 delete[]</a></li>
<li><a href="#两次重复 delete">两次重复 delete</a></li>
<li><a href="#delete 空指针">delete 空指针</a></li>
<li><a href="#调试版的 delete">调试版的 delete</a></li>
</ul>
</li>
<li><a href="#msvcp80[d].dll 导出的 new/delete">msvcp80[d].dll 导出的 new/delete</a></li>
<li><a href="#总结">总结</a></li>
<li><a href="#参考">参考</a></li>
</ul>
<hr class="widen_margin_bottom" />
<h4><a name="缘起"></a>缘起&nbsp;&nbsp;<a href="#post-485-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></h4>
<p>我用 dependency walker（简称 depends）跟了一下，发现 operator new/delete 函数是从 msvcr[ver].dll 中导出的（如图），其中 ver 是 VC 运行时库（CRT）的版本，例如：VC 2005（简称 VC8）环境下，Release 版本为 80，Debug 版本为 80d。本以为 operator new/delete 是从另一个 msvcp[ver].dll 导出的，其实不是，msvcp[ver].dll 有自己导出的 operator new/delete，但并不是我们编程常规用的 new/delete 操作符。</p>
<p><img src="/res/upload/win-dev/vc-crt-new-delete-usage/op_new.png" /></p>
<p><span id="more-485"></span></p>
<h4><a name="CRT 的动态链接模块"></a>CRT 的动态链接模块&nbsp;&nbsp;<a href="#post-485-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></h4>
<p>VC 的运行时库，通常简称 CRT。特指时，它表示 msvcr[ver].dll 这个动态链接库，但在泛义上它和其它几个动态链接库有着紧密的联系，概念上的划分也有共享的部分。这几个都含 Runtime Library 语义的 dll 分别是：msvcr[ver].dll、msvcp[ver].dll、msvcm[ver].dll 和 msvcrt.dll。</p>
<p>看看这些 dll 名字的都代表了什么意思：</p>
<ul style="list-style-type: disc;">
<li>
<p><strong>msvcr[ver].dll</strong></p>
<p>全称为 Microsoft C Runtime Library。msvcr[ver].dll 导出所有的标准 C 库 API，和微软的标准 C 库扩展 API，以及一些 C++ 基本语言特性需要的 API。</p>
</li>
<li>
<p><strong>msvcp[ver].dll</strong></p>
<p>全称 Microsoft C++ Runtime Library。msvcp[ver].dll 导出标准 C++ 库中的 STL 和 iostream 类。</p>
</li>
<li>
<p><strong>msvcm[ver].dll</strong></p>
<p>它也叫做 Microsoft C Runtime Library，不过是给托管代码用的 CRT，所以后缀 m 的含义可以理解为 managed（托管）。msvcm 没有任何对应的静态库，也就是说要使用托管 CRT，只能动态链接到 msvcm[ver].dll。有两种方式指定使用托管 CRT：</p>
<ol>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>使用 /clr 编译选项，这时客户程序可以使用 managed/native 混合代码，并且使用 msvcmrt.lib 导入库。</p>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>使用 /clr:pure 编译选项，这时客户程序使用纯粹的 MSIL 托管代码。</p>
</li>
</ol>
<p>由于 CLR 的 COM 实质，msvcm[ver].dll 依赖这些 dll：Native CRT：msvcr[ver].dll，OLE Library：ole32.dll，以及 .NET Runtime Execution Engine：mscoree.dll。</p>
</li>
</ul>
<p>上面的 msvcr、msvcp、msvcm 是 VC 引入的 dll，在部署应用程序时可以使用微软提供的 VC Redistributable Package 包来安装这些 dll，安装使用 side by side 方式，dll 拷贝到 %SystemRoot%\winsxs 目录下，并且这些 dll 都是以 Release 方式编译的。</p>
<p>另外，还有一种直接拷贝这些 dll 到目标系统的部署方法，参考 MSDN 的 <a href="http://msdn.microsoft.com/en-us/library/ms235291(VS.80).aspx" target="_blank">How to: Deploy using XCopy</a> (VC8) 的 Deploying Visual C++ library DLLs as private assemblies 章节。这些要拷贝的 dll 保存在：Path-to-VS\VC\redist（Release 版）和 Path-to-VS\VC\redist\Debug_NonRedist（Debug 版）。</p>
<ul style="list-style-type: disc;">
<li>
<p><strong>msvcrt.dll</strong></p>
<p>全称 Windows NT CRT DLL，它的名字是固定不带版本号的，位置总在 %SystemRoot%\system32 下。msvcr[ver].dll 和 msvcrt.dll 的作用区别借用 MSDN 的话说为：</p>
<blockquote><p>
from: <a href="http://msdn.microsoft.com/en-us/library/abx4dbyh(v=VS.80).aspx" target="_blank">C Run-Time Libraries</a> (VC8)</p>
<p>What is the difference between msvcrt.dll and msvcr80.dll?</p>
<p>The msvcrt.dll is now a "known DLL", meaning that it is a system component owned and built by Windows. It is intended for future use only by system-level components.
</p></blockquote>
<p>msvcr[ver].dll 依赖 msvcrt.dll。</p>
</li>
</ul>
<h4><a name="使用 msvcr[ver].dll 导出的 new"></a>使用 msvcr[ver].dll 导出的 new&nbsp;&nbsp;<a href="#post-485-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></h4>
<p>下面说说我们用 VC 编译的 C++ 程序中，常规使用的 new/delete 操作符，也就是 msvcr[ver].dll 导出的 operator new/delete 函数，它们的调用和二进制模块 msvcr[ver].dll 的导出函数的对应关系，以及申请内存失败的处理机制。</p>
<p>声明：下面程序的编译、测试环境均为 VC8，用模块名 msvcr80[d].dll 表示无差别情况下的 Debug 或 Release 版 CRT 动态链接库；当提到 VC 的头文件、源文件、静态库、导入库、对象文件等路径时，均是相对于 Visual Studio 2005 的安装目录；给出的 MSDN 参考，若非指明，也是适用于 VC8 版本。</p>
<p>工具：用 depends 查找模块导出符号时，有些不方便。可以使用 VC 的附带工具 dumpbin，用法参考 <a href="http://msdn.microsoft.com/en-us/library/c1h23y6c(VS.90).aspx" target="_blank">DUMPBIN Reference</a> (VC9)，例如：dumpbin /exports msvcp80d.dll &gt; res.txt。</p>
<p><strong><a name="头文件和模块 msvcr80[d].dll 中 new/delete 的对应"></a>头文件和模块 msvcr80[d].dll 中 new/delete 的对应&nbsp;&nbsp;<a href="#post-485-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></strong></p>
<p>有两个关于 new/delete 的头文件：标准 C++ 库的头文件 &lt;new&gt;（没有 .h 后缀），和 CRT 的头文件 &lt;new.h&gt;。&lt;new.h&gt; 对应的模块是 msvcr80[d].dll；而 &lt;new&gt; 中声明的大部分函数对应于 msvcr80[d].dll，如常规的 new/delete 函数，而有些函数，例如下面会讲的 set_new_handler()，则对应 msvcp80[d].dll。</p>
<p>msvcr80[d].dll 导出的 new/delete 有：</p>
<pre class="brush: cpp;">
// 这是模块 msvcr80[d].dll 导出的函数/二进制接口
void* operator new(unsigned int);
void* operator new(unsigned int, int, char const*, int);
void* operator new[](unsigned int);
void* operator new[](unsigned int, int, char const*, int);

void operator delete(void*);
void operator delete[](void*);
</pre>
<p>这些就是我们编程中常规用的 new/delete 的二进制接口。</p>
<p>在 C++ 源码级别，总共有 6 个 new 函数声明，上面的第一个导出函数 void* operator new(unsigned int) 对应其中的 4 个：</p>
<pre class="brush: cpp;">
// 这是源文件中声明的函数原型
void* operator new(size_t count) throw(std::bad_alloc);
void* operator new(size_t count, const std::nothrow_t&amp;) throw();

void* operator new[](size_t count) throw(std::bad_alloc);
void* operator new[](size_t count, const std::nothrow_t&amp;) throw();
</pre>
<p>为什么二进制的 void* operator new(unsigned int) 也会对应声明的 operator new[] 原型？后面会慢慢道来。</p>
<p>另外 2 个 new 函数的声明是：</p>
<pre class="brush: cpp;">
void* operator new(size_t count, void* object) throw();
void* operator new[](size_t count, void* object) throw();
</pre>
<p>这两个被称为放置式的 new，不对应任何运行时库的动态链接库，它们的二进制代码会编译进客户程序中。</p>
<p>上面 6 个 new 的使用语法参考：<a href="http://msdn.microsoft.com/en-us/library/3zf921y2(v=VS.80).aspx" target="_blank">operator new (CRT)</a> 和 <a href="http://msdn.microsoft.com/en-us/library/1yh4zsas(v=VS.80).aspx" target="_blank">operator new[] (CRT)</a>。</p>
<p>下面就说说这几个 new 的区别。</p>
<p><strong><a name="标量 new 与矢量 new[]"></a>标量 new 与矢量 new[]&nbsp;&nbsp;<a href="#post-485-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></strong></p>
<p>标量 new（scalar new），即 operator new；矢量 new（vector new），operator new[]。</p>
<p>按照 MSDN <a href="http://msdn.microsoft.com/en-us/library/1yh4zsas(VS.80).aspx" target="_blank">operator new[] (CRT)</a> 的说法，当使用 new 申请数组块内存时，就调用矢量的 operator new[]。</p>
<p>我在实际的 VC8 环境测试中，发现下列客户代码均会调用 msvcr80[d].dll 导出的 void* operator new(unsigned int)，而非导出的 void* operator new[](unsigned int)：</p>
<pre class="brush: cpp;">
// 基本类型
char* s1 = new char[10];
int* s2 = new int[10];
// POD 的平坦结构
TestStruct* s3 = new TestStruct[10];
// non-POD 的类
TestObj* s4 = new TestObj[10];
</pre>
<p>反汇编调试后发现，在客户程序中 VC8 编译器已经将申请数组块的整体大小计算出来了，比如 sizeof(TestObj) = 40，则编译器就会计算出 new TestObj[10] 申请的大小为 40 * 10 = 400。</p>
<ul style="list-style-type: disc;">
<li>
<p>如果此时仅包含 &lt;new.h&gt;，或者不包含 &lt;new&gt; 和 &lt;new.h&gt; 任一个，则会调用 msvcr80[d].dll 中导出的 void * operator new(unsigned int)。</p>
</li>
<li>
<p>如果仅包含 &lt;new&gt;，或者 &lt;new&gt;、&lt;new.h&gt; 两个都包含（顺序无关），则会调用 VC\crt\src\newaop.cpp 中定义的 operator new[]（aop 的含义是 array operator）。那么 newaop.cpp 中的 operator new[] 是否是 msvcr80[d].dll 中导出的 void* operator new[](unsigned int)？答案不是，从 VC 的 Call Stack 中看出 newaop.cpp 对应的二进制模块是客户程序的模块而非 msvcr80[d].dll。newaop.cpp 定义的 operator new[] 是 msvcr80[d].dll 导出的 void* operator new(unsigned int) 的简单包裹，最后将调用传给它。</p>
</li>
</ul>
<p>delete/delete[] 的调用也有这种受包含 &lt;new&gt; 还是 &lt;new.h&gt; 影响的问题，见下面对 delete 的讨论。</p>
<p>所以问题是，虽然在 msvcr80[d].dll 中导出了 operator new[]，但似乎不能通过标准的方法使用它。</p>
<p><strong><a name="放置式 new"></a>放置式 new&nbsp;&nbsp;<a href="#post-485-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></strong></p>
<p>按 new 的行为是申请存储位置，还是将对象放置到某个存储位置，可以分为：非放置式（nonplacement）new，和 放置式（placement）new。</p>
<p>放置式 new 语法参考：《C++ 程序设计语言》特别版（Bjarne）章节 10.4.11 对象的放置。有两种惯用的放置手法：1. 放置到已有存储位置。2. 使用 Arena（场地）分配存储位置（自定义放置式 new）。</p>
<p>VC CRT 中提供了第一种放置式 new，在 &lt;new&gt; 和 &lt;new.h&gt; 中都有声明：</p>
<pre class="brush: cpp;">
// &lt;new.h&gt; 中声明：
inline void *__CRTDECL operator new(size_t, void *_Where);

// &lt;new&gt; 中声明：
inline void *__CRTDECL operator new(size_t, void *_Where) _THROW0();
inline void *__CRTDECL operator new[](size_t, void *_Where) _THROW0();
</pre>
<p>这些放置 new 都是 inline 函数，声明的同时给出定义，函数体都是一句简单的 return (_Where)，所以不存在 msvcr80[d].dll 中导出的放置式 new。</p>
<p>测试放置式 new 的代码：</p>
<pre class="brush: cpp;">
#include &lt;new.h&gt;

class TestObj
{
public:
    int a[10];
    TestObj(int _a = 1)
    {
        for (int i = 0; i &lt; 10; i++)
            a[i] = _a;
    }
};

int main()
{
    TestObj* place = (TestObj*) malloc(sizeof(TestObj));

    // 用 TestObj(2) 初始化放置位置，注意：这种初始化是直接的，即
    // 调用直接构造函数 TestObj() 完成，而没有拷贝构造 TestObj(const TestObj&amp; obj)
    // 或赋值 TestObj&amp; operator=(const TestObj&amp; obj) 语义。

    TestObj* s1 = new(place) TestObj(2);

    // s1 和 place 指向同一块分配内存，下句会将这块内存释放
    delete s1;
    return 0;
}
</pre>
<p><strong><a name="no-throw 的 new"></a>no-throw 的 new&nbsp;&nbsp;<a href="#post-485-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></strong></p>
<p>还有一种 MSDN 上称为 placement, no-throw 的 operator new/new[]，在 &lt;new.h&gt; 和 &lt;new&gt; 中都有声明：</p>
<pre class="brush: cpp;">
// &lt;new.h&gt; 中的声明：
__bcount_opt(_Size) void *__CRTDECL operator new(size_t _Size, const std::nothrow_t&amp;) throw();
__bcount_opt(_Size) void *__CRTDECL operator new[](size_t _Size, const std::nothrow_t&amp;) throw();

// &lt;new&gt; 中的声明：
__bcount_opt(_Size) void *__CRTDECL operator new(size_t _Size, const std::nothrow_t&amp;) _THROW0();
__bcount_opt(_Size) void *__CRTDECL operator new[](size_t _Size, const std::nothrow_t&amp;) _THROW0();
</pre>
<p>不过它和放置位置没有关系，而是影响内存申请失败时的报告机制，如果使用这种 new，则申请失败时将以 operator new 返回 0 值表示失败，否则使用默认抛出异常的方式表示申请失败。</p>
<p>以调用 new(std::nothrow) char[BIG_SIZE] 为例，调用顺序如下：</p>
<ol>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>客户程序模块: newaopnt.cpp: void* __CRTDECL operator new[](::size_t count, const std::nothrow_t&#038; x)</p>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>客户程序模块: newopnt.cpp: void* __CRTDECL operator new(size_t count, const std::nothrow_t&#038;)</p>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>msvcr80[d].dll: 导出的 void* operator new(unsigned int)</p>
</li>
</ol>
<p>newaopnt.cpp 和 newopnt.cpp 在 VC\crt\src 目录下，后缀 nt 表示 no-throw。</p>
<p>更详细的 new 申请失败报告机制在后面叙述。</p>
<p><strong><a name="调试版的 new"></a>调试版的 new&nbsp;&nbsp;<a href="#post-485-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></strong></p>
<p>在 msvcr80[d].dll 中有两个 4 个参数的 operator new 函数的导出：</p>
<pre class="brush: cpp;">
void* operator new(unsigned int, int, char const*, int);
void* operator new[](unsigned int, int, char const*, int);
</pre>
<p>这两个函数的声明在 &lt;crtdbg.h&gt; 中，实现源码在 &lt;dbgnew.cpp&gt;，用法参考 <a href="http://msdn.microsoft.com/en-us/library/19f56tw3(VS.80).aspx" target="_blank">The Debug Heap from C++</a>。示例：</p>
<p>在工程公共头文件中：</p>
<pre class="brush: cpp;">
// common.h

#include &lt;crtdbg.h&gt;

// Defines global operator new to allocate from client blocks

#ifdef _DEBUG
	#define DEBUG_CLIENTBLOCK	new(_CLIENT_BLOCK, __FILE__, __LINE__)
#else
	#define DEBUG_CLIENTBLOCK
#endif // _DEBUG
</pre>
<p>在源文件中：</p>
<pre class="brush: cpp;">
#include &quot;common.h&quot;

#ifdef _DEBUG
#define new	DEBUG_CLIENTBLOCK
#endif

int main()
{
	char* p1;
	p1 = new char[40];
	_CrtMemDumpAllObjectsSince(NULL);
}
</pre>
<p>上面申请数组块内存时，会调用 msvcr80d.dll 导出的 void* operator new[](unsigned int, int, char const*, int)。</p>
<p>_CrtMemDumpAllObjectsSince(NULL) 将从程序开始到其调用点的所有堆对象调试信息，转储到调试输出，比如上面用 operator new[](unsigned int, int, char const*, int) 申请内存时，就会产生调试信息。_CrtMemDumpAllObjectsSince() 只在调试版起作用（有 _DEBUG 定义），当没有 _DEBUG 定义时，_CrtMemDumpAllObjectsSince() 就被替换成一个空操作 ((void)0)，如同 _ASSERT() 的实现一样（_ASSERT() 也在 &lt;crtdbg.h&gt; 中定义）。可以用 VC 的调试 Output 窗口，或 DebugView 工具查看 _CrtMemDumpAllObjectsSince() 的输出。</p>
<p><strong><a name="new 申请内存失败"></a>new 申请内存失败&nbsp;&nbsp;<a href="#post-485-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></strong></p>
<p>参考：<a href="http://msdn.microsoft.com/en-us/library/kftdy56f(v=VS.80).aspx" target="_blank">The new and delete Operators</a></p>
<p>msvcr80[d].dll 中导出的 new 的报告申请失败的默认方式是抛出 std::bad_alloc 异常。测试例子：</p>
<pre class="brush: cpp;">
#include &lt;stdio.h&gt;
#include &lt;typeinfo&gt;
#include &lt;new&gt;

#define BIG_SIZE	0x7fffffff

char* s1 = NULL;

try
{
	s1 = new char[BIG_SIZE];
}
catch (std::bad_alloc&amp; e)
{
	printf(&quot;Caught: %s\n&quot;, e.what());
	printf(&quot;Type: %s\n&quot;, typeid(e).name());
}
</pre>
<p>上面的代码编译后不会链接到 msvcp80[d].dll，因为标准 C++ 异常类（即 std::bad_alloc）和 C++ RTTI 类（type_info），均在 msvcr80[d].dll 中有导出。</p>
<p>如果想使用返回 0 来表示 new 申请内存失败，除了使用上面提到的 placement, no-throw 的 new 外，调试版的 operator new/new[](unsigned int, int, char const*, int) 也是以返回 0 表示失败，并不抛出异常。</p>
<p>另外，还有一种用返回 0 来表示失败的方法，就是和 VC\lib\nothrownew.obj 链接，此时便不会调用 msvcr80[d].dll 中导出的 new（用 depends 可以观察到），而调用 nothrownew.obj 中包含的 new。</p>
<p>VC 编译出代码的异常处理方式（Exception Handling Model）和编译选项 /EH 有关系，它会影响 C 和 C++ 两种语言中的异常处理，以及标准 C++ 规范中的异常（try-catch 结构）和 Windows 特有的异常处理方式 SEH (Structured Exception Handling)（__try-__except-__finally 结构）。</p>
<p>关于 VC 中 C/C++ 的异常处理，和更多 new/delete 操作的内容，请查阅文章最后的参考。</p>
<p><strong><a name="std::set_new_handler 和 _set_new_handler"></a>std::set_new_handler 和 _set_new_handler&nbsp;&nbsp;<a href="#post-485-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></strong></p>
<ul style="list-style-type: disc;">
<li>
<p><strong>std::set_new_handler</strong></p>
<p>参考：</p>
<ul style="list-style-type: disc;">
<li>
<p><a href="http://msdn.microsoft.com/en-US/library/5fath9te(v=VS.80).aspx" target="_blank">set_new_handler</a>：set_new_handler 的用法。</p>
</li>
<li>
<p><a href="http://msdn.microsoft.com/en-US/library/we2zys4d(v=VS.80).aspx" target="_blank">operator new (&lt;new&gt;)</a>：包含 operator new() 的循环尝试申请工作机制，和自定义 new_handler 应该完成的功能。</p>
</li>
</ul>
<p>set_new_handler() 是 C++ 标准中定义的 new 申请失败处理设定函数，使用的失败处理函数类型为：typedef void (*new_handler)()。相关函数、类型在 &lt;new&gt; 中声明，set_new_handler() 在 msvcp80[d].dll 中导出。</p>
<p>例子：</p>
<pre class="brush: cpp;">
#include &lt;new&gt;
#include &lt;typeinfo&gt;

using namespace std;

#define BIG_SIZE	0x7fffffff

void my_handler()
{
	printf(&quot;allocation failed.&quot;);

	// 除非你产生更多可用的内存，否则应该抛出一个异常
	throw bad_alloc();
}

int main()
{
	new_handler old_handler = set_new_handler(my_handler);
	char* s1 = NULL;

	try
	{
		s1 = new char[BIG_SIZE];
	}
	catch (exception&amp; e)
	{
		printf(&quot;Caught: %s\n&quot;, e.what());
		printf(&quot;Type: %s\n&quot;, typeid(e).name());
	}

	delete[] s1;
	s1 = NULL;

	set_new_handler(old_handler);

	return 0;
}
</pre>
<p>上面代码会链接到 msvcp80[d].dll。</p>
<p>自定义的错误处理函数 new_handler，必需完成 3 种功能之一：</p>
<ol>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>产生更多可用的内存以供申请，例如使用垃圾回收、不常用对象交换到文件等手段，具体方式和应用有关，此时该函数可以用直接返回的方式离开，随后控制返回给 operator new()，并再次尝试申请内存。</p>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>调用 abort 或 exit 函数，让 CRT 负责并终止程序执行。</p>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>抛出一个异常，通常是 std::bad_alloc，该异常会穿过 operator new() 一直上抛到客户程序，此时 new_handler 通过异常方式离开 operator new()。</p>
</li>
</ol>
<p>所以用户的 new_handler 实现中，如果即没有产生更多可用内存的工作，又不通过异常或 abort/exit 方式离开 operator new()，则 operator new() 就会陷入一直调用 new_handler 的死循环。</p>
</li>
<li>
<p><strong>_set_new_handler</strong></p>
<p>参考：<a href="http://msdn.microsoft.com/en-US/library/a45x8asx(v=VS.80).aspx" target="_blank">_set_new_handler</a></p>
<p>_set_new_handler() 是 CRT 提供的 new 申请失败处理设定函数，使用的失败处理函数类型为：typedef int (*_PNH)(size_t)。相关函数、类型在 &lt;new.h&gt; 中声明，_set_new_handler() 在 msvcr80[d].dll 中导出。</p>
<p>例子：</p>
<pre class="brush: cpp;">
#include &lt;new.h&gt;
#include &lt;typeinfo&gt;

using namespace std;

#define BIG_SIZE	0x7fffffff

int my_handler(size_t sz)
{
	printf(&quot;allocation failed, request size: %d\n&quot;, sz);

	// 除非你产生更多可用的内存，否则应该返回 0
	return 0;
}

int main()
{
	_PNH old_handler = _set_new_handler(my_handler);
	char* s1 = NULL;

	try
	{
		s1 = new char[BIG_SIZE];
	}
	catch (exception&amp; e)
	{
		printf(&quot;Caught: %s\n&quot;, e.what());
		printf(&quot;Type: %s\n&quot;, typeid(e).name());
	}

	delete[] s1;
	s1 = NULL;

	_set_new_handler(old_handler);

	return 0;
}
</pre>
<p>上面代码会仅会链接到 msvcr80[d].dll，而不会链接 msvcp80[d].dll。</p>
<p>和标准 C++ 库中的 new 失败处理函数不同，CRT 中规定的失败处理函数 int (*_PNH)(size_t)，有返回值和参数。_PNH 的参数 size_t，表示请求申请但失败的内存大小，而 int 型返回值表示：</p>
<ol>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>返回非 0 值，表示 _PNH 做过一些产生更多可用内存的工作，控制返回给 operator new() 后，会再次尝试申请内存。该情况和 C++ new_handler 的直接返回类似。</p>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>返回 0，表示无需让 operator new() 再次尝试申请内存，申请操作已经彻底失败，最终控制会以默认抛出异常方式回到客户程序。该情况和 C++ new_handler 的以抛出异常方式结束申请类似。</p>
</li>
</ol>
<p>所以类似标准 C++ 库的 new_handler，_PNH 如果即没有产生更多可用内存的工作，又返回了非 0 值，则 operator new() 就会陷入一直调用 _PNH 的死循环。</p>
<p>_PNH 是作用于 CRT 提供的全局 operator new() 的，要想 _PNH 也作用于 malloc()，使得 malloc() 申请失败时也调用 _PNH 处理，可以调用 _set_new_mode(1) 激活 malloc() 的失败处理机制。</p>
<p>在二进制层次，_PNH 函数是在所有模块间共享的，在 dll 中设定的 _PNH 会影响到主 exe 和其它 dll 中的 operator new() 行为。</p>
</li>
</ul>
<h4><a name="使用 msvcr[ver].dll 导出的 delete"></a>使用 msvcr[ver].dll 导出的 delete&nbsp;&nbsp;<a href="#post-485-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></h4>
<p><strong><a name="delete 和 delete[]"></a>delete 和 delete[]&nbsp;&nbsp;<a href="#post-485-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></strong></p>
<p>在客户代码中使用 delete[] 时，如果仅包含 &lt;new&gt;，或者 &lt;new&gt;、&lt;new.h&gt; 两个都包含（顺序无关），则调用 msvcr80[d].dll 导出的 void operator delete[](void*)。</p>
<p>如果仅包含 &lt;new.h&gt;，或者不包含 &lt;new&gt; 和 &lt;new.h&gt; 任一个，则调用 msvcr80[d].dll 导出的 void operator delete(void*)。</p>
<p>实际上 msvcr80[d].dll 的 void operator delete[](void*) 实现，仅仅是对 void operator delete(void*) 做了一个简单的包裹。delete[] 的源码在 VC\crt\src\delete2.cpp，Release 版的 delete 的源码在 VC\crt\src\delete.cpp，Debug 版的 delete 的源码在 VC\crt\src\dbgdel.cpp。</p>
<p><strong><a name="两次重复 delete"></a>两次重复 delete&nbsp;&nbsp;<a href="#post-485-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></strong></p>
<p>示例：</p>
<pre class="brush: cpp;">
char* s1 = new char[10];
delete[] s1;
delete[] s1;
</pre>
<p>在 Debug 配置下，即链接到 msvcr80d.dll，dbgdel.cpp 中定义的 delete 在程序运行时会检查到这种情况，并报 assert 诊断错误。</p>
<p>在 Release 配置下，即链接到 msvcr80.dll，如果程序运行时 attach 到 VC 的调试器，则在重复 delete 的位置会给出警告，中止运行并报错 "This may be due to a corruption of the heap, and indicates a bug in [program-name.exe] or any of the DLLs it has loaded."，可以让调试器继续运行程序。如果没有 attach 到调试器，而是独立运行程序，则不会看到任何警告提示，程序运行直到结束，这可能会造成复杂程序中 bug 的潜藏点。</p>
<p><strong><a name="delete 空指针"></a>delete 空指针&nbsp;&nbsp;<a href="#post-485-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></strong></p>
<p>在上面重复 delete 的代码中加上一句，如下：</p>
<pre class="brush: cpp;">
char* s1 = new char[10];
delete[] s1;
s1 = NULL;
delete[] s1;
</pre>
<p>和重复 delete 不同，上述 delete 空指针无论是从 C++ 语法标准的角度（参考《C++ 程序设计语言》章节 6.2.6 自由存储），还是在实际的 VC8 环境中都是正确的，Debug 和 Release 版的 CRT 库均会正常运行，不会报出错误或警告。</p>
<p><strong><a name="调试版的 delete"></a>调试版的 delete&nbsp;&nbsp;<a href="#post-485-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></strong></p>
<p>参考：<a href="http://msdn.microsoft.com/en-us/library/19f56tw3(VS.80).aspx" target="_blank">The Debug Heap from C++</a></p>
<p>和使用调试版的 void* operator new(unsigned int, int, char const*, int) 不同，不需用户写 delete 操作的替换宏和更改任何代码，只需用 Debug 配置方式编译程序即可使用调试版的 delete，而换到 Release 方式编译就可使用一般的 delete。</p>
<h4><a name="msvcp80[d].dll 导出的 new/delete"></a>msvcp80[d].dll 导出的 new/delete&nbsp;&nbsp;<a href="#post-485-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></h4>
<p>最后，不妨看看 msvcp80.dll 导出的 operator new/delete 函数，大家想想这些接口在何种情况下被调用呢？</p>
<pre class="brush: cpp;">
void* operator new(unsigned int, struct std::_DebugHeapTag_t const &amp;, char*, int);
void* operator new[](unsigned int, struct std::_DebugHeapTag_t const &amp;, char*, int);

void operator delete(void *, struct std::_DebugHeapTag_t const &amp;, char*, int);
void operator delete[](void *, struct std::_DebugHeapTag_t const &amp;, char*, int);
</pre>
<p>除了上述全局的 new/delete 外，msvcp80[d].dll 中还导出了 std::locale::facet 类定义的 new/delete，不过这些跟用户常规的 new/delete 操作都没有关系。</p>
<h4><a name="总结"></a>总结&nbsp;&nbsp;<a href="#post-485-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></h4>
<p>CRT 的模块 msvcr80[d].dll 中并非只含 C API，下面标准 C++ 库的 API 也在其中：</p>
<ul style="list-style-type: disc;">
<li>
<p>常规使用的 operator new/delete，包括调试版 void* operator new(unsigned int, int, char const*, int)，对应头文件 &lt;new&gt; 和 &lt;new.h&gt;。</p>
</li>
<li>
<p>标准 C++ 异常类，对应头文件 &lt;exception&gt;。</p>
</li>
<li>
<p>标准 C++ RTTI 类，对应头文件 &lt;type_info&gt; 和 &lt;type_info.h&gt;</p>
</li>
</ul>
<p>而 msvcp80[d].dll 则侧重于标准 C++ 库中的 STL 和 iostream 类的实现，basic_string、vector、basic_ostream 等均在这里导出，另外还包括 &lt;new&gt; 中声明的 std::set_new_handler()。</p>
<p>VC 的这种混合 C API 和标准 C++ 库的模块管理方式，让人感觉很混乱并摸不着头脑。其实，微软这么做是有考虑的，试想一下，如果你即想使用 C++ 的基本语言特性，如 new/delete、RTTI 等，又不想依赖额外的标准 C++ 库，如一大堆 STL 模板类和 iostream 类，此时就可以仅链接 msvcr80[d].dll。总之，只要记住 msvcr80[d].dll 包括所有 VC 基本的 C/C++ 语言支持就好了，这也是 CRT“运行时”库的内涵所在。</p>
<h4><a name="参考"></a>参考&nbsp;&nbsp;<a href="#post-485-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></h4>
<ul style="list-style-type: disc;">
<li>
<p><a href="http://msdn.microsoft.com/en-us/library/abx4dbyh(v=VS.80).aspx" target="_blank">C Run-Time Libraries</a>：说明 VC 中：运行时库（CRT）、托管代码运行时库、标准 C++ 库，对应的静态、动态、导入库文件，以及编译选项。</p>
</li>
<li>
<p><a href="http://msdn.microsoft.com/en-us/library/zh712wwf(v=VS.80).aspx" target="_blank">CRT Debugging Techniques</a>：CRT 中的调试技术，包括调试用到的宏、函数，调试版的堆管理函数 malloc、new/delete 等。</p>
</li>
<li>
<p><a href="http://msdn.microsoft.com/en-US/library/1deeycx5(v=VS.80).aspx" target="_blank">/EH (Exception Handling Model)</a>：VC 用来控制异常处理方式的编译选项 /EH，默认的 VC 工程通常使用 /EHsc 选项编译。</p>
</li>
<li>
<p><a href="http://msdn.microsoft.com/en-US/library/x057540h(v=VS.80).aspx" target="_blank">Exception Handling in Visual C++</a>：讲述 VC 支持的两种异常处理方式：C++ 异常 和 SEH (Structured Exception Handling) 的工作机制和语法。建议先看其中的 <a href="http://msdn.microsoft.com/en-US/library/wfa0edys(v=VS.80).aspx" target="_blank">Exception Specifications</a>，明白函数的 throw() 修饰词怎样和编译选项 /EH 相互作用。</p>
</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://zyxhome.org/wp/win-dev/vc-crt-new-delete-usage/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>文件对比工具 Beyond Compare 和 搜索工具 Everything 介绍</title>
		<link>http://zyxhome.org/wp/app/intro-beyond-compare-everything/</link>
		<comments>http://zyxhome.org/wp/app/intro-beyond-compare-everything/#comments</comments>
		<pubDate>Tue, 01 Jun 2010 01:33:30 +0000</pubDate>
		<dc:creator>zy</dc:creator>
				<category><![CDATA[Windows 管理与应用]]></category>
		<category><![CDATA[应用技巧]]></category>

		<guid isPermaLink="false">/wp/?=484</guid>
		<description><![CDATA[今天介绍两个工具：文件对比工具 Beyond Compare 和 搜索工具 Everything。
不得不向还没有用过的同学们介绍这两个工具，因为它们太强大、太方便了，对于海量文件的整理和搜索任务特别有帮助，特别适合程序员、文秘等 IT 工作的人。我从它们上获得了很多的益处，故而才介绍别人使用。

Beyond Compare
主页：www.scootersoftware.com
在 HW 认识的对比工具，那时就已经觉得很强大，当时用来对比更改前后的源码，CMO 经常用它做版本库源文件交叉覆盖的检查。可是 Beyond Compare（简称 BC）能做的更多……
我不说 BC 的用法，基本用法一分钟上手：1. 在文件浏览器中关联 BC 的右键菜单。2. 在文件浏览器中第一次右键选择一个文件/文件夹作为左侧。3. 在文件浏览器中第二次右键选择一个文件/文件夹作为右侧，并打开 BC 窗口开始比较。BC 高级的用法我也是随用随学的，网上有很多介绍，也可自己挖掘技巧。
我只说说我使用 BC 的场景，有同样需求的人就知道它就是你要找的工具了。


比较文本文件，包括源代码文件


翻出以前对某个别人写的程序的扩展（或自己写的程序的升级版），忘了改哪了，用 BC 比一下。


一份基线代码，多个人在改，要提交你今天的工作了，把工作代码和当前基线用 BC 比一下，只合并你改的部分，OK 提交，不用担心覆盖别人的代码。


次要差异，对于 C/C++ 代码，缩进用的空格或 Tab 还有空行对于程序逻辑都没什么影响，称为次要差异，可以让 BC 标识或忽略这些次要差异，工具条上的“约等于”按钮激活这个功能。


字符集编码和换行符类型，两份文件，一份在 Linux 上写的，一份在 Windows 上写的，或者一份用 GBK 编码，一份用 UTF-8 编码，但逻辑内容完全一样，BC 在对比时可以显式指定这些影响因素。




比较文件夹
对文件夹，及其子目录结构，和所有内含文件进行比较，通常用来完成：


比较两个工程中的所有文件或代码。


完成同步文件夹的任务。这是我现在用 BC 完成的重头任务。叙述一下场景：在家里下了一些资料，在公司也下了一些资料，另外移动硬盘上也保存了一些资料。在开始的时候，文件少，用移动硬盘把家里的和公司的资料互传，和移动硬盘上的资料保证一致。后来，随着资料越攒越多，出现各种各样的情况：在公司新下了一些资料，在家里也新下了一些资料，都没来得即拷到移动硬盘上并整理；家里下的资料拷到移动硬盘上，或带到公司发现文件名不合适进行改名，或文件换了个目录保存，或觉得这东西写地太烂删掉了。等等。所有这些操作会使得公司、家里和移动硬盘上的保存资料的文件夹完全不一致，混乱不堪，根本不可查找和整理。在大多同步工具中，这种情况有个专有名称叫做“冲突”。有很多优秀的同步软件，比如 Synkron，原先是 Linux 下的现在也有 Windows 版，我早先一直用它，直到我发现 BC [...]]]></description>
			<content:encoded><![CDATA[<p>今天介绍两个工具：文件对比工具 Beyond Compare 和 搜索工具 Everything。</p>
<p>不得不向还没有用过的同学们介绍这两个工具，因为它们太强大、太方便了，对于海量文件的整理和搜索任务特别有帮助，特别适合程序员、文秘等 IT 工作的人。我从它们上获得了很多的益处，故而才介绍别人使用。</p>
<p><span id="more-484"></span></p>
<h4>Beyond Compare</h4>
<p>主页：<a href="http://www.scootersoftware.com/moreinfo.php" target="_blank">www.scootersoftware.com</a></p>
<p>在 HW 认识的对比工具，那时就已经觉得很强大，当时用来对比更改前后的源码，CMO 经常用它做版本库源文件交叉覆盖的检查。可是 Beyond Compare（简称 BC）能做的更多……</p>
<p>我不说 BC 的用法，基本用法一分钟上手：1. 在文件浏览器中关联 BC 的右键菜单。2. 在文件浏览器中第一次右键选择一个文件/文件夹作为左侧。3. 在文件浏览器中第二次右键选择一个文件/文件夹作为右侧，并打开 BC 窗口开始比较。BC 高级的用法我也是随用随学的，网上有很多介绍，也可自己挖掘技巧。</p>
<p>我只说说我使用 BC 的场景，有同样需求的人就知道它就是你要找的工具了。</p>
<ol>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>比较文本文件，包括源代码文件</p>
<ul style="list-style-type: disc;">
<li>
<p>翻出以前对某个别人写的程序的扩展（或自己写的程序的升级版），忘了改哪了，用 BC 比一下。</p>
</li>
<li>
<p>一份基线代码，多个人在改，要提交你今天的工作了，把工作代码和当前基线用 BC 比一下，只合并你改的部分，OK 提交，不用担心覆盖别人的代码。</p>
</li>
<li>
<p>次要差异，对于 C/C++ 代码，缩进用的空格或 Tab 还有空行对于程序逻辑都没什么影响，称为次要差异，可以让 BC 标识或忽略这些次要差异，工具条上的“约等于”按钮激活这个功能。</p>
</li>
<li>
<p>字符集编码和换行符类型，两份文件，一份在 Linux 上写的，一份在 Windows 上写的，或者一份用 GBK 编码，一份用 UTF-8 编码，但逻辑内容完全一样，BC 在对比时可以显式指定这些影响因素。</p>
</li>
</ul>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>比较文件夹</p>
<p>对文件夹，及其子目录结构，和所有内含文件进行比较，通常用来完成：</p>
<ul style="list-style-type: disc;">
<li>
<p>比较两个工程中的所有文件或代码。</p>
</li>
<li>
<p>完成同步文件夹的任务。这是我现在用 BC 完成的重头任务。叙述一下场景：在家里下了一些资料，在公司也下了一些资料，另外移动硬盘上也保存了一些资料。在开始的时候，文件少，用移动硬盘把家里的和公司的资料互传，和移动硬盘上的资料保证一致。后来，随着资料越攒越多，出现各种各样的情况：在公司新下了一些资料，在家里也新下了一些资料，都没来得即拷到移动硬盘上并整理；家里下的资料拷到移动硬盘上，或带到公司发现文件名不合适进行改名，或文件换了个目录保存，或觉得这东西写地太烂删掉了。等等。所有这些操作会使得公司、家里和移动硬盘上的保存资料的文件夹完全不一致，混乱不堪，根本不可查找和整理。在大多同步工具中，这种情况有个专有名称叫做“冲突”。有很多优秀的同步软件，比如 Synkron，原先是 Linux 下的现在也有 Windows 版，我早先一直用它，直到我发现 BC 也可以做同步工具。不是说 Synkron 不好，而是我想用一个词来形容 BC 做同步：BC 做同步的使用是“直觉的”（台湾程序员老爱用的一个词，很有意念~），一点点的操作 + 一点点的使用习惯，就让你管理海量资料的整理。</p>
<p>同步按钮在哪？打开文件夹对比视图后，右键左右文件列表中间空白的竖条，右键菜单->同步，我一般用的是“更新左侧”或“更新右侧”，因为这两个操作的语义很明确，就是拷贝文件到你要更新的侧，如果是文件夹就是拷贝所有子目录及文件，如果文件名相同但内容不同就会覆盖。多说无益，贴张图：</p>
<p><a href="/res/upload/app/intro-beyond-compare-everything/bc_01.png" target="_blank"><img style="width:637px;" src="/res/upload/app/intro-beyond-compare-everything/bc_01.png" /></a></p>
<p>我用 BC 管理 10+GB 级别的资料很轻松（没有视频，全是电子书、文档等，视频那种东西应该被刻盘的），不过光靠工具没有整理的习惯，待到不同地方的资料已是山花烂漫时，再强的同步工具也不顶鸟用，不过那种人适合我介绍的第二个工具 Everything。</p>
</li>
</ul>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>BC 的其它技巧</p>
<ul style="list-style-type: disc;">
<li>
<p>更新比较。BC 能够快速的展现比较视图，是因为它用了文件的修改时间戳，但有的时候（1/100 的几率）会有毛病，有的时候文件夹的修改时间戳一样，但内容确实已经发生了变化，这时显式指定比较就可以解决问题，并显示区别了。</p>
</li>
<li>
<p>比较大文件。如果使用十六进制方式，比较速度会很慢，可以选择使用 CRC 方式比较，只算个校验和，贼快，比较含文件巨多的文件夹也可以用这个方式。</p>
</li>
<li>
<p>比较可执行文件。这时会显示它们版本号、作者、公司等版本信息的差异，随后你可以切换到十六进制方式，去读 Hex (-_-^)。</p>
</li>
<li>
<p>比较 MP3。在 BC 的比较方式中有一个“MP3 比较”，但我说的不是那个，BC 自定义的“MP3 比较”实际是比较两个 MP3 的 ID3 信息。两首 MP3 可能音乐数据完全相同，但是 ID3 信息不同，那么在文件系统上它们的大小可能不同，校验和也不同，这时要把它们找出来，删掉多余的。用十六进制方式比较，如果你发现两个 MP3 只有头部和尾部（ID3 位置）不同，而中间的大多数 Hex 是相同的，或有零星的几个字节不同，那么可以肯定这两首歌是相同的。</p>
</li>
</ul>
</li>
</ol>
<h4>Everything</h4>
<p>主页：<a href="http://www.voidtools.com/" target="_blank">www.voidtools.com</a></p>
<p>介绍：<a href="http://xbeta.info/everything-search-tool.htm" target="_blank">xbeta.info</a></p>
<p>和 BC 不同，这个东西才用上，但已经感觉到它的强大，也可以用使用方式是“直觉的”来赞誉它。</p>
<p>使用方法见上面的介绍链接，我还是说我的使用场景和技巧点。</p>
<p>莫要把 Everything 仅当做 Windows 自带搜索的替代工具，那样做是暴敛天物了。说下我的用法：</p>
<ol>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>配置 Everything，让它索引移动硬盘上的 kb 目录（保存各种参考资料），配置它允许多个实例运行（这点必需）。</p>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>然后拷贝一份 Everything 的可执行文件（只拷可执行文件，不要其它文件）到其它目录，然后运行并配置它，让它索引本地硬盘上的 kb 目录（从移动硬盘同步到本地的），和 kb-misc（琐碎的网页帖子、参考，没有 kb 资料那么系统），note 目录（笔记但未整理），以及 note-archive 目录（已整理的笔记），也配置它允许多个实例运行。</p>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>根据你的搜索需要，你可以拷贝出多个 Everything 的可执行文件，然后分别像上面一样配置。</p>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>最后把配好的 Everything 都在方便的位置（桌面等）建立快捷方式，并起上易懂的名字，如图：</p>
<p><img src="/res/upload/app/intro-beyond-compare-everything/et_02.png" /></p>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>现在可以双击这些快捷方式，并享受实时搜索的快感了，如果你用过 MSDN 帮助的索引，或是 Firefox 的收藏夹搜索，就知道这种方式有多么方便。</p>
<p>可以执行“本地知识库_资料_笔记”快捷方式，这时是同时搜索 kb、kb-misc、note、note-archive，4 个目录中的文件。</p>
<p>下来我执行上图中“移动硬盘知识库”那个快捷方式（对应移动硬盘上的 kb 目录），在我印象中似乎收藏过一份关于 Windows ntdll.dll 的 API 参考资料，但我记不清给它起名是用英文 "Native API" 还是用中文“本地 API”了，不用想这些，在 Everything 的搜索框中输入 "本地 | native" 你希望的结果就出来了，如图，这表示搜索包含 "本地" 或 "native" 的文件，更复杂的 Everything 还支持正则表达式匹配的文件名。在 Everything 中搜索关键字间，用空格表示“与”用 "|" 表示“或”，如果要搜索的关键字中就包含空格，用引号括起来整个关键字。</p>
<p><img src="/res/upload/app/intro-beyond-compare-everything/et_01.png" /></p>
</li>
</ol>
<p><strong>FAQ</strong></p>
<p>为什么拷贝出来一份 Everything 的可执行文件，然后配置新的这个，用一个 Everything 可执行文件不行么？对，不行，目前的 Everything 是不支持对某此搜索的位置进行保存，然后下次打开再搜这个位置的功能，或许它应该加一个“搜索会话保存”的功能来完成这个需求。不过也不要紧，通过我的新建一份 Everything，也可以完成这个功能，况且 Everything 很小只有不到 600K。</p>
<p>Everything 不支持非 NTFS 的文件系统上的文件索引。Everything 之所以可以达到如此高效的索引：和扫雷、纸牌一样，肉眼是看不出系统资源的占用，是因为 Everything 的索引引擎和 Google 桌面搜索等其它搜索工具的设计是迥异的，它并不靠读实际的目录文件来索引，而是读 NTFS 文件系统的元数据来索引，有兴趣的同学可以看看 NTFS 文件系统的设计。</p>
<p>就这么多，把琐碎的工作交给强悍的工具，解放出大脑只为创造。</p>
]]></content:encoded>
			<wfw:commentRss>http://zyxhome.org/wp/app/intro-beyond-compare-everything/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Wordpress Thread Comment 运行失败：Linux 文件权限导致 Apache 访问资源拒绝</title>
		<link>http://zyxhome.org/wp/network/web/wp/linux-file-permission-wp-thread-comment-apache-forbidden/</link>
		<comments>http://zyxhome.org/wp/network/web/wp/linux-file-permission-wp-thread-comment-apache-forbidden/#comments</comments>
		<pubDate>Fri, 07 May 2010 08:59:08 +0000</pubDate>
		<dc:creator>zy</dc:creator>
				<category><![CDATA[Linux 管理与应用]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">/wp/?=478</guid>
		<description><![CDATA[Wordpress Thread Comment 插件（作者）的功能是在 Wordpress 评论/回复中提供评论会话跟踪，通常以缩进或嵌套的形式表示一条讨论会话，论坛通常都有这个功能。
我在本地 Windows 的 WordPress 环境下配置、修改好 Wordpress Thread Comment 插件后，上传到 Linux 主机发现点击 [嵌套回复] 时，这个插件的 Javascript 脚本竟然不起作用了，但在 Windows 上工作可是正常的。

用 Firefox 的 JavaScript 控制台（Ctrl + Shift + J）跟踪，提示 movecfm() is not defined，网上说，这多是因为 WordPress 主题模板的 comments.php 中没有规范定义的 WordPress 钩子导致的，解决办法是修改 comments.php，在 &#60;/form&#62; 标签闭合前，添加一句：&#60;?php do_action(’comment_form’, $post-&#62;ID); ?&#62;。Wordpress Thread Comment 的作者也说了这一点。
我检查自己主题的 comments.php 后，发现其中已经有上面那句 do_action() 了，否则也不会在 Windows 主机上运行成功。
折腾了好久，最后发现又是 [...]]]></description>
			<content:encoded><![CDATA[<p>Wordpress Thread Comment 插件（<a href="http://blog.2i2j.com/plugins/wordpress-thread-comment" target="_blank">作者</a>）的功能是在 Wordpress 评论/回复中提供评论会话跟踪，通常以缩进或嵌套的形式表示一条讨论会话，论坛通常都有这个功能。</p>
<p>我在本地 Windows 的 WordPress 环境下配置、修改好 Wordpress Thread Comment 插件后，上传到 Linux 主机发现点击 [嵌套回复] 时，这个插件的 Javascript 脚本竟然不起作用了，但在 Windows 上工作可是正常的。</p>
<p><span id="more-478"></span></p>
<p>用 Firefox 的 JavaScript 控制台（Ctrl + Shift + J）跟踪，提示 movecfm() is not defined，网上说，这多是因为 WordPress 主题模板的 comments.php 中没有规范定义的 WordPress 钩子导致的，解决办法是修改 comments.php，在 &lt;/form&gt; 标签闭合前，添加一句：&lt;?php do_action(’comment_form’, $post-&gt;ID); ?&gt;。Wordpress Thread Comment 的作者也说了这一点。</p>
<p>我检查自己主题的 comments.php 后，发现其中已经有上面那句 do_action() 了，否则也不会在 Windows 主机上运行成功。</p>
<p>折腾了好久，最后发现又是 Linux 的文件权限造成的：</p>
<p>我将 Wordpress Thread Comment 插件上传到 Linux 主机后，解压完，插件目录 wp-thread-comment 的权限默认为 700 (rwx------)，这个权限导致 Browser &rarr; Apache &rarr; wp-thread-comment/wp-thread-comment.js.php 拒绝访问，在用 Firefox 的源码浏览器跟踪时，就报 403 Forbidden，而且手工访问 wp-thread-comment 下的其它文件时，也报 403，对于其它插件的目录测试却能正常访问。一般考虑到安全性和源码保护，除过动态页面文件（.php、.asp、.jsp、.cgi、.pl 等，在 apache 中可以配置）返回的是 Web 服务器运行后的内容外，其它文件是可以返回其内容的，况且这两者都不能是 403，除非有意设置。</p>
<p>解决办法是，将 wp-thread-comment 目录权限设为常规的 755 (rwxr-xr-x)，一切就好了。怪不得 Windows 上没有问题，这是 Apache 联合 Linux 文件权限共同作用的结果。</p>
<p>以前设置媒体资源目录时也遇到过这个问题，没想到在这里又犯了。</p>
<p>所以还是记住这个标准的 755/644 权限：</p>
<pre class="brush: bash;">
chmod -R dir-name 644
find dir-name -type d | xargs chmod 755
</pre>
<p><strong>P.S.</strong></p>
<p>贴下自己的 Wordpress Thread Comment 配置。</p>
<p>编辑评论的 HTML：</p>
<pre class="brush: xml; collapse: true; light: false; toolbar: true; wrap-lines: false;">
&lt;div class=&quot;comment-childs&lt;?php echo $deep%2 ? ' chalt' : ''; ?&gt;&quot; id=&quot;comment-[ID]&quot;&gt;
&lt;div class=&quot;header &lt;?php if($comment-&gt;comment_author_email == get_the_author_email()) {echo 'adminheader';} else { echo 'regularheader';} ?&gt;&quot;&gt;
	&lt;div class=&quot;date&quot;&gt;
		&lt;?php printf( __('%1$s at %2$s', 'blocks'), get_comment_date(__('M jS, Y', 'blocks')), get_comment_time(__('H:i', 'blocks')) ); ?&gt;
	&lt;/div&gt;
	&lt;div class=&quot;items&quot;&gt;
		&lt;?php if (!get_option('thread_comments')) : ?&gt;
			&lt;a href=&quot;javascript:void(0);&quot; onclick=&quot;MGJS_CMT.reply('commentauthor-&lt;?php comment_ID() ?&gt;', 'comment-&lt;?php comment_ID() ?&gt;', 'comment');&quot;&gt;&lt;?php _e('Reply', 'blocks'); ?&gt;&lt;/a&gt; |
		&lt;?php else : ?&gt;
			&lt;?php comment_reply_link(array('depth' =&gt; $depth, 'max_depth'=&gt; $args['max_depth'], 'reply_text' =&gt; __('Reply', 'blocks'), 'after' =&gt; ' | '));?&gt;
		&lt;?php endif; ?&gt;
		&lt;a href=&quot;javascript:void(0);&quot; onclick=&quot;MGJS_CMT.quote('commentauthor-&lt;?php comment_ID() ?&gt;', 'comment-&lt;?php comment_ID() ?&gt;', 'commentbody-&lt;?php comment_ID() ?&gt;', 'comment');&quot;&gt;&lt;?php _e('Quote', 'blocks'); ?&gt;&lt;/a&gt;
		&lt;?php edit_comment_link(__('Edit', 'blocks'), ' | ', ''); ?&gt;
	&lt;/div&gt;
	&lt;div class=&quot;fixed&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;?php
	$author_class = '';
	if (function_exists('get_avatar') &amp;&amp; get_option('show_avatars')) {
		$author_class = 'with_avatar';
	}
?&gt;
&lt;div class=&quot;author &lt;?php echo $author_class; ?&gt;&quot;&gt;
	&lt;?php echo get_avatar($comment, 32); ?&gt;
	&lt;?php if (get_comment_author_url()) : ?&gt;
		&lt;a id=&quot;commentauthor-&lt;?php comment_ID() ?&gt;&quot; href=&quot;&lt;?php comment_author_url() ?&gt;&quot; rel=&quot;external nofollow&quot; title=&quot;&lt;?php comment_author(); ?&gt;&quot;&gt;
	&lt;?php else : ?&gt;
		&lt;span id=&quot;commentauthor-&lt;?php comment_ID() ?&gt;&quot; title=&quot;&lt;?php comment_author(); ?&gt;&quot;&gt;
	&lt;?php endif; ?&gt;
		&lt;?php comment_author(); ?&gt;
	&lt;?php if (get_comment_author_url()) : ?&gt;
		&lt;/a&gt;
	&lt;?php else : ?&gt;
		&lt;/span&gt;
	&lt;?php endif; ?&gt;
&lt;/div&gt;
&lt;div class=&quot;body&quot; id=&quot;commentbody-&lt;?php comment_ID() ?&gt;&quot;&gt;
	[moderation]
	&lt;?php comment_text(); ?&gt;
&lt;/div&gt;
&lt;div class=&quot;fixed&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
</pre>
<p>编辑评论的 CSS：</p>
<pre class="brush: css; collapse: true; light: false; toolbar: true; wrap-lines: false;">
.editComment, .editableComment, .textComment{
	display: inline;
}
.comment-childs{
	border: 1px solid #999;
	margin: 10px 0px 2px 0px;
	padding: 0px 0px 2px 0px;
	background-color: white;
}
.chalt{
	/*background-color: #E2E2E2;*/
}
#newcomment{
	border:1px dashed #777;width:90%;
}
#newcommentsubmit{
	color:red;
}
.adminreplycomment{
	border:1px dashed #777;
	width:99%;
	margin:4px;
	padding:4px;
}
.mvccls{
	color: #999;
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://zyxhome.org/wp/network/web/wp/linux-file-permission-wp-thread-comment-apache-forbidden/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Windows 下 python 的源码字符集编码与 locale</title>
		<link>http://zyxhome.org/wp/py/win-python-src-cset-locale/</link>
		<comments>http://zyxhome.org/wp/py/win-python-src-cset-locale/#comments</comments>
		<pubDate>Sat, 01 May 2010 10:13:09 +0000</pubDate>
		<dc:creator>zy</dc:creator>
				<category><![CDATA[Python 编程]]></category>
		<category><![CDATA[i18n]]></category>

		<guid isPermaLink="false">/wp/?=482</guid>
		<description><![CDATA[测试 Windows 下 python 的源码字符集编码，还有 python 程序的默认 locale。
python 的源码字符编码
当 python 程序中用到 ASCII 以外的字符时，需要指定源码的字符集编码，方法参考：PEP 0263: Defining Python Source Code Encodings。
常用的几种 python 源码字符编码指示方法如下：

Python will default to ASCII as standard encoding if no other
encoding hints are given.
To define a source code encoding, a magic comment must
be placed into the source files either as first or second
line in the [...]]]></description>
			<content:encoded><![CDATA[<p>测试 Windows 下 python 的源码字符集编码，还有 python 程序的默认 locale。</p>
<h4>python 的源码字符编码</h4>
<p>当 python 程序中用到 ASCII 以外的字符时，需要指定源码的字符集编码，方法参考：<a href="http://www.python.org/dev/peps/pep-0263/" target="_blank">PEP 0263: Defining Python Source Code Encodings</a>。</p>
<p>常用的几种 python 源码字符编码指示方法如下：</p>
<blockquote><p>
Python will default to ASCII as standard encoding if no other<br />
encoding hints are given.</p>
<p>To define a source code encoding, a magic comment must<br />
be placed into the source files either as first or second<br />
line in the file, such as:</p>
<p><strong># coding=&lt;encoding name&gt;</strong></p>
<p>or (using formats recognized by popular editors)</p>
<p><strong>#!/usr/bin/python<br />
# -*- coding: &lt;encoding name&gt; -*-</strong></p>
<p>or</p>
<p><strong>#!/usr/bin/python<br />
# vim: set fileencoding=&lt;encoding name&gt; :</strong>
</p></blockquote>
<p><span id="more-482"></span></p>
<p>常用的字符集名字有：ascii、latin-1、iso-8859-1、gb2312、gbk、utf-8 等等，均是 iconv 库使用的字符集名。</p>
<h4>使用 python 源码字符编码指示例子</h4>
<p>下面 python 程序中，源码字符编码指示设定为 GBK（#coding=gbk），并且将源码内容保存为 GBK 编码。</p>
<pre class="brush: python;">
#!/bin/python
#coding=gbk

# 因为程序中使用了 unicode(&quot;中文&quot;, &quot;gbk&quot;)，所以要将源文件保存为 GBK 编码

if __name__ == '__main__':

	if u&quot;中文&quot; == unicode(&quot;中文&quot;, &quot;gbk&quot;) :
		print(&quot;equal 1.&quot;)
	else:
		print(&quot;not equal 1.&quot;)

	if &quot;中文&quot;.decode(&quot;gbk&quot;) == unicode(&quot;中文&quot;, &quot;gbk&quot;) :
		print(&quot;equal 2.&quot;)
	else:
		print(&quot;not equal 2.&quot;)

	if unicode(&quot;中文&quot;, &quot;gbk&quot;).encode(&quot;utf8&quot;) == u&quot;中文&quot;.encode(&quot;utf8&quot;) :
		print(&quot;equal 3.&quot;)
	else:
		print(&quot;not equal 3.&quot;)
</pre>
<p>正常情况下应该输出：</p>
<p>equal 1.<br />
equal 2.<br />
equal 3.</p>
<p>我在 python 2.6 (vc build) 和 cygwin 的 python 2.5 (cygwin gcc build) 下运行，均是这个结果。</p>
<h4>python 运行环境的活动 locale 影响</h4>
<p>测试程序使用 UTF-8 编码，并使用 utf-8 源码编码指示：</p>
<pre class="brush: python;">
#!/bin/python
#coding=utf-8

if __name__ == '__main__':
    print(&quot;These're hanzi: 这是汉字&quot;)
</pre>
<p>源码的 8 进制 dump 如下：</p>
<p><img src="/res/upload/py/win-python-src-cset-locale/src_dump.png" /></p>
<p>同一个上面的程序，在 cmd 和 cygwin bash 下的运行结果：</p>
<p><a href="/res/upload/py/win-python-src-cset-locale/py_lc.png" target="_blank"><img style="width: 672px;" src="/res/upload/py/win-python-src-cset-locale/py_lc.png" /></a></p>
<p>cmd 的默认 locale 是 Windows Native ANSI，cygwin bash 的默认 locale 是 UTF-8，python 进程会继承 shell 父进程的 locale 作为默认活动 locale。</p>
]]></content:encoded>
			<wfw:commentRss>http://zyxhome.org/wp/py/win-python-src-cset-locale/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>GB2312 中的常用汉字编码</title>
		<link>http://zyxhome.org/wp/i18n/gb2312-regular-hanzi-code/</link>
		<comments>http://zyxhome.org/wp/i18n/gb2312-regular-hanzi-code/#comments</comments>
		<pubDate>Thu, 29 Apr 2010 10:28:36 +0000</pubDate>
		<dc:creator>zy</dc:creator>
				<category><![CDATA[i18n]]></category>

		<guid isPermaLink="false">/wp/?p=475</guid>
		<description><![CDATA[GB2312 使用区位码定位，并编码一个汉字，后来的 GBK 和 GB18030 都是 GB2312 的超集。我这里说一下在 GB2312 中，常用汉字的编码范围，这个一般用在随机产生汉字功能，比如：产生随机汉字字符串，然后将其转换成图片格式，字体/图像变形后，作为验证码。

常用汉字编码范围：
GB2312 区位码中，AF 区之前只有符号，没有汉字，汉字从 B0 区开始，而从 D7 区开始以后的汉字是偏僻字或繁体字。
每个区中第一个位和最后一个位是空的，不编码汉字，位码不能是 A0 和 FF。
描述很晦涩，看看形式化的说明：
设某个 GB2312 常用汉字的编码：r1 r2 r3 r4
其中 r1 ~ r4 为 16 进制值 0123456789ABCDEF，下面列举 r1 ~ r4 可能的取值：

r1 =
	{B C D}

r2 =
if r1 = D
	{0 1 2 3 4 5 6}
else
	{0 1 2 3 4 5 6 7 [...]]]></description>
			<content:encoded><![CDATA[<p>GB2312 使用区位码定位，并编码一个汉字，后来的 GBK 和 GB18030 都是 GB2312 的超集。我这里说一下在 GB2312 中，常用汉字的编码范围，这个一般用在随机产生汉字功能，比如：产生随机汉字字符串，然后将其转换成图片格式，字体/图像变形后，作为验证码。</p>
<p><span id="more-475"></span></p>
<p>常用汉字编码范围：</p>
<p>GB2312 区位码中，AF 区之前只有符号，没有汉字，汉字从 B0 区开始，而从 D7 区开始以后的汉字是偏僻字或繁体字。</p>
<p>每个区中第一个位和最后一个位是空的，不编码汉字，位码不能是 A0 和 FF。</p>
<p>描述很晦涩，看看形式化的说明：</p>
<p>设某个 GB2312 常用汉字的编码：r1 r2 r3 r4</p>
<p>其中 r1 ~ r4 为 16 进制值 0123456789ABCDEF，下面列举 r1 ~ r4 可能的取值：</p>
<pre>
r1 =
	{B C D}

r2 =
if r1 = D
	{0 1 2 3 4 5 6}
else
	{0 1 2 3 4 5 6 7 8 9 A B C D E F}
</pre>
<p>r1 r2 组合起来的字节范围：B0 ~ D6，即 B0 前为符号，D7 后为偏僻字。</p>
<pre>
r3 =
	{A B C E F}

r4 =
if r3 = A
	{1 2 3 4 5 6 7 8 9 A B C D E F}
elif r3 = F
	{0 1 2 3 4 5 6 7 8 9 A B C D E}
else
	{0 1 2 3 4 5 6 7 8 9 A B C D E F}
</pre>
<p>r3 r4 组合起来的字节范围：A1 ~ FE ，即除掉 A0 和 FF。</p>
]]></content:encoded>
			<wfw:commentRss>http://zyxhome.org/wp/i18n/gb2312-regular-hanzi-code/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MySQL 产生随机字符串存储过程和函数</title>
		<link>http://zyxhome.org/wp/mysql/mysql-rand-str-proc-func/</link>
		<comments>http://zyxhome.org/wp/mysql/mysql-rand-str-proc-func/#comments</comments>
		<pubDate>Thu, 29 Apr 2010 09:58:13 +0000</pubDate>
		<dc:creator>zy</dc:creator>
				<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">/wp/?p=473</guid>
		<description><![CDATA[我的 MySQL 存储过程和函数练手代码，用于产生随机字符串。

产生随机 ASCII 字符串 函数
产生随机 ASCII 字符串函数，指定随机字符串的长度为参数 n。
所有可能的 ASCII 字符在变量 char_set 中定义。
代码如下：

DELIMITER $$

DROP FUNCTION IF EXISTS `test`.`gen_rand_ascii_str` $$
CREATE DEFINER=`root`@`localhost` FUNCTION `gen_rand_ascii_str`(n int) RETURNS char(255) CHARSET utf8
    NO SQL
BEGIN

-- author: zy
-- date: 2010-03-dd
-- description: generate random ascii string with length of parameter n, all possible characters is in variable char_set.

  declare [...]]]></description>
			<content:encoded><![CDATA[<p>我的 MySQL 存储过程和函数练手代码，用于产生随机字符串。</p>
<p><span id="more-473"></span></p>
<h4>产生随机 ASCII 字符串 函数</h4>
<p>产生随机 ASCII 字符串函数，指定随机字符串的长度为参数 n。</p>
<p>所有可能的 ASCII 字符在变量 char_set 中定义。</p>
<p>代码如下：</p>
<pre class="brush: plain;">
DELIMITER $$

DROP FUNCTION IF EXISTS `test`.`gen_rand_ascii_str` $$
CREATE DEFINER=`root`@`localhost` FUNCTION `gen_rand_ascii_str`(n int) RETURNS char(255) CHARSET utf8
    NO SQL
BEGIN

-- author: zy
-- date: 2010-03-dd
-- description: generate random ascii string with length of parameter n, all possible characters is in variable char_set.

  declare char_set char(255) default '_1234567890abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ';
  declare ret_str char(255) default '';
  declare i int default 0;
  declare len int default 0;
  declare rd int default 0;

  set len = length(char_set);

  while i &lt; n do
    set rd = floor(1 + rand() * len);
    set ret_str = concat(ret_str, substring(char_set, rd, 1));
    set i = i + 1;
  end while;

  return ret_str;

END $$

DELIMITER ;
</pre>
<h4>产生常用汉字字符保存到表中 存储过程</h4>
<p>产生所有常用汉字字符（字符类型）以及对应的编码（整数类型），保存到表 tb_hanzi_regular 中。</p>
<p>表 tb_hanzi_regular 中的列含义为：</p>
<p>hanzi_gb2312：保存汉字字符，使用 GB2312 字符集。<br />
code_gb2312：对应的 hanzi_gb2312 字符的编码整数值。<br />
hanzi_utf8：保存汉字字符，使用 UTF-8 字符集。<br />
code_utf8：对应的 hanzi_utf8 字符的编码整数值。</p>
<p>代码如下：</p>
<pre class="brush: plain;">
DELIMITER $$

DROP PROCEDURE IF EXISTS `test`.`gen_tb_hanzi_regular` $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `gen_tb_hanzi_regular`()
BEGIN

-- author: zy
-- date: 2010-03-dd
-- description: generate regular hanzi code table, containing gb2312 and utf8 charset

  -- section code boundary
  declare SEC_BEG int unsigned default 0xb0;
  declare SEC_END int unsigned default 0xd6;
  declare sec_code int unsigned default 0;

  -- position code boundary
  declare POS_BEG int unsigned default 0xa1;
  declare POS_END int unsigned default 0xfe;
  declare pos_code int unsigned default 0;

  declare code_gb2312 int unsigned default 0;
  declare hanzi_gb2312 char(4) default '';
  declare code_utf8 int unsigned default 0;
  declare hanzi_utf8 char(4) default '';

  -- test
  -- declare str_tmp char(255) default '';
  -- declare iter int unsigned default 0;

  -- performance test
  -- declare beg_tick bigint unsigned default 0;
  -- declare end_tick bigint unsigned default 0;

  -- create hanzi table if not exist
  -- drop table if exists `tb_hanzi_regular`;
  create table if not exists `tb_hanzi_regular`
  (
    `id` int(10) unsigned zerofill not null auto_increment,
    `code_gb2312` int(10) unsigned default null,
    `hanzi_gb2312` char(4) character set gb2312 default null,
    `code_utf8` int(10) unsigned default null,
    `hanzi_utf8` char(4) default null,
    primary key (`id`),
    unique key `uk_id` using btree (`id`),
    key `idx_code_gb2312` (`code_gb2312`),
    key `idx_hanzi_gb2312` (`hanzi_gb2312`),
    key `idx_code_utf8` (`code_utf8`),
    key `idx_hanzi_utf8` (`hanzi_utf8`)
  ) ENGINE=MyISAM AUTO_INCREMENT=1 default CHARSET=utf8;

  -- clear all the hanzi table
  delete from `tb_hanzi_regular`;
  alter table `tb_hanzi_regular` auto_increment = 1;

  -- set beg_tick = time_to_sec(now());

  set sec_code = SEC_BEG;
  -- section code iteration
  SEC_ITER: while sec_code &lt;= SEC_END do

    set pos_code = POS_BEG;

    -- position code iteration
    while pos_code &lt;= POS_END do
      set code_gb2312 = (sec_code &lt;&lt; 8) + pos_code;
      set hanzi_gb2312 = char(code_gb2312 using gb2312);

      set hanzi_utf8 = convert(hanzi_gb2312 using utf8);

      -- test
      -- set str_tmp = hex(hanzi_utf8);

      -- candidate 1
      -- set code_utf8 = cast(conv(hex(hanzi_utf8), 16, 10) as unsigned);

      -- candidate 2
      set code_utf8 = conv(hex(hanzi_utf8), 16, 10);

      -- candidate 3
      -- set code_utf8 = ord(hanzi_utf8);

      insert into `tb_hanzi_regular`
        (`code_gb2312`,`hanzi_gb2312`,`code_utf8`,`hanzi_utf8`)
        values (code_gb2312,hanzi_gb2312,code_utf8,hanzi_utf8);

      set pos_code = pos_code + 1;

      -- test
      -- set iter = iter + 1;
      -- if iter &gt;= 1 then
      --   leave SEC_ITER;
      -- end if;

    end while;
    -- eof position code iteration

    set sec_code = sec_code + 1;

  end while SEC_ITER;
  -- eof section code iteration

  -- set end_tick = time_to_sec(now());
  -- set @difftime = end_tick - beg_tick;

END $$

DELIMITER ;
</pre>
<h4>产生随机长度汉字字符串 函数</h4>
<p>汉字字符，和字符串的长度都是随机的，字符串的长度在参数 [min_len, max_len] 之间，要想产生固定长度的随机字符串，可以指定 min_len 等于 max_len。出现任何错误都返回空字符串。</p>
<pre class="brush: plain;">
DELIMITER $$

DROP FUNCTION IF EXISTS `test`.`gen_rand_regular_hanzi_str` $$
CREATE DEFINER=`root`@`localhost` FUNCTION `gen_rand_regular_hanzi_str`(min_len int unsigned, max_len int unsigned) RETURNS char(255) CHARSET gb2312
    NO SQL
BEGIN

-- author: zy
-- date: 2010-03-dd
-- description: generate random gb2312 hanzi string with random length from min_len to max_len

  declare rand_str_gb2312 char(255) charset gb2312 default '';
  declare rand_len int unsigned default 0;
  declare cur_len int unsigned default 0;
  -- warning: will force rand() to generate repeatable sequence
  -- declare rand_seed int unsigned default 0;

  -- section code boundary
  declare SEC_BEG int unsigned default 0xb0;
  declare SEC_END int unsigned default 0xd6;
  declare rand_sec_code int unsigned default 0;

  -- position code boundary
  declare POS_BEG int unsigned default 0xa1;
  declare POS_END int unsigned default 0xfe;
  declare rand_pos_code int unsigned default 0;

  declare rand_code_gb2312 int unsigned default 0;
  declare rand_hanzi_gb2312 char(4) default '';

  -- invalid parameters handling
  if min_len &gt; max_len or max_len = 0 or max_len &gt; 255 then
    -- set @a = 'point 1';  -- test
    return '';
  end if;

  -- set rand_seed = time_to_sec(current_time());
  set rand_len = floor(min_len+(rand()*(max_len-min_len+1)));
  -- set @a = rand_len;  -- test

  -- concat for hanzi string length of rand_len
  CONCAT_HANZI_STR: while cur_len &lt; rand_len do

    -- generate random hanzi char in gb2312
    set rand_sec_code = floor(SEC_BEG+(rand()*(SEC_END-SEC_BEG+1)));
    set rand_pos_code = floor(POS_BEG+(rand()*(POS_END-POS_BEG+1)));
    set rand_code_gb2312 = (rand_sec_code &lt;&lt; 8) + rand_pos_code;
    set rand_hanzi_gb2312 = char(rand_code_gb2312 using gb2312);

    set rand_str_gb2312 = concat(rand_str_gb2312, rand_hanzi_gb2312);

    set cur_len = cur_len + 1;
  end while CONCAT_HANZI_STR;

  return rand_str_gb2312;
END $$

DELIMITER ;
</pre>
]]></content:encoded>
			<wfw:commentRss>http://zyxhome.org/wp/mysql/mysql-rand-str-proc-func/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MySQL 实践笔记：常规操作</title>
		<link>http://zyxhome.org/wp/mysql/mysql-practice-note-common-op/</link>
		<comments>http://zyxhome.org/wp/mysql/mysql-practice-note-common-op/#comments</comments>
		<pubDate>Thu, 29 Apr 2010 09:00:08 +0000</pubDate>
		<dc:creator>zy</dc:creator>
				<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">/wp/?p=471</guid>
		<description><![CDATA[本文记录 MySQL 中常用的操作命令，也是我的《MySQL 实践笔记》系列文章中，在 MySQL 日常工作里最常用的部分。


连接 MySQL 服务
备份数据库
show 命令
列类型
MySQL 命令实例
MySQL 函数使用



连接 MySQL 服务&#160;&#160;

mysql -h [hostname] -u [user] -p[pwd] [default-db-name]

连接后得到 mysql 的提示符交互环境，终止命令执行使用 \c，例如：

mysql> SELECT
    -> USER()
    -> \c

备份数据库&#160;&#160;
使用 mysqldump 工具:

mysqldump --opt -u [user] -p[pwd] [db-name] &#62; [db-name].sql

# 备份所有的数据库模式
mysqldump --opt --all-databases &#62; db-all.sql

其它 MySQL 批处理操作参考我的相关帖：MySQL 实践笔记：批量处理
show 命令&#160;&#160;
show 命令用来查看 MySQL 的当前信息，具体分很多种：


查看存储过程或函数

show procedure [...]]]></description>
			<content:encoded><![CDATA[<p>本文记录 MySQL 中常用的操作命令，也是我的《MySQL 实践笔记》系列文章中，在 MySQL 日常工作里最常用的部分。</p>
<p><a name="post-471-menu"></a></p>
<ul class="trangle_img">
<li><a href="#连接 MySQL 服务">连接 MySQL 服务</a></li>
<li><a href="#备份数据库">备份数据库</a></li>
<li><a href="#show 命令">show 命令</a></li>
<li><a href="#列类型">列类型</a></li>
<li><a href="#MySQL 命令实例">MySQL 命令实例</a></li>
<li><a href="#MySQL 函数使用">MySQL 函数使用</a></li>
</ul>
<p><span id="more-471"></span></p>
<hr class="widen_margin_bottom" />
<h4><a name="连接 MySQL 服务"></a>连接 MySQL 服务&nbsp;&nbsp;<a href="#post-471-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></h4>
<pre class="brush: plain;">
mysql -h [hostname] -u [user] -p[pwd] [default-db-name]
</pre>
<p>连接后得到 mysql 的提示符交互环境，终止命令执行使用 \c，例如：</p>
<pre>
mysql> SELECT
    -> USER()
    -> \c
</pre>
<h4><a name="备份数据库"></a>备份数据库&nbsp;&nbsp;<a href="#post-471-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></h4>
<p>使用 mysqldump 工具:</p>
<pre class="brush: plain;">
mysqldump --opt -u [user] -p[pwd] [db-name] &gt; [db-name].sql

# 备份所有的数据库模式
mysqldump --opt --all-databases &gt; db-all.sql
</pre>
<p>其它 MySQL 批处理操作参考我的相关帖：<a href="/wp/?p=465">MySQL 实践笔记：批量处理</a></p>
<h4><a name="show 命令"></a>show 命令&nbsp;&nbsp;<a href="#post-471-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></h4>
<p>show 命令用来查看 MySQL 的当前信息，具体分很多种：</p>
<ul style="list-style-type: disc;">
<li>
<p><strong>查看存储过程或函数</strong></p>
<pre class="brush: plain;">
show procedure status;
show function status;
show create procedure [db-name].[sp-name];
show create function [db-name].[func-name];
</pre>
</li>
<li>
<p><strong>查看所有支持的字符集</strong></p>
<pre class="brush: plain;">
show character set
# 简写为
show charset
</pre>
</li>
<li>
<p><strong>查看校对规则</strong></p>
<pre class="brush: plain;">
show collation like 'latin1%';
</pre>
</li>
<li>
<p><strong>查看触发器</strong></p>
<pre class="brush: plain;">
select * from information_schema.triggers
show triggers
</pre>
</li>
<li>
<p><strong>查看用户权限</strong></p>
<pre class="brush: plain;">
show grants for [user]
</pre>
</li>
<li>
<p><strong>查看引擎支持</strong></p>
<pre class="brush: plain;">
show engines;
</pre>
</li>
<li>
<p><strong>查看系统变量</strong></p>
<pre class="brush: plain;">
show variables like 'varname'

# 或用
select @@varname;
</pre>
</li>
<li>
<p><strong>查看数据库模式和表</strong></p>
<pre class="brush: plain;">
show databases;
show create database [db-name];

show tables;
show create table [db-name].[tb-name];
</pre>
</li>
<li>
<p><strong>查看当前的数据库模式</strong></p>
<pre class="brush: plain;">
select database();
</pre>
<p>如果没选择任何数据库，结果是 NULL。</p>
</li>
</ul>
<h4><a name="列类型"></a>列类型&nbsp;&nbsp;<a href="#post-471-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></h4>
<p>具体的 列类型 使用技巧参考 MySQL 手册中 列类型一章。</p>
<ul style="list-style-type: disc;">
<li>
<p><strong>整形 id 列</strong></p>
<p>常用的整形 id 列如下定义：</p>
<pre class="brush: plain;">
create table tb_1(
	id int unsigned zerofill not NULL auto_increment unique
)
</pre>
<p>另外有 serial 这个类型别名，相当于 bigint unsigned not NULL auto_increment unique，注意 serial 是没有 zerofill 修饰的。</p>
</li>
<li>
<p><strong>NULL 值</strong></p>
<p>0 或 空字符串（''）不是 NULL，因此可以在定义为 NOT NULL 的列插入它们。</p>
<p>布尔运算中，0 或 NULL 意味着假而其它值意味着真，默认真值是 1。</p>
<p>NULL 是特殊的值，不能使用普通比较符（= 、&lt;&gt;、!=）来比较，而用 is、is not 比较：where col_name is not NULL。</p>
<p>NULL 值最小，执行 order by 时，如果运行 ASC，则 NULL 值出现在最前面，若 DESC，则 NULL 值出现在最后面。</p>
</li>
<li>
<p><strong>时间类型</strong></p>
<p>有 4 种基本时间类型：DATETIME、DATE、TIMESTAMP、TIME。</p>
<p>DATETIME 和 TIMESTAMP 包含日期和时间，DATETIME 值域：1000-01-01 00:00:00 到 9999-12-31 23:59:59；TIMESTAMP 值域：1970-01-01 00:00:00 到 2037 年。</p>
<p>DATE 类型，用字符串 'yyyy-mm-dd' 的格式表示。</p>
<p>可以使用 sql_mode=MAXDB，让 TIMESTAMP 变为 DATETIME。</p>
<p>不启用 sql_mode=MAXDB 时，TIMESTAMP 定义列的默认值为 CURRENT_TIMESTAMP（与此类似的有 CURRENT_TIME 和 CURRENT_DATE），和 now() 的值相同，并且启动自动更新（即缺省设置 ON UPDATE CURRENT_TIMESTAMP）。</p>
<p>而 DATETIME 列是没有这个特性的，只能在插入值时，指定 now()：insert into t1 values (now());</p>
</li>
</ul>
<h4><a name="MySQL 命令实例"></a>MySQL 命令实例&nbsp;&nbsp;<a href="#post-471-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></h4>
<ul style="list-style-type: disc;">
<li>
<p><strong>创建数据库</strong></p>
<p>对于 MyISAM 引擎，在数据目录（DATADIR）下 mkdir XXX，XXX 就是一个数据库，说明 create database 刚完成时只是新建一个 datadir 下的一个目录。例子：</p>
<pre class="brush: plain;">
create database if not exists SchoolStu
	default character set utf8
	default collate utf8_general_ci;
</pre>
<p>创建的数据库的特性，如 character set 和 collate 信息，放在数据库目录下的 db.opt 文件中。</p>
</li>
<li>
<p><strong>创建表</strong></p>
<pre class="brush: plain;">
drop table if exists schoolstu.student;
create table schoolstu.student (
    sno int(10) unsigned not NULL auto_increment,
    sname varchar(50) default '',
    ssex char(1) default NULL,
    sbirth date default NULL,
    dno int(10) unsigned default '0',
    primary key (sno),

    &quot; 下面这句建 uk_sno 的索引其实没必要，因为 primary 键是
    &quot; unique 键的一种
    unique key uk_sno using btree (sno),
    key idx_sname using btree (sname)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
</pre>
<p>表名、列名可用反单引号（`）括起来，以防和其它关键字冲突，e.g. 创建名称为 date 的列名（date 是 MySQL 的列类型）。</p>
</li>
<li>
<p><strong>修改表</strong></p>
<p>由新增列而引发的列值插入，其值为 NULL。例子：</p>
<pre class="brush: plain;">
alter table Student add scome date;

alter table Student mod sage smallint;

alter table Student drop unique(sname);
</pre>
</li>
<li>
<p><strong>删除表</strong></p>
<pre class="brush: plain;">
drop table Student;
</pre>
</li>
<li>
<p><strong>索引</strong></p>
<p>参加我的相关帖：<a href="/wp/?p=461">MySQL 实践笔记：索引</a></p>
<p>create index 实际上在 mysql 内部会转换成 alter table 命令。例子：</p>
<pre class="brush: plain;">
create unique index uk_sc USING BTREE on SC(sno asc, cno desc);

drop index idx_sname;
</pre>
</li>
<li>
<p><strong>全文索引</strong></p>
<pre class="brush: plain;">
create fulltext index idx_sname on Student(sname);
</pre>
<p>MySQL 默认的全文索引对中文文章是没有效果的，全文索引依靠断词符（空白字符）进行单词索引，而中文段落是连续的，MySQL 没办法得知什么是索引词汇。</p>
<blockquote><p>
from: <a href="http://forum.percona.com/s/t/7/" target="_blank">http://forum.percona.com/s/t/7/</a></p>
<p>There is a simple problem with FULLTEXT: we need to know the end of a word. With Western writing this is rarely a problem because there are spaces between words. With Asian writing this is not the case. We could use half-good solutions, like saying that all Han characters represent words, or depending on (Japanese) changes from Katakana to Hiragana which are due to grammatical endings. But the only good solution requires a dictionary, and we haven't found a good open-source dictionary.
</p></blockquote>
<p>可行的 MySQL 中文全文索引的办法是建立中文词汇字典，然后对 MySQL 全文索引模块进行改进：</p>
<p>参考：<a href="http://qwik.jp/senna/install_en.html" target="_blank">Sienna</a>，<a href="http://mecab.sourceforge.jp/" target="_blank">Mecab</a></p>
</li>
<li>
<p><strong>排序</strong></p>
<p>排序对字符类型默认是不区分大小写的，用 BINARY 强制区分大小写：order by binary col_name。</p>
<p>多列排序（可按不同方向），越先的越主，DESC 修饰词只影响它前面的列，例如：</p>
<pre class="brush: plain;">
select name, species, birth from pet order by species, birth desc;
</pre>
</li>
<li>
<p><strong>字符串匹配</strong></p>
<p>% 匹配 0 到多个任意字符，忽略大小写，不能用 =、!=，而用 like 和 not like。</p>
<p>_ 匹配任意单个字符，比如：</p>
<pre class="brush: plain;">
# 匹配 5 个字符的 name
select * from pet where name like '_____';
</pre>
<p>MySQL 支持扩展正则表达式（类似 egrep），使用 regexp/rlike 和 not regexp/not rlike 操作符。</p>
<p>类似 egrep 的行匹配策略：如果 REGEXP 模式与被测试值的任何地方匹配，模式就匹配。</p>
<p>而 like 模式匹配为：只有与整个值匹配，模式才匹配。</p>
<p>例子：</p>
<pre class="brush: plain;">
select * from pet where name regexp binary '^b';

# 用 regexp 的方法匹配 5 个字符的 name
select * from pet where name regexp '^.....$';
</pre>
</li>
<li>
<p><strong>表自身连接</strong></p>
<p>雌雄宠物配对查询：</p>
<pre class="brush: plain;">
select p1.name, p1.sex, p2.name, p2.sex, p1.species
from pet as p1, pet as p2
where p1.species = p2.species and p1.sex = 'f' and p2.sex = 'm';
</pre>
</li>
<li>
<p><strong>group by 分组</strong></p>
<p>count() 和 group by 的联用：</p>
<pre class="brush: plain;">
select owner, count(*) from pet group by owner;

# 复合分组
select species, sex, count(*) from pet group by species, sex;
</pre>
<p>每项物品的的最高价格？</p>
<pre class="brush: plain;">
select article, max(price) as price
from shop
group by article;
</pre>
</li>
<li>
<p><strong>用户变量</strong></p>
<p>找出价格最高或最低的物品：</p>
<pre class="brush: plain;">
select @min_price:=min(price),@max_price:=max(price) from shop;
select * from shop where price=@min_price or price=@max_price;
</pre>
</li>
<li>
<p><strong>外键</strong></p>
<p>只有 InnoDB 引擎支持外键的参照完整性检查，MyISAM 和 BDB 是不支持的，MyISAM 和 BDB 支持一种在列定义时，使用 references 指明参照主表关键字的语法，实际上它以及 on delete 或 on update 等修饰根本不起作用。</p>
<p>例子：</p>
<pre class="brush: plain;">
create table person (
    id smallint unsigned not NULL auto_increment,
    ...
);

create table shirt (
    ...
    owner smallint unsigned not NULL references person(id),
    ...
);
</pre>
<p>用了 foreign key + references 后，MyISAM 和 BDB 仍一样，不做实际的参照完整性检查，只起到备忘或注释作用。用 show create table 或 describe 时，也看不到定义时的 foreign key 和 references 了。</p>
<p>仅使用 references 语法不能用在 InnoDB 上。</p>
</li>
<li>
<p><strong>union 联合查询结果</strong></p>
<p>用 union 将两个单独的 select 语句的输出合成到一起：</p>
<pre class="brush: plain;">
select field1_index, field2_index
    from test_table where field1_index = '1'
union
select field1_index, field2_index
    from test_table where field2_index = '1';
</pre>
<p>相当于：</p>
<pre class="brush: plain;">
select field1_index, field2_index from test_table
where field1_index = '1' or  field2_index = '1'
</pre>
</li>
<li>
<p><strong>自增整数列 AUTO_INCREMENT</strong></p>
<p>使用 last_insert_id() MySQL 函数或 mysql_insert_id() MySQL C API 函数来查询最新的 AUTO_INCREMENT 值。</p>
<p>例子：</p>
<pre class="brush: plain;">
create table animals (
    grp enum('fish','mammal','bird') not NULL,
    id mediumint not NULL auto_increment,
    name char(30) not NULL,
    primary key (grp,id)
);

insert into animals (grp,name) values
    ('mammal','dog'),('mammal','cat'),
    ('bird','penguin'),('fish','lax'),
    ('mammal','whale'),('bird','ostrich');
</pre>
<p>自增仅为主键区别而产生，例如：上面的 insert 语句对 id 的自增影响如下：</p>
<pre>
+--------+----+---------+
| grp    | id | name    |
+--------+----+---------+
| fish   |  1 | lax     |
| mammal |  1 | dog     |
| mammal |  2 | cat     |
| mammal |  3 | whale   |
| bird   |  1 | penguin |
| bird   |  2 | ostrich |
+--------+----+---------+
</pre>
<p>改变当前 AUTO_INCREMENT 的初始值，这会影响下次的插入操作：</p>
<pre class="brush: plain;">
alter table [tb-name auto_increment = 100;
</pre>
</li>
<li>
<p><strong>插入全行默认数据</strong></p>
<p>插入全行默认数据，说明所有的列都有 default、auto_increment 修饰：</p>
<pre class="brush: plain;">
insert into student() values();
</pre>
</li>
<li>
<p><strong>将 apache 日志载入 mysql 数据库</strong></p>
<p>1. 先更改 apache 的配置文件，更改它的日志格式：</p>
<pre class="brush: plain;">
LogFormat \
        &quot;\&quot;%h\&quot;,%{%Y%m%d%H%M%S}t,%&gt;s,\&quot;%b\&quot;,\&quot;%{Content-Type}o\&quot;,  \
        \&quot;%U\&quot;,\&quot;%{Referer}i\&quot;,\&quot;%{User-Agent}i\&quot;&quot;
</pre>
<p>2. 按照 apache 日志格式，让 mysql 载入日志文件到数据库：</p>
<pre class="brush: plain;">
load data infile 'path/to/[apache-httpd-log-file]'
into table [tb-name]
fields terminated by ',' optionally enclosed by '&quot;' escaped by '\\'
</pre>
</li>
</ul>
<h4><a name="MySQL 函数使用"></a>MySQL 函数使用&nbsp;&nbsp;<a href="#post-471-menu"><img src="/res/small/arrow_up.gif" alt="返回目录" /></a></h4>
<ul style="list-style-type: disc;">
<li>
<p><strong>服务器版本</strong></p>
<p>version()</p>
</li>
<li>
<p><strong>函数与算术</strong></p>
<p>select sin(pi() / 4), (4 + 1) * 5;</p>
</li>
<li>
<p><strong>随机数</strong></p>
<p>rand() 返回一个随机浮点值 v ，范围为 0 ≤ v ≤ 1.0。</p>
<p>rand(N)，N 为整数种子值，用来产生重复序列，即如果 N 相同，第二次的 rand(N)，等于第一次的 rand(N)。</p>
<p>随机抽样 100 行：</p>
<pre class="brush: plain;">
select * from tb_hanzi_regular t order by rand() limit 100;
</pre>
<p>若要在 7 到 12 的范围（包括 7 和 12）内得到一个随机整数，使用以下语句：</p>
<pre class="brush: plain;">
select floor(7 + (rand() * 6));
</pre>
</li>
<li>
<p><strong>字符串拼接 concat()</strong></p>
<p>concat() 的结果为字符类型：concat(14.3) 返回 '14.3'。</p>
<p>连接列名字，加 0 使得结果变为一个数字类型：concat(col_1, col_2) + 0。</p>
<p>拼接中有 NULL 时，结果为 NULL：concat('my', null, 'ql') 返回 NULL。</p>
</li>
<li>
<p><strong>日期函数</strong></p>
<p>year(), month(), dayofmonth(), current_date, now()</p>
<p>月累加 用 date_add()：</p>
<pre class="brush: plain;">
where month(birth) = month(date_add(curdate(),interval 1 month));
</pre>
<p>月累加 用 mod()：</p>
<pre class="brush: plain;">
where month(birth) = mod(month(curdate()), 12) + 1;
</pre>
<p>年龄计算：</p>
<pre class="brush: plain;">
select name, birth, curdate(),
(year(curdate())-year(birth)) - (right(curdate(),5) &lt; right(birth,5))
as age
from pet;
</pre>
<p>说明：表达式 (right(curdate(),5) < right(birth,5) 返回布尔值，返回 0 或 1，用于计算 月-日（MM-DD）数的早晚。</p>
</li>
<li>
<p><strong>ASCII(str)</strong></p>
<p>返回值为字符串 str 第一个字符的数值，例如：</p>
<p>select ascii('2');<br />
select ascii(2);</p>
<p>均返回 50。</p>
</li>
<li>
<p><strong>ORD(str)</strong></p>
<p>若字符串 str 的第一个字符是一个多字节字符，则返回该字符的代码，代码通过下面公式得出：</p>
<p>(1st byte code)<br />
+ (2nd byte code × 256)<br />
+ (3rd byte code × 2562) ...</p>
<p>如果第一个字符不是一个多字节字符，那么 ORD() 和函数 ASCII() 返回相同的值。</p>
</li>
<li>
<p><strong>BIN(N)</strong></p>
<p>返回值为 N 的二进制值的字符串表示，同于 CONV(N,10,2)。</p>
<p>BIN(12) 返回 '1100'。</p>
</li>
<li>
<p><strong>BIT_LENGTH(str)</strong></p>
<p>返回值为二进制的字符串 str 长度。</p>
<p>BIT_LENGTH('text') 返回 32。</p>
</li>
<li>
<p><strong>CHAR(N,... [USING charset])</strong></p>
<p>将字符编码值（整数）转换成它对应的字符串。</p>
<p>大于 255 的 CHAR() 参数被转换为多结果字符，即 CHAR(256) 相当于 CHAR(1,0)。</p>
<p>CHAR() 的返回值为一个二进制字符串，使用 USING 语句产生指定字符集的字符串：</p>
<pre class="brush: plain;">
select charset(char(0x65)), charset(char(0x65 using utf8));
</pre>
<p>如果 USING 已经产生，而结果字符串不符合给出的字符集，则发出警告，如果使用严格模式，则 CHAR() 的结果会成为 NULL。</p>
</li>
<li>
<p><strong>CHAR_LENGTH(str)</strong></p>
<p>返回值为字符串str 的长度，长度的单位为字符。一个多字节字符算作一个单字符。</p>
</li>
<li>
<p><strong>LENGTH(str)</strong></p>
<p>返回值为字符串 str 的长度，单位为字节。例如：对于一个包含 5 个 2 字节字符的字符串，LENGTH() 的返回值为 10, 而 CHAR_LENGTH() 的返回值则为 5。</p>
</li>
<li>
<p><strong>CONV(N,from_base,to_base)</strong></p>
<p>不同数基间转换数字。返回值为数字的字符串表示，由 from_base 基转化为 to_base 基。</p>
<p>最小基数为 2 ，而最大基数为 36。如果 to_base 是一个负数，则 N 被看作一个带符号数，否则，N 被看作无符号数。</p>
<p>CONV('a',16,2) 返回 '1010'。</p>
</li>
<li>
<p><strong>HEX(N_or_S)</strong></p>
<p>如果 N_OR_S 是一个数字，则返回一个 16 进制值的字符串表示，N_or_S 被认为是 BIGINT 类型，这相当于 CONV(N,10,16)。例如：HEX(255) 返回 'FF'。</p>
<p>如果 N_OR_S 是一个字符串，则返回值为 N_OR_S 的 16 进制字符串表示，其中 N_OR_S 里的每个字符被转化为两个 16 进制数字。例如：HEX('abc') 返回 '616263'。</p>
</li>
<li>
<p><strong>CAST() 和 CONVERT()</strong></p>
<p>BINARY str 是 CAST(str AS BINARY) 的缩略形式。</p>
<p>CAST() 和 CONVERT() 转换函数有 3 个形式：</p>
<p>CAST(expr AS type), CONVERT(expr,type), CONVERT(expr USING transcoding_name)</p>
<p>type 可以是以下值：</p>
<p>BINARY[(N)]<br />
CHAR[(N)]<br />
DATE<br />
DATETIME<br />
DECIMAL<br />
SIGNED [INTEGER]<br />
TIME<br />
UNSIGNED [INTEGER]</p>
<p>带有 USING 的 CONVERT() 被用来在不同的字符集之间转化数据，一般用于比较出现在不同字符集中的字符串。。</p>
</li>
<li>
<p><strong>CASE 语句</strong></p>
<p>参考我的一段判断学生成绩级别的 SQL 代码，成绩级别分为 nopass、pass、good、excellent：</p>
<pre class="brush: plain;">
select id,grade,
case
when grade &lt; 60
	then 'nopass'
else case
	when grade &lt; 80
		then 'pass'
	else case
		when grade &lt; 90
			then 'good'
		else 'excellent'
		end
	end
end
as grade_level
from student_grade;
</pre>
</li>
<li>
<p><strong>MAX(col)</strong></p>
<p>查找某个列的最大值的行：</p>
<pre class="brush: plain;">
select *
from shop
where price=(select max(price) from shop);
</pre>
<p>如果不要求多个最值行，可以用排序，然后用 limit 取第一行：</p>
<pre class="brush: plain;">
select *
from shop
order by price desc
limit 1;
</pre>
<p>很普通的一个查询任务，通常用来作为面试题 (-_-^)，优化的关键在于给那个列编录索引。</p>
</li>
</ul>
<p>最后贴一张 Dave Child 的 <a href="http://www.addedbytes.com/cheat-sheets/mysql-cheat-sheet/" target="_blank">MySQL 快速参考卡片</a>：<a href="/res/upload/db/mysql/mysql-practice-note-common-op/mysql-cheat-sheet.png" target="_blank">MySQL Cheat Sheet</a></p>
]]></content:encoded>
			<wfw:commentRss>http://zyxhome.org/wp/mysql/mysql-practice-note-common-op/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MySQL 实践笔记：批量处理</title>
		<link>http://zyxhome.org/wp/mysql/mysql-practice-note-batch-process/</link>
		<comments>http://zyxhome.org/wp/mysql/mysql-practice-note-batch-process/#comments</comments>
		<pubDate>Sun, 25 Apr 2010 08:59:00 +0000</pubDate>
		<dc:creator>zy</dc:creator>
				<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">/wp/?p=465</guid>
		<description><![CDATA[记录批量录入/导出 MySQL 表数据的方法。
LOAD DATA INFILE
当大量的表数据需要录入时，如果觉得 insert into 命令繁琐，可以用 load data infile 命令。
load data infile 从一个指定的文件中读取行数据，然后插入表中。这个文件为一个文本文件，用指定的域分隔符隔开列，要求文件中的列顺序和插入表的列创建顺序相同。通常把这种文本文件称为：CSV 文件，意思为 Comma Separated Values，当然不一定是由逗号（,）作为域分隔符。


fields terminated by ','：指定字段分隔符，默认为 tab。


lines terminated by '\r\n'：指定行分隔符，默认为 \n (LF)。


optionally enclosed by '"'：指定包围字符，这样指定列值时，可以包含字段分隔符。


escaped by '\\'：指定转义字符，默认为后斜线（\），比如 NULL 值可用 \N 表示。



例子
表 student 定义如下：

create table tb_student (
    sno int unsigned not null auto_increment,
    sname [...]]]></description>
			<content:encoded><![CDATA[<p>记录批量录入/导出 MySQL 表数据的方法。</p>
<h4>LOAD DATA INFILE</h4>
<p>当大量的表数据需要录入时，如果觉得 insert into 命令繁琐，可以用 load data infile 命令。</p>
<p>load data infile 从一个指定的文件中读取行数据，然后插入表中。这个文件为一个文本文件，用指定的域分隔符隔开列，要求文件中的列顺序和插入表的列创建顺序相同。通常把这种文本文件称为：CSV 文件，意思为 Comma Separated Values，当然不一定是由逗号（,）作为域分隔符。</p>
<ul style="list-style-type: disc;">
<li>
<p><strong>fields terminated by ','</strong>：指定字段分隔符，默认为 tab。</p>
</li>
<li>
<p><strong>lines terminated by '\r\n'</strong>：指定行分隔符，默认为 \n (LF)。</p>
</li>
<li>
<p><strong>optionally enclosed by '"'</strong>：指定包围字符，这样指定列值时，可以包含字段分隔符。</p>
</li>
<li>
<p><strong>escaped by '\\'</strong>：指定转义字符，默认为后斜线（\），比如 NULL 值可用 \N 表示。</p>
</li>
</ul>
<p><span id="more-465"></span></p>
<p><strong>例子</strong></p>
<p>表 student 定义如下：</p>
<pre class="brush: plain;">
create table tb_student (
    sno int unsigned not null auto_increment,
    sname varchar(50) default '',
    ssex char(1) default null,
    sbirth date default null,
    dno int unsigned default '0',
    primary key (sno),
) default charset=utf8;
</pre>
<p>创建表数据文本文件 tb-student.txt，使用 UTF-8 编码，换行符设置为 LF (\n)，内容如下：</p>
<pre>
1,张三,F,1992-10-09,1
2,李四,M,1993-12-11,1
3,王五,F,1994-04-05,\N
4,"立,什",M,1990-02-07,2
</pre>
<p>上面第 4 条记录也可以用 \ 转义输入：</p>
<pre>
4,立\,什,M,1990-02-07,2
</pre>
<p>使用下面 load data infile 语句：</p>
<pre class="brush: plain;">
load data local infile 'path/to/tb-student.txt'
into table tb_student
lines terminated by '\n'
fields terminated by ','
optionally enclosed by '&quot;'
escaped by '\\';
</pre>
<p>插入的 tb_student 表结果为：</p>
<pre>
+-----+---------+------+------------+------+
| sno | sname   | ssex | sbirth     | dno  |
+-----+---------+------+------------+------+
|   1 | 张三    | F    | 1992-10-09 |    1 |
|   2 | 李四    | M    | 1993-12-11 |    1 |
|   3 | 王五    | F    | 1994-04-05 | NULL |
|   4 | 立,什   | M    | 1990-02-07 |    2 |
+-----+---------+------+------------+------+
</pre>
<h4>SELECT INTO OUTFILE</h4>
<p>select into outfile 是 load data infile 的相反命令，用于将查询结果写入文本文件中，这个文件可以作为后续 load data infile 的录入文件。</p>
<p>例如，将表 tb_student 中的数据写入文件 tb-student.txt：</p>
<pre class="brush: plain;">
select * into outfile 'tb-student.txt' fields terminated by ',' from tb_student;
</pre>
<h4>mysql 命令行指定 SQL 语句</h4>
<p>mysql 的选项 <strong>--execute</strong> (<strong>-e</strong>)，可以指定 MySQL 语句，用于非交互的 mysql 工作方式，例如：</p>
<pre class="brush: plain;">
mysql -t -vvv -u [user] -p[pwd] --default-character-set=utf8 --execute=&quot;use [db-name]; select * from [tb-name];&quot;
</pre>
<p>短格式 -e 后不加等号（=）。</p>
<p><strong>-t</strong> 选项：使查询结果用表格显示。</p>
<p><strong>-vvv</strong> 选项：显示查询语句、运行时间、影响行数等信息。</p>
<h4>mysql 执行 SQL 脚本</h4>
<p>有两种方式让 mysql 执行 SQL 脚本：</p>
<p><strong>方法 1</strong></p>
<p>用 --execute (-e) 执行 source 语句，source 语句指定 SQL 脚本：</p>
<pre class="brush: plain;">
mysql -e &quot;source [sql-script-file]&quot;
</pre>
<p><strong>方法 2</strong></p>
<p>mysql 将标准输入流的数据作为 SQL 语句解析，所以下面命令都可以执行脚本中的数据：</p>
<pre class="brush: plain;">
echo &quot;select * from [tb-name];&quot; | mysql -u [user] -p[pwd] [db-name]

cat [sql-script-file] | mysql -u [user] -p[pwd] [db-name]

mysql -u [user] -p[pwd] [db-name] &lt; [sql-script-file]
</pre>
]]></content:encoded>
			<wfw:commentRss>http://zyxhome.org/wp/mysql/mysql-practice-note-batch-process/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MySQL 实践笔记：字符集</title>
		<link>http://zyxhome.org/wp/mysql/mysql-practice-note-charset/</link>
		<comments>http://zyxhome.org/wp/mysql/mysql-practice-note-charset/#comments</comments>
		<pubDate>Sun, 25 Apr 2010 06:38:48 +0000</pubDate>
		<dc:creator>zy</dc:creator>
				<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">/wp/?p=463</guid>
		<description><![CDATA[MySQL 的字符集设置包括两个方面：Character Set（字符集）和 Collation（排序规则，有时翻译成“校对规则”）。
参考
MySQL 5.1 Manual - Chapter 9. Internationalization and Localization

查看 MySQL 字符集设置
在 mysqld 中用一些内置变量表示 MySQL 服务和连接状态使用的字符集设置，包括：
character_set_xxx 形式的字符集设置变量：

&#124; character_set_client     &#124; [charset-name]
&#124; character_set_connection &#124; [charset-name]
&#124; character_set_database   &#124; [charset-name]
&#124; character_set_filesystem &#124; [charset-name]
&#124; character_set_results    &#124; [charset-name]
&#124; character_set_server     &#124; [charset-name]
&#124; character_set_system     [...]]]></description>
			<content:encoded><![CDATA[<p>MySQL 的字符集设置包括两个方面：Character Set（字符集）和 Collation（排序规则，有时翻译成“校对规则”）。</p>
<h4>参考</h4>
<p>MySQL 5.1 Manual - <a href="http://dev.mysql.com/doc/refman/5.1/en/internationalization-localization.html">Chapter 9. Internationalization and Localization</a></p>
<p><span id="more-463"></span></p>
<h4>查看 MySQL 字符集设置</h4>
<p>在 mysqld 中用一些内置变量表示 MySQL 服务和连接状态使用的字符集设置，包括：</p>
<p>character_set_xxx 形式的字符集设置变量：</p>
<pre>
| character_set_client     | [charset-name]
| character_set_connection | [charset-name]
| character_set_database   | [charset-name]
| character_set_filesystem | [charset-name]
| character_set_results    | [charset-name]
| character_set_server     | [charset-name]
| character_set_system     | [charset-name]
| character_sets_dir       | [charset-file-path]
</pre>
<p>cllation_xxx 形式的校对规则变量：</p>
<pre>
| collation_connection | [collate-name]
| collation_database   | [collate-name]
| collation_server     | [collate-name]
</pre>
<p>校对规则名约定：以相关的字符集名开始，包括一个语言名，以 <strong>_ci</strong>（大小写不敏感）、<strong>_cs</strong>（大小写敏感）或 <strong>_bin</strong>（二元）结束，例如：latin1_swedish_ci。</p>
<p>因为 character_set_xxx、cllation_xxx 是标准的内置变量，所以可以用：</p>
<p><strong>select @@character_set_xxx;</strong></p>
<p><strong>set character_set_xxx=[charset-name];</strong></p>
<p>来查看或设置。</p>
<p>进入 MySQL 环境后，运行以下命令：</p>
<pre class="brush: bash;">
# 查看 MySQL 支持的所有字符集
show character set;

# 查看 MySQL 支持的所有校对规则
show collation;

# 查看所有以 latin1 为前缀的校对规则
show collation like 'latin1%';

# 查看当前设定的所有活动字符集变量
show variables like 'character%';

# 查看当前设定的所有活动校对规则变量
show variables like 'collation%';
</pre>
<h4>MySQL 字符集设置级别</h4>
<p>MySQL 对字符集的支持包括这几个级别：服务器（server）、连接（connection）、数据库模式（database）、表（table）、列（column）。</p>
<p>当某一级别没有设定字符集时，就继承上一级别的设置，继承顺序如下：</p>
<ol>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>编译 MySQL 时，指定了一个默认的字符集，通常是 latin1。</p>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>安装 MySQL 时，可在配置文件中设定一个默认字符集（default-character-set 变量），如果没指定，这个值继承自编译时指定的。在 MySQL 配置文件中设定默认字符集：</p>
<p>在 [client]、[mysqld]、[mysql] 段落中分别加入：<strong>default-character-set=[charset-name]</strong>。</p>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>启动 mysqld 时，可在命令行（--character-set-server 选项）中指定一个默认字符集，如果没指定，这个值继承配置文件中的设置，启动后用 character_set_server 变量表示这个字符集。</p>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>启动 mysql 命令行客户端，连接 mysqld 时，可在命令行（--default-character-set 选项）中指定一个默认字符集，作为本次连接会话的字符集设置，连接后用 character_set_client、character_set_connection、character_set_results 三个变量表示。</p>
<p>例如，有时配置文件中的 default-character-set 设定不起作用，即不影响 character_set_client、character_set_connection、character_set_results 三个变量，则可以用命令行参数 --default-character-set 启动 mysql：</p>
<p><strong>mysql -u [user] --default-character-set=[charset-name] -p</strong></p>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>当创建一个新数据库模式时，如果没有指定字符集，这个数据库的字符集继承 character_set_server，并用 character_set_database 变量表示。</p>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>在数据库模式中创建一个表时，可以指定表级别的默认字符集，如果没有指定则继承 character_set_database，例如：</p>
<pre class="brush: plain;">
create table if not exists tb_hanzi_regular
(
    id int unsigned zerofill not null auto_increment,
    code_utf8 int unsigned default null,
    hanzi_utf8 char(4) default null,
) default CHARSET=utf8;
</pre>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>对于表中的某一列（字符类型列），可以指定列级别的字符集，如果没有指定则继承表的默认字符集，例如：</p>
<pre class="brush: plain;">
create table if not exists tb_hanzi_regular
(
    id int unsigned zerofill not null auto_increment,
    code_gb2312 int unsigned default null,
    hanzi_gb2312 char(4) character set gb2312 default null,
) default CHARSET=utf8;
</pre>
</li>
</ol>
<h4>字符集设置的一致性</h4>
<p>要想用 MySQL 客户端正确显示数据库表中的字符类型数据，需满足两个条件：</p>
<ol>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>MySQL 客户端与 mysqld 的字符集设置一致。只要三个字符集设定变量一致即可：<strong>character_set_client</strong>、<strong>character_set_connection</strong>、<strong>character_set_database</strong>。</p>
</li>
<li class="ol_sty_1" style="margin-left: 0.5em;">
<p>运行环境支持某种字符集编码的显示。</p>
<p>运行环境要视使用的 MySQL 客户端而定。如果使用 mysql 命令行程序，则它的运行环境就涉及终端（或控制台）程序对字符集的支持。</p>
<p>下面的例子假设已经达成条件 1（字符集设置一致）。</p>
<p><strong>例子 1</strong></p>
<p>Windows 实现的控制台设备基础设施（一套 console 库，mysql 链接到其上，依靠其在“黑屏幕”上显示字符），默认使用系统的 Native ANSI 字符集作为活动字符集，所以简体中文版的 Windows 上使用 Windows 编译版的 mysql 时，默认是不能在控制台中正确显示 UTF-8 字符集的数据库表列的，会显示乱码/错误字符，原因是控制台将 mysql 输出的 UTF-8 字符编码以 GBK（简体中文 Windows 的系统默认字符集）编码进行解码去查找字体造成的。通常一个 BOM 中的汉字以 UTF-8 编码为 3 个字节，而以 GBK 编码为 2 个字节，所以当 mysql 查询到 2 个 UTF-8 编码的汉字返回时，常会在控制台上显示 3 个错误的中文字符，例如：数据库中 UTF-8 编码的“张三”，会显示成 GBK 编码的“寮犱笁”。</p>
<p>解决办法：</p>
<ul style="list-style-type: disc;">
<li>
<p>由于只是控制台的解码误判，所以可以将 mysql 的输出重定向到文件（绕过解码这个步骤），然后用支持 UTF-8 的编辑器打开文件即可看到正确的字符。</p>
</li>
<li>
<p>可以将 mysql 的输出通过管道传递给 iconv，用 iconv 转换成 GBK 编码，最后再输出到 stdout。</p>
</li>
</ul>
<p><strong>例子 2</strong></p>
<p>在 Cygwin 实现的终端（bash）下运行 Windows 编译版的 mysql。和 例子 1 中相似，在数据库表中用 UTF-8 编码字符数据。</p>
<p>正确地在 Cygwin bash 下显示 UTF-8 查询字符，可以使用下面命令：</p>
<pre class="brush: bash;">
echo &quot;select * from [tb-name];&quot; | mysql -u [user] --default-character-set=utf8 -p[pwd] [db-name] | cat

# 或

mysql -u [user] --default-character-set=utf8 -p[pwd] --execute=&quot;select * from [tb-name];&quot; [db-name] | cat
</pre>
<p>加最后一个 | cat 是因为：只有基于 cywin1.dll 的程序才能把 UTF-8 的 stdout 显示到 Cygwin 的终端上（因为 cygwin1.dll 在实现运行时库是有默认的字符集转换（根据 LANG 环境变量）），而这里的 Windows 编译版的 mysql 是 native 的 Windows 程序，没法直接在 Cygwin 的终端上显示正确字符的，所以通过一个 Cygwin 的程序 cat 转一下，当然 more（是 Cygwin 的 more，而非 Windows 的 more）也可以。</p>
<p>当然如果使用 Cygwin 编译版的 mysql，就不需要这个了。</p>
</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://zyxhome.org/wp/mysql/mysql-practice-note-charset/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MySQL 实践笔记：索引</title>
		<link>http://zyxhome.org/wp/mysql/mysql-practice-note-index/</link>
		<comments>http://zyxhome.org/wp/mysql/mysql-practice-note-index/#comments</comments>
		<pubDate>Thu, 22 Apr 2010 10:04:12 +0000</pubDate>
		<dc:creator>zy</dc:creator>
				<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">/wp/?p=461</guid>
		<description><![CDATA[记下 MySQL 的表索引用法。
当使用 create table 创建表时，也能在表中创建所有的索引。当表创建完成后，用 create index 向已有的表中添加索引，实际上这个操作会转换成 alter table 操作去修改表的索引，并且导出表定义时，所有的索引定义都包含在 create table 创建表语句中。

建立索引的引擎约束
在 MySQL 5.1 中：
只有 MyISAM、InnoDB、BDB 表类型时，可以向有 NULL 值的列中添加索引，并且可以向 BLOB 或 TEXT 列中添加索引。
前缀索引
对于 CHAR 和 VARCHAR 列，只用一列的一部分就可创建索引。创建索引时，使用 col_name(len) 语法，对前缀编制索引，前缀包括每列值的前 len 个字符。BLOB 和 TEXT 列也可以编制索引，但是必须给出前缀长度。
例如建立前 10 个字符的索引：

CREATE INDEX part_of_name ON customer (name(10));

前缀最长为 255 字节。对于 MyISAM 和 InnoDB 表，前缀最长为 1000 字节。注意前缀的限长以字节计，而 CREATE INDEX 语句中的前缀长度指的是字符的数目。对于使用多字节字符集的列，在指定列的前缀长度时，要自行计算。
存储引擎允许的索引类型
MyISAM：允许 [...]]]></description>
			<content:encoded><![CDATA[<p>记下 MySQL 的表索引用法。</p>
<p>当使用 create table 创建表时，也能在表中创建所有的索引。当表创建完成后，用 create index 向已有的表中添加索引，实际上这个操作会转换成 alter table 操作去修改表的索引，并且导出表定义时，所有的索引定义都包含在 create table 创建表语句中。</p>
<p><span id="more-461"></span></p>
<h4>建立索引的引擎约束</h4>
<p>在 MySQL 5.1 中：</p>
<p>只有 MyISAM、InnoDB、BDB 表类型时，可以向有 NULL 值的列中添加索引，并且可以向 BLOB 或 TEXT 列中添加索引。</p>
<h4>前缀索引</h4>
<p>对于 CHAR 和 VARCHAR 列，只用一列的一部分就可创建索引。创建索引时，使用 col_name(len) 语法，对前缀编制索引，前缀包括每列值的前 len 个字符。BLOB 和 TEXT 列也可以编制索引，但是必须给出前缀长度。</p>
<p>例如建立前 10 个字符的索引：</p>
<pre class="brush: plain;">
CREATE INDEX part_of_name ON customer (name(10));
</pre>
<p>前缀最长为 255 字节。对于 MyISAM 和 InnoDB 表，前缀最长为 1000 字节。注意前缀的限长以字节计，而 CREATE INDEX 语句中的前缀长度指的是字符的数目。对于使用多字节字符集的列，在指定列的前缀长度时，要自行计算。</p>
<h4>存储引擎允许的索引类型</h4>
<p>MyISAM：允许 BTREE。</p>
<p>InnoDB：允许 BTREE。</p>
<p>MEMORY/HEAP：允许 HASH、BTREE。</p>
<p>例如：</p>
<pre class="brush: plain;">
CREATE TABLE lookup (id INT) ENGINE = MEMORY;
CREATE INDEX id_index USING HASH ON lookup (id);
</pre>
<h4>全文索引</h4>
<p>FULLTEXT 索引只对 CHAR、VARCHAR 和 TEXT 列编制索引，并且只能在 MyISAM 表中编制。</p>
<p>全文搜索同 match() 函数使用示例：</p>
<pre class="brush: plain;">
# 创建表
CREATE TABLE articles (
    id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
    title VARCHAR(200),
    body TEXT,
    FULLTEXT (title,body)
    );

# 插入表数据
INSERT INTO articles (title,body) VALUES
    ('MySQL Tutorial','DBMS stands for DataBase ...'),
    ('How To Use MySQL Well','After you went through a ...'),
    ('Optimizing MySQL','In this tutorial we will show ...'),
    ('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),
    ('MySQL vs. YourSQL','In the following database comparison ...'),
    ('MySQL Security','When configured properly, MySQL ...');

# 用 match() 做全文（body 列）检索查询
SELECT * FROM articles
    WHERE MATCH (title,body) AGAINST ('database');
</pre>
<p>查询结果：</p>
<pre>
+----+-------------------+------------------------------------------+
| id | title             | body                                     |
+----+-------------------+------------------------------------------+
|  5 | MySQL vs. YourSQL | In the following database comparison ... |
|  1 | MySQL Tutorial    | DBMS stands for DataBase ...             |
+----+-------------------+------------------------------------------+
</pre>
<h4>键 KEY</h4>
<p>键 KEY 通常是 INDEX 同义词。如果关键字属性 PRIMARY KEY 在列定义中已给定，则 PRIMARY KEY 也可以只指定为 KEY。</p>
<p>在 UNIQUE 索引中，所有的值必须互不相同。如果您在添加新行时使用的关键字与原有行的关键字相同，则会出现错误。例外情况是，如果索引中的一个列允许包含 NULL 值，则此列可以包含多个 NULL 值。此例外情况不适用于 BDB 表。在 BDB 中，带索引的列只允许一个单一 NULL。</p>
<p>PRIMARY KEY 是一个 UNIQUE KEY，此时，所有的关键字列必须定义为 NOT NULL。如果这些列没有被明确地定义为 NOT NULL，MySQL 隐含地定义这些列。一个表只有一个 PRIMARY KEY。如果您没有 PRIMARY KEY 并且一个应用程序要求在表中使用 PRIMARY KEY，则 MySQL 返回第一个 UNIQUE 索引，此索引没有作为 PRIMARY KEY 的 NULL 列。</p>
<p>在已创建的表中，PRIMARY KEY 的位置最靠前，然后是所有的 UNIQUE 索引，然后是非唯一索引。</p>
<p>PRIMARY KEY 可以是一个多列索引。但是，在列规约中使用 PRIMARY KEY 关键字属性无法创建多列索引。这么做只能把一个列标记为主列。必须使用一个单独的 PRIMARY KEY(col_1, col_2, ...) 子句。</p>
<p>PRIMARY KEY 的名称为 PRIMARY。对于其它索引，如果您没有赋予名称，则索引被赋予的名称与第一个已编入索引的列的名称相同，并自选添加后缀（_2, _3,...），使名称为唯一名称。您可以使用 SHOW INDEX FROM tbl_name 来查看表的索引名称。</p>
]]></content:encoded>
			<wfw:commentRss>http://zyxhome.org/wp/mysql/mysql-practice-note-index/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
