最新IBM水货笔记本价格,详细点击进入

查看完整版本: MySQL数据库技术(36)

风舞残阳 2008-4-2 17:54

MySQL数据库技术(36)

[color=#0000ff]6.8其他主题
[/color]
本节包括几个主题,这些主题不完全适合于本章从client1到client5的开发中的任一小节的内容:
■在使用结果集元数据帮助验证这些数据适合于计算之后,使用结果集数据计算结果。
■如何处理很难插入到查询中的数据。
■如何处理图形数据。
■如何获得表[wiki]结构[/wiki]的[wiki]信息[/wiki]。
■常见的My[wiki]SQL[/wiki][wiki]程序[/wiki]设计错误及如何避免。

[color=#0000ff]6.8.1在结果集上执行计算
[/color]
迄今为止,我们集中而主要地使用了结果集元数据来打印行数据,但很明显,除打印之外,还有需要使用数据做其他事情的时候。例如,计算基于数据值的统计信息,应用元数据确保数据适合它们要满足的[wiki]需求[/wiki]。哪种[wiki]类[/wiki]型的需求?对于启动程序来说,可能要校验一下正
要执行数字计算的列实际上是否包含着数字!
下面的列表显示了一个简单函数summary_stats(),它获取结果集和列索引,并产生列值的汇总统计。该函数还列出缺少数值的数量,它是通过检查NULL来检测的。这些计算包括两个数据所必须满足的需求,summary_stats()用结果集元数据来校验:
■指定的列必须存在(也就是说,列索引必须在结果集列值的范围内)。
■此列必须包括数字值。
如果这些条件不满足,则summary_stats()只打印出错误消息并返回。代码如下:


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202740511.JPG[/img]


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202740171.JPG[/img]


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202740273.JPG[/img]

请注意在mysql_fetch_row()循环前面调用的mysql_data_seek()。为获得同样的结果集,它允许多次调用summary_stats()(假设要计算几列的统计值的话)。每次调用summary_stats()都要“重新回到”到结果集的开始(这里假设mysql_store_result()创建结果集,如果用mysql_use_result()创建结果集就只能按顺序处理行,而且只能处理一次)。summary_stats()是个相对简单的函数,但它给我们一个提示,就是如何编写一个比较复杂的计算程序,如两个列的最小二乘回归或者[wiki]标准[/wiki]统计,如t-检验。

[color=#0000ff]6.8.2对查询中有疑问的数据进行编码
[/color]
包括引号、空值和反斜线的数据值,如果把它们插入到查询中,在执行查询时就会产生一些问题。下面的讨论论述了这些难点,并介绍了解决的办法。假设要建造一个SELECT查询,它基于由name指向的空终结串的内容:


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202741397.JPG[/img]

 如果name的值类似于“0’Malley,Brian”,这时进行的查询就是非法的,因为引号在引用的字符串里出现:


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202741125.JPG[/img]

需要特别注意这个引号,以便使[wiki]服务器[/wiki]不将它解释为name的结尾。一种方法是在字符串内使用双引号,这就是ANSISQL约定。SQL支持这个约定,也允许引号在反斜线后使用:


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202741702.JPG[/img]

另一个有问题之处是查询中任意二进制数据的使用,例如,在把图形存储到[wiki]数据库[/wiki]这样的应用程序中会发生这种情况。因为二进制数值含有一些字符,把它放到查询中是不[wiki]安全[/wiki]的。为了解决这个问题,可使用mysql_escape_string(),它可以对特殊字符进行编码,使其在引用的字符串中可以使用。mysql_escape_string()认为的特殊字符是指空字符、单引号、双引号、反斜线、换行符、回车符和Control-Z(最后一个在Windows语言[wiki]环境[/wiki]中出现)。什么时候使用mysql_escape_string()呢?最保险的回答是“始终”。然而,如果确信数据的形式并且知道它是正确的—可能因为预先执行了确认检查—就不必编码了。例如,如果处理[wiki]电话[/wiki]号码的字符串,它完全由数字和短线组成,那么就不必调用mysql_escape_string()了,否则还是要调用。
mysql_escape_string()对有问题的字符进行编码是将它们转换为以反斜线开头的2个字符的序列。例如,空字符转换为‘\0’,这里的0是可打印的[wiki]ASCII码[/wiki]0,而不是空。反斜线、单引号和双引号分别转换为‘\\’、‘\’’和‘\”’。调用mysql_escape_string()的过程如下:


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202741824.JPG[/img]

mysql_escape_string()对from_str进行编码,并把结果写入to_str中,还添加了空终结值,这样很方便,因为可以利用像strcpy()和strlen()这样的函数使用该结果串。from_str指向包括将要编码的字符串的char缓冲区,这个字符串可能包含任何内容,其中包括二进制数据。to_str指向一个存在的char缓冲区,在这个缓冲区里,可以写入编码的字符串;不要传递未初始化的指针或NULL指针,希望由mysql_escape_string()分配[wiki]空间[/wiki]。由to_str指向的缓冲区的长度至少是(from_len*2)+1个字节(很可能from_str中的每个字符都需要用2个字符来编码;额外的字节是空终结值)。
from_len和to_len都是unsignedint值,from_len表示from_str中数据的长度;提供这个长度是非常必要的,因为from_str可能包含空值字节,不能把它当作空终结串。从mysql_escape_string()返回的to_len值是作为结果的编码字符串的实际长度,没有对空终结值进行计数。
当mysql_escape_string()返回时,to_str中编码的结果就可看作是空终结串,因为from_str中的空值都被编码为‘\0’。
为了重新编写构造SELECT的代码,使名称的值即使包含引号也能工作,我们进行下面的操作:


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202741892.JPG[/img]

  
[color=#0000ff]6.8.3图像数据的处理
[/color]
  mysql_escape_string()的基本功能之一就是把图像数据加载到一个表中。本节介绍如何进行这项工作(这个讨论也适用于二进制数据的其他形式)。假设想从文件中读取图像,并将它们连同唯一的标识符存储到表中。BLOB类型对二进制数据来讲是个很好的选择,因此可以使用下面的表说明:


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202741745.JPG[/img]

实际上,要想从文件中获取图像并放入images表,利用下面的函数load_image()可以实现,给出一个标识符号码和一个指向包括这个图像数据的打开文件的指针:


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202741890.JPG[/img]

load_image()不会分配非常大的查询缓冲区(100K),因此它只能处理相对较小的图形。
在实际的应用程序中,可以根据图形文件的大小动态地分配缓冲区。处理从数据库中恢复的图形数据(或任何二进制数据)并不像开始把它放入时那样问题重重,因为在变量MYSQL_ROW中数据值的原始形式是有效的,通过调用mysql_fetch_length(),这个长度也是有效的。必须将值看作是计数串,而不是空终结串。

[color=#0000ff]6.8.4获取表信息
[/color]
MySQL允许使用下面的查询获取有关表结构的信息(下面两者是等价的):


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202741455.JPG[/img]

与SELECT相类似,两个语句都返回结果集。为了在表中找出有关列,所需做的就是处理结果集中的行,从中获取有用的信息。例如,如果从mysql客户机上发布DESCRIBEimages语句,就会返回这样的信息:


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202741841.JPG[/img]

 如果从自己的客户机上执行同样的查询,可以得到相同的信息(没有边框)。如果只想要单个列的信息,则使用如下这个查询:
 SHOWFIELDSFROMtbl_nameLIKE“col_name”
 此查询会返回相同的列,但只是一行(如果列不存在就不返回行)。
 
[color=#0000ff]6.8.5需要避免的客户机程序设计错误
[/color]
 本节讨论一些常见的MySQLCAPI程序设计错误,以及如何避免其发生(这些问题在MySQL邮件清单中会[wiki]周期[/wiki]性地突然出现)。
 1.错误1——使用未初始化的连接处理程序指针在本章的样例中,我们已经通过传递NULL参数调用了mysql_init(),这就是让它分配并且初始化MYSQL结构,然后返回一个指针。另外一种方法是将指针传递到一个已有的MYSQL结构中。在这种情况下,mysql_init()会将结构初始化并返回一个指针,而不必自己分配结构。如果要使用第二种方法,则要小心会出现一些微妙的问题。下面的讨论指出了需要注意的一些问题。如果将一个指针传递给mysql_init(),它应该实际指向某些东西。看下面的代码段:


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202741694.JPG[/img]

这个问题是,mysql_init()得到了一个指针,但指针没有指向所知的任何地方。conn是一个局部变量,因此在main()开始执行时它是一个能指向任何地方的未初始化的存储器,这就是说mysql_init()将使用指针,并可在内存的一些任意区域滥写。如果幸运的话,conn将指向您的程序地址空间的外部,这样,[wiki]系统[/wiki]将立即终止,使您能尽早意识到代码中出现的问题。
如果不幸的话,conn将指向程序中以后才使用的一些数据的内部,直到再次使用那个数据时才发现问题。因此实际出现问题的地方远比执行程序时出现的问题多,也更难捕捉到。下面是一段有问题的代码:


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202741377.JPG[/img]

 此时,conn是一个全局变量,因此在程序启动前,将它初始化为0(就是NULL)。mysql_init()遇到NULL参数,因此初始化并分配一个新的连接处理程序。只要将conn传递给需要非NULL连接处理程序的MySQLCAPI函数,系统就会崩溃。这些代码段的修改就是确保conn有一个可知的值。例如,可以将它初始化到已经分配的MYSQL结构地址中去:


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202741539.JPG[/img]

然而,推荐的(较容易的!)解决方案仅仅是将NULL显式地传递给mysql_init(),让该函数分配MYSQL结构,并将返回值赋值给conn:


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202741584.JPG[/img]

无论如何不要忘记检验mysql_init()的返回值,以确保它不是NULL。
2.错误2——有效结果集检验的失败
请记住检查希望得到的结果集的调用状态。下面的代码没有做到这一点:


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202741352.JPG[/img]

不幸地是,如果mysql_store_result()失败,res_set为NULL,while循环也不执行了,应[wiki]测试[/wiki]返回结果集函数的返回值,以确保实际上在进行工作。
3.错误3——NULL列值引起的失败
不要忘记检查mysql_fetch_row()返回的数组MYSQL_ROW中列值是否为NULL指针。如果row[i]为NULL,则在一些机器上,下面的代码就会引起崩溃:


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202741922.JPG[/img]

该错误危害最大的部分是,有些printf()的版本很宽容地对NULL指针输出了“(null)”,这就使错误很容易逃脱而没有把错误定位。如果把程序给了朋友,而他只有不太宽容printf()版本,程序就会崩溃,您的朋友会认为您是个无用的[wiki]程序员[/wiki]。循环应该写成下面这样:


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202741881.JPG[/img]

不需要检查列值是否为NULL的惟一一次是当已经从列信息结构确定IS_NOT_NULL()为真时。
4.错误4——传递无意义的结果缓冲区
需要您提供缓冲区的客户机库函数通常要使这些缓冲区真正存在,下面的代码违反了这个规则:


[img]http://www.wangyeba.com/Article/UploadFiles/200512/20051214202741344.JPG[/img]

问题是什么呢?to_str必须指向一个存在的缓冲区,而在这个样例中没有,因此,它指向了随意的位置。不要向mysql_escape_string传递无意义的指针作为to_str参数,否则它会恣意践踏内存。
页: [1]
查看完整版本: MySQL数据库技术(36)