垂直搜索

来源:互联网 发布:威斯特伐利亚和约知乎 编辑:程序博客网 时间:2024/04/30 20:06

 

    垂直搜索是针对某一个行业的专业搜索引擎,是搜索引擎的细分和延伸,是对网页库中的某类专门的信息进行一次整合,定向分字段抽取出需要的数据进行处理后再以某种形式返回给用户。
   垂直搜索引擎和普通的网页搜索引擎的最大区别是对网页信息进行了结构化信息抽取,也就是将网页的非结构化数据抽取成特定的结构化信息数据,好比网页搜索是以网页为最小单位,基于视觉的网页块分析是以网页块为最小单位,而垂直搜索是以结构化数据为最小单位。然后将这些数据存储到数据库,进行进一步的加工处理,如:去重、分类等,最后分词、索引再以搜索的方式满足用户的需求。
   整个过程中,数据由非结构化数据抽取成结构化数据,经过深度加工处理后以非结构化的方式和结构化的方式返回给用户。
   垂直搜索引擎的应用方向很多,比如企业库搜索、供求信息搜索引擎、购物搜索、房产搜索、人才搜索、地图搜索、mp3搜索、图片搜索……几乎各行各业各类信息都可以进一步细化成各类的垂直搜索引擎。
   举个例子来说明会更容易理解,比如购物搜索引擎,整体流程大致如下:抓取网页后,对网页商品信息进行抽取,抽取出商品名称、价格、简介……甚至可以进一步将笔记本简介细分成“品牌、型号、CPU、内存、硬盘、显示屏、……”然后对信息进行清洗、去重、分类、分析比较、数据挖掘,最后通过分词索引提供用户搜索、通过分析挖掘提供市场行情报告。
   垂直搜索引擎大体上需要以下技术
   1.Spider
   2.网页结构化信息抽取技术或元数据采集技术
   3.分词、索引
   4.其他信息处理技术
   垂直搜索引擎的技术评估应从以下几点来判断
   1.全面性
   2.更新性
   3.准确性
   4.功能性
   垂直搜索的进入门槛很低,但是竞争的门槛很高。没有专注的精神和精湛的技术是不行的。行业门户网站具备行业优势但他们又是没有技术优势的,绝对不要想像着招几个人就可以搞定垂直搜索的全部技术,作为一个需要持续改进可运营的产品而不是一个项目来说对技术的把握控制程度又是垂直搜索成功的重要因素之一。
   垂直搜索引擎是相对通用搜索引擎的信息量大、查询不准确、深度不够等提出来的新的搜索引擎服务模式,通过针对某一特定领域、某一特定人群或某一特定需求提供的有一定价值的信息和相关服务。其特点就是“专、精、深”,且具有行业色彩,相比较通用搜索引擎的海量信息无序化,垂直搜索引擎则显得更加专注、具体和深入。
   垂直搜索引擎能否赢得市场?
   垂直搜索引擎为用户提供的并不是上百甚至上千万相关网页,而是范围极为缩小、极具针对性的具体信息。因此,特定行业的用户更加青睐垂直搜索引擎,是垂直搜索引擎的长期、稳定的群体。
   中国十年多来互联网的不断发展,造就出1.3亿的网民,搜索引擎也出现空前的火热。在互联网出现的初期,雅虎、新浪、网易等大型门户网站拥有着绝对多的浏览量,原因在于当初的大部分网站在技术上无法与门户网站相媲美,多数质量较差,内容不丰富,所以大型门户网站优秀的网页设计风格,大量的信息及时更新赢得了用户的认可,创造了第一次互联网的高峰。然而随之近年来网络技术的普及与应用,建立一个专业的网站已经不存在太多的技术门槛。于是看好互联网前景的网站纷纷涌现在我们的面前。相对比而言在某些领域中,大型门户网站的页面风格反而不如一些中小型网站的界面漂亮,同时各种分类的行业网站也慢慢的兴起,也使得门户网站的专业性信息远远难以相论,如此一来导致了流量的分流,众多的商家也逐渐意识到互联网商机并不像当初抄做的那样有实际价值。于是互联网第一次危机出现,这也是互联网发展的必然趋势。
   搜索引擎的出现,整合了众多网站信息,恰恰起到了信息导航的作用。通用搜索引擎就如同互联网第一次出现的门户网站一样,大量的信息整合导航,极快的查询,将所有网站上的信息整理在一个平台上供网民使用,于是信息的价值第一次普遍的被众多商家认可,迅速成为互联网中最有价值的领域。互联网的低谷由此演变为第二次高峰。大家熟知的搜索引擎Google、百度、雅虎等是通用搜索引擎现如今的杰出代,他们为互联网的发展做出了重要的贡献。然而,搜索引擎行业也不是一家公司就可以独撑天下的,从百度的上市、yahoo中国的并购一系列动作表明,如今的搜索引擎大战如同门户网站初期的竞争一样激烈。相信,通用搜索引擎在经历过一段时间的角逐后,也将会继续维持几大服务商各自分控一部分市场的局面。
   垂直搜索引擎概念的提出,就是针对性的为某一特定领域、某一特定人群或某一特定需求提供的有一定价值的信息和相关服务。可以简单的说成是搜索引擎领域的行业化分工。众多专业性网站、行业网站独立服务于互联网的成功,恰恰证明了互联网的格局应该是多方面的。通用搜索引擎的性质,决定了其不能满足特殊领域、特殊人群的精准化信息需求服务。市场需求多元化决定了搜索引擎的服务模式必将出现细分,针对不同行业提供更加精确的行业服务模式。可以说通用搜索引擎的发展为垂直搜索引擎的出现提供了良好的市场空间,势必将出现垂直搜索引擎在互联网中占据部分市场的趋势,也是搜索引擎行业细分化的必然趋势。
1、垂直搜索引擎不是什么?
   垂直搜索不只是类google的行业通用搜索。以房产行业为例,如果我们按照google抓取网页的方式,来建造一个房产行业google的做法,是行不通的。技术壁垒不用解释,就算我们借助nutch,lucene等搜索技术来做,我们也无法提供差异化的服务,而没有差异化的产品在互联网赢家通吃的规则下是无法生存的,就是不要简单地模仿,而要想办法形成互补。
   垂直搜索和目前的google,baidu等通用搜索从定位,内容,用户,市场策略等都是不同的。所以垂直搜索不只是简单的行业google。
   用户使用google,baidu等通用搜索引擎的方式是通过关键字的方式实现的,是语义上的搜索,返回的结果倾向于知识成果,比如文章,论文,新闻等;垂直搜索也是提供关键字来进行搜索的,但被放到了一个行业知识的上下文中,返回的结果更倾向于信息,消息,条目等。对买房的人讲,他希望找的房子供求信息和文章,新闻等不同。这个特性是他们各自的的技术特点决定的。打个比方,如果google搜索引擎是一个正金字塔型,那么垂直搜索引擎就是个倒金字塔型,两者是互补的。

2、垂直搜索是什么?
   我认为:垂直搜索的本质是对垂直门户信息提供方式的一次简化性的整合。
搜索领域有句明言:就是用户无法描述道他要找什么,除非让他看到想找的东西,这个过程有点像找对象,碰运气是用户搜索行为的最大的特征。而垂直搜索引擎就是提高为用户提供更好的运气。
   垂直搜索是服务于某项功能的,比如:用户搜索租房,买房信息就是一种垂直搜索。对信息的再加工处理是非常关键的,不管是结构化的数据,还是非结构化的数据。

3、垂直搜索的内容来源:
   A门户网站自身的资源
   B以开放接口方式让行业用户提供的资源
   C普通用户发布的资源
   D抓取行业用户的资源
   微软亚洲研究院负责搜索的一名技术专家说:75%的内容通用搜索引擎搜索不出来。这里面包含2层含义:
   (1)网站结构不合理,网页对搜索引擎不友好;
   (2)由于信息在互联网是海量的,非结构化的信息需要经过结构化的梳理后才能更好的展现。 如果梳理者能提供搜索,那样会更好。而垂直门户网站就是行业信息最好的梳理者。 垂直搜索引擎提供的主要内容不应该局限于普通的网页索引,而且包括商业信息的加工,结构化的信息。
4、进入垂直搜索的门槛在那里?
   在互联网上说门槛,就是比资源。垂直搜索也是这样,能否提供全面权威的行业信息,能否拥有行业资源是垂直搜索引擎发展的门槛。换句化说,垂直门户是垂直搜索血统最近的父亲。作为房产行业的搜房网就是一个垂直门户,在房产领域没有谁比我们更清楚什么是垂直搜索了。
   垂直搜索的难点不是技术,而是用户参与门户网站行为的创新和垂直门户网站对产业上下游信息资源的整合。
5、垂直搜索的特点:
   (1)、搜索是一个产业商业联盟的平台,一个集成产业链的上下游公司的搜索门户。
   (2)、垂直搜索的表现方式和google,baidu的表现方式不同,结构化的搜索和非结构化搜索并用。
   (3)、从广告模式上 提供了除 google adsense 和 百度竞价排名广告 之外的另一种可能。
6、垂直搜索引擎的三个特点:
   (1)、垂直搜索引擎抓取的数据来源于垂直搜索引擎关注的行业站点:
比如:找工作的搜索引擎 www.deepdo.com 的数据来源于:www.51job.com ,www.zhaoping.com , www.chinahr.com 等等;
   股票搜索引擎 www.macd.cn 的数据来源于: www.jrj.com.cn , www.gutx.com 等股票站点;
   (2)、垂直搜索引擎抓取的数据倾向于结构化数据和元数据:
   比如:我们找工作关注的:
   职位信息: 软件工程师;
   公司名称,行业名称:软件公司,外包行业等;
   地点:北京,海淀;
   (3)、垂直搜索引擎的搜索行为是基于结构化数据和元数据的结构化搜索:
比如: 找:海淀 软件工程师 的工作等。
7、垂直搜索引擎站点的8条准则:
   1、选择一个好的垂直搜索方向。俗话说男怕选错行,这一点对于搜索引擎来说也是一样的,除了选择的这个行业有垂直搜索的大量需求外,这个行业的数据属性最好不要和 Yahoo,Google等通用搜索的的抓取方向重叠。
   目前热门的垂直搜索行业有:购物,旅游,汽车,工作,房产,交友等行业。搜索引擎对动态url数据不敏感也是众所周知的,这些可以作为垂直搜索引擎的切入点;
   2、评价所选垂直搜索行业的网站、垂直搜索内容、行业构成等情况:
   我们都知道垂直搜索引擎并不提供内容来源,它的数据依赖爬虫搜集,并做了深度加工而来的。因此考虑垂直搜索引擎的所处的大环境和定位至关重要。
   3、深入分析垂直搜索引擎的索引数据特点:
   垂直搜索引擎的索引数据过于结构化,那么进入的门槛比较低,行业竞争会形成一窝蜂的局面;如果搜索数据特点是非结构化的,抓取,分析这样的数据很困难,进入壁 垒太高,很可能出师未杰身先死。
   4、垂直搜索引擎的索引数据倾向于结构化数据和元数据,这个特点是区别于yahoo,google等通用搜索引擎的,这是垂直搜索引擎的立足点。而垂直搜索引擎是根植于某一个行业 ,因此行业知识,行业专家这些也是通用搜索引擎不具备的。也就是说进入垂直搜索是有门槛的。
   5、垂直搜索引擎的搜索结果要覆盖整个行业,搜索相关性要高于通用搜索引擎,贴近用户搜索意图,搜索结果要及时。
   6、垂直搜索引擎的web 2.0 需求:
   垂直搜索引擎的搜索数据由于带有结构化的天性,相对于通用搜索引擎的全文索引而言,更显的少而精。因此,设计的时候要提供收集用户数据的接口,同时提供tag,积 分等机制,使搜索结果更加“垂直”。
   7、垂直搜索引擎的目标是帮助用户解决问题,而不只是向通用搜索引擎一样发现信息:这一点是垂直搜索引擎的终极目标。 在做垂直搜索引擎的时候你需要考虑:什么问题是这个行业内的特殊性问题,什么问题是一般性问题。keso多次提到google的目标是让用户尽快离开google,而垂直搜索引擎应该粘住用户。一般来说,使用垂直搜索引擎的用户都是和用户的利益需求密切相关的。所谓利益需求是我自己独创的,大意是和用户工作密切相关,生活中必不可少的需求,而求有持续性。比如:学生找论文,业主找装修信息等等这样的需求。因此粘住用户,让用户有反馈的途径是一个关键部分。
   8、垂直搜索引擎的社区化特征:
   这一条和第9条是相关的。
   俗话说物以类聚,人以群分,垂直搜索引擎定位于一个行业,服务于一群特定需求的人群,这个特点决定了垂直搜索的社区化行为。人们利用垂直搜索引擎解决问题,分享回馈。现在做网站都讲求社区化,所以垂直搜索引擎本质上还是:对垂直门户信息提供方式的一次简化性的整合。

搜索市场细分 Google、百度面临挑战
  
   百度上市后,搜索市场一下子热了起来。越来越多的企业围绕着搜索市场作起了文章。而且,在搜索大战的同时,一些企业也抛出了一些惊人言论。近日,记者从专业做人脉交际的联络家(www.linkist.com)技术总监冉征处了解到,联络家正在加紧研发人脉相关领域的专业垂直搜索引擎系统,比如工作招聘信息搜索引擎等,预计将在2005年底推出,联络家之所以涉足专业垂直搜索引擎领域,是看到未来垂直专业搜索引擎市场的巨大商机,他认为未来搜索市场将进一步细分,象Google、百度等主张大而全的全球式搜索引擎将会面临垂直专业搜索引擎更大的竞争与挑战,他们的市场分额将会被逐渐瓜分,专业的行业性垂直搜索将受到网民的青睐。
   那么缘何能得出如此结论呢?冉先生解释,CNNIC第十四次互联网调查显示,搜索以71.9%的绝对优势成为用户从互联网上获得信息的主要方式。几乎在全球所有的调查中,搜索引擎都是互联网上使用程度仅次于电子邮箱的服务,搜索引擎服务能成为最受欢迎的服务是因为他解决了用户在浩瀚的互联网海量快速定位信息屏颈问题,在海量的网页里找信息按照传统方式需要用户一个网站一个网站一级目录一级目录下找,要耗费大量的精力和时间,几乎是不可能实现的任务。但互联网的信息量呈爆炸趋势增长,几年前全球式搜索引擎收录的网页量只有几千万页,而现在已经达到几十亿页,数量增加带来的是搜索服务的品质下降,查询的结果集就是海量的,经常是几十万笔的资料,结果里存在大量的重复信息和垃圾信息,用户越来越难迅速的找到符合的信息,现在经常使用搜索引擎可以感觉到很难在短时间内准确的筛选出需要的内容,而垂直搜索引擎针对专业特定的领域或行业的内容做了专业和深入的分析挖掘,精细分类,过滤筛选等,信息定位更精准,因此在此垂直领域或行业提供的搜索服务势必更好更强,更为用户所欢迎。
   比如,对于一个网民来说如果有对特定的领域或行业的信息需求的时候,如果一个是专业的垂直搜索能做到精确锁定内容,但内容量偏小,而另一个是能检索出大量内容,但搜索到的内容一大部分都是“垃圾”并且很难找到符合的信息,这样的话,你会选择哪个呢,就如用户想购买一个商品,他是会去专门的比价购物搜索引擎上找还是会去Google上找,如果你想找一份工作,是会去专门的工作搜索引擎上找还是会去百度上找?答案很明显,更多的用户会舍弃后者,即使前者品牌小名声小,但结果往往是最令网民看重的。
   冉先生还象记者举了个简单的例子,联络家LINKIST一直做人脉交际圈的拓展,也就是现在炙手可热的SNS网站,SNS网站的目的就是要建立一个庞大的人脉圈,参与其中的人都能通过站内人脉的搜索引擎找到自己想找的人,可以找工作、搜罗人才、寻找合作商机。联络家LINKIST短短几个月的时间已经聚敛起了近7万多位高级商务人士。有了一定的用户基础做铺垫,联络家LINKIST目前大力开发人脉专业领域的垂直搜索引擎,如工作搜索引擎的人脉搜索引擎,而这比以往的“贴简历、翻招聘信息”的机械作法要灵活的多。
   而且,能做出这样的预测显然并不是空穴来风。据记者了解,现在搜索市场大量的的垂直专业搜索引擎的诞生如雨后春笋般,如比价购物搜索引擎,工作搜索引擎,博客搜索引擎等等,占了百度几乎一半以上的流量的MP3搜索,其实也可以说是专业的搜索MP3的垂直搜索引擎,许多垂直门户也纷纷推出了自己的搜索引擎系统。
   记者获悉,之前刚从网易内容总监职位上辞职后创业的李学凌也作起了针对博客内容的搜索引擎,这也表露出,垂直搜索引擎的市场正在孕育过程中,既是机会又存在着挑战,其赢利模式也已经在Google、百度等身上得到了验证。而且,很多风险投资(VC)对搜索的概念已经认可并下了赌注。
   那么,象Google、百度能会不会通过“补课”挤掉这部分专用搜索市场呢?冉先生对记者表示,Google、百度注定了走的是大而全的粗犷路线,而专用的垂直搜索引擎则不同,需要对做内容的深度挖掘,做精细的分类,构建专业领域的知识库体系等等,而这些都是Google、百度等无法做到的,他们根本就没有精力做这些,也不可能针对每个行业领域都能做透,“术业有专攻”就是这个道理。
   就象门户网站与专业垂直的行业性网站可以共存一样,网民也有不同的胃口,有的仅仅是简单模糊的信息就已经满足了,而一些寻找精确内容的网民则更青睐于专业引擎,比如你打算换一份工作,以前去人才招聘网站贴简历往往都尿杳无音信,现在,就可以去联络家LINKIST试试,还能跟同行的朋友交换下最新的行业信息,探讨下行业发展趋势。而且,以后联络家LINKIST推出人脉引擎后,只需轻轻点击便能收获颇丰。
   有专家预测,未来,专业的垂直搜索将掀起一轮热潮,而且,垂直搜索引擎不会是一个简单的文本框、一个按纽就走遍天下了,更需要专业的信息辅助和配套的增值内容的支持,也就是对相关内容的二次“加工”。而这恰恰是Google、百度类所不能提供的。相信,届时很多VC的眼球会聚焦于此,而Google、百度又将面临怎样的挑战呢?我们只能拭目以待了。

   要了解垂直搜索引擎,就要同大家熟知的横向搜索引擎即通用搜索引擎来做对比。目前互联网领域主要的搜索引擎服务商如yahoo、百度、google等,为用户提供的都是横向的海量信息搜索。他们可以满足大量信息的横向搜索、提供,但很难兼顾搜索的准确度与相关度的质量。通用搜索引擎的价值在于在做大量的信息导航,对于信息需求相对集中、分类更加详细的行业客户缺乏导向。垂直搜索引擎的产生正是有效的解决了以上通用搜索引擎无法满足的市场需求。
   垂直搜索指搜索引擎为用户提供的并不是上百甚至上千万相关网页,而是范围极为缩小、极具针对性的具体信息。换言之,搜索引擎收集的是市场空间中某一“市场利基”的数据,如工作、旅游、高端房地产等。这样的信息不但更加易于为用户所消化,而且也更有深度。
   Google、雅虎、MSN这几大搜索引擎巨头主宰着互联网搜索市场,全球大多数网民 都是通过这几大搜索引擎查找自己所需的信息的。但在查找一些具体信息时,这几大搜索引擎的表现却并不尽如人意。有时候用户得到的是往往是和他们的查询本意 风马牛不相及的结果。对拉近用户及其所需信息之间的距离的需求催生并促进了搜索行业的利基发展。垂直搜索引擎瞄准的正是搜索市场中的不同利基市场。
   利基是Niche这一英文名词的译称,利基市场指市场中通常为大企业忽略的某些细分市场;而利基市场战略则指企业通过专业化经营来占领这些市场,从 而最大限度的获取收益所采取的策略。实施利基战略的重要意义在于:进行市场利基的公司事实上已经充分了解了目标顾客群,因而能够比其他公司更好、更完善地 满足消费者的需求。并且,市场利基者可以依据其所提供的附加价值收取更多的利润额。总之,市场利基者获得的是“高边际收益”,而密集市场营销者获得的只是“高总量收益”。
   分析家认为:利基搜索市场和强大的在线广告市场之间有着密切的联系。管理者可以象Google和雅虎那样利用搜索结果页面运作广告,即在搜索结果页面上提供一定的定向文本广告。这种广告策略已被视作搜索行业的一个盈利渠道。
对于垂直搜索引擎来说,由于数据源得到了详细划分,使得对这些数据进行操作,并将其通过简单易用、消费方便的方式表现出来变成可能。此外,以往的两种网络广告“每千次展示成本”和“每点击付费(CPC)”这两种广告方式上存在着效率低下,广告费用风险高的问题,这也正是垂直搜索被看好的一个主要原因。垂直搜索能够提供更为集中的受众群体,从而提高搜索引擎广告的宣传能力。同时,垂直搜索也能够有效推动新的广告商机的发展――我们姑且称之为“每行动成本”。这种广告方式不限广告投放量,按照广告投放的实际效果,即按潜在客户回应行动计费。
浅谈垂直搜索引擎
   通过关键字:"垂直搜索引擎"在google查一下,在返回结果中可以看到不少的投资公司很看好这一领域,即使百度的发言人也在演讲中提到垂直搜索引擎,而一些国外软件巨头例如Google和Microsoft也在这一领域有所动作,据说Microsoft的一个研究购物的小组最近就推出了一个购物垂直搜索引擎,
   首先,谈谈垂直搜索引擎的基本原理,垂直搜索引擎针对某个特定领域,招聘、购物、blog、新闻等方面都是垂直搜索的潜在领域,假想一下,如果网络上有非常便利的产品垂直搜索引擎、新闻垂直搜索平台,以后上网就不会漫无目的了,现在许多的行业门户做的很红火,而垂直搜索引擎的模式本身就是一种很好的门户网站.
   接下来谈谈垂直搜索引擎的技术,垂直搜索引擎技术同信息采集技术有一些共同点,不同的是,信息采集主要是将采集的信息导入本地库,而垂直搜索引擎主要是以网页的形式展现给用户,通用搜索引擎主要是利用一个spider程序到网络上爬行,一般是某个特定的周期派出一次将网页更新,垂直搜索引擎同样应有一个spider程序,但该程序只在一些特定的网络上爬行,并不会对每一个链接都感兴趣,相对来说,垂直搜索引擎的收录范围大大缩小了,但并不意味着内容的缩小,通用搜索引擎对一些动态脚本是不敏感的,例如***asp?id=***之类的网页一般不被收录,而恰恰是这类动态网页包含了丰富的内容,垂直搜索引擎是必须收录这些动态脚本的,这就需要在技术上做一些特殊处理,另外由于目前网页中的链接形式非常多,不但有动态脚本也有flash做的链接,这些链接方式通过传统的spider程序是很难解析出来的,在垂直搜索引擎中也应该解决.
   以上只是垂直搜索引擎的简单说明,如果需要深入了解甚至实际开发,建议按如下步骤深入学习:
   1) 到搜索引擎中查一下垂直搜索引擎,进一步了解垂直搜索引擎的应用前景
   2) 如果要实际开发一个垂直搜索引擎,建议到一些开源网站上找一些spider程序进行分析,看看如何改造成一个垂直搜索的spider,一般将爬行全部链接的方式改为只爬行特定链接.这些特定链接可以通过正则表达式的方式来匹配,凡不符合匹配的不进行采集.

中文搜索引擎技术揭密:排序技术

    随着“眼球经济”席卷互联网,成千上万的资金迅速流向最能吸引浏览着眼球的搜索引擎市场。有大量调查显示搜索引擎市场正处在高速发展时期,成为了未来几年内最具发展潜力的产业之一。随着Google、百度、中国搜索等各具特色的搜索引擎逐渐成为人们最常用的网络工具,企业对搜索引擎的注意力也从“观察”升级为“动武”。


    随着市场容量和使用者人数的不断激增,如何完善搜索功能使之更加公平、公开、标准和人性化也就随之成为了一个备受关注的话题。但是有一个矛盾体在这其中不断的显现出来:收费可以为搜索引擎公司带来利润,但同时会降低访问者的体验满意度。如何权衡金钱和用户需求之间的天平呢?


Google成功的秘密


    到2004年为止,Google( http://www.google.com)已经连续两年被评为全球第一品牌,Google成立仅五年时间,最初只是两个斯坦福大学学生的研究项目。这不能不说是一个奇迹,就像比尔?盖茨创制奇迹一样。比尔?盖茨能创造奇迹,是因为他看准了个人计算机软件市场的趋势,所以创建的公司叫Microsoft(微软):Micro(小)Soft(软件)。那么Google呢?在Google出来之前已经有一些很有成就的搜索引擎公司,其实力也很强,看来不只是Google看见了搜索的趋势。Google究竟成功的秘密在哪儿?


    Google的成功有许多因素,最重要的是Google对搜索结果的排序比其它搜索引擎都要好。Google保证让绝大部分用搜索的人,都能在搜索结果的第一页找到他想要的结果。客户得到了满足,下一次还过来,而且会向其他人介绍,这一来一往,使用的人就多了。所以Google在没有做任何广告的前提下,让自己成为了全球最大的品牌。Google究竟采用了哪种排序技术?PageRank,即网页级别。


    Google有一个创始人叫Larry Page,据说PageRank的专利是他申请的,于是依据他的名字就有了Page Rank。国内也有一家很成功的搜索引擎公司,叫百度(http://www.baidu.com)。百度的创始人李彦宏说,早在1996年他就申请了名为超链分析的专利,PageRank的原理和超链分析的原理是一样的,而且PageRank目前还在Paten-pending(专利申请中)。言下之意是这里面存在专利所有权的问题。这里不讨论专利所有权,只是从中可看出,成功搜索引擎的排序技术,就其原理上来说都差不多,那就是链接分析。超链分析和PageRank都属于链接分析。


    链接分析到底为何物?由于李彦宏的超链分析没有具体的介绍,笔者唯一看过的就是在美国专利局网站上关于李彦宏的专利介绍。PageRank的介绍倒是不少,而且目前Google毕竟是全球最大的搜索引擎,这里以PageRank为代表,详细介绍链接分析的原理。


 


PageRank揭密


    PageRank的原理类似于科技论文中的引用机制:谁的论文被引用次数多,谁就是权威。说的更白话一点:张三在谈话中提到了张曼玉,李四在谈话中也提到张曼玉,王五在谈话中还提到张曼玉,这就说明张曼玉一定是很有名的人。在互联网上,链接就相当于“引用”,在B网页中链接了A,相当于B在谈话时提到了A,如果在C、D、E、F中都链接了A,那么说明A网页是最重要的,A网页的PageRank值也就最高。


    如何计算PageRank值有一个简单的公式 :


中文搜索引擎技术揭密:排序技术


    其中:系数为一个大于0,小于1的数。一般设置为0.85。网页1、网页2至网页N表示所有链接指向A的网页。


由以上公式可以看出三点 :


1、链接指向A的网页越多,A的级别越高。即A的级别和指向A的网页个数成正比,在公式中表示,N越大, A的级别越高;


2、链接指向A的网页,其网页级别越高, A的级别也越高。即A的级别和指向A的网页自己的网页级别成正比,在公式中表示,网页N级别越高, A的级别也越高;


3、链接指向A的网页,其链出的个数越多,A的级别越低。即A的级别和指向A的网页自己的网页链出个数成反比,在公式中现实,网页N链出个数越多,A的级别越低。


    每个网页有一个PageRank值,这样形成一个巨大的方程组,对这个方程组求解,就能得到每个网页的PageRank值。互联网上有上百亿个网页,那么这个方程组就有上百亿个未知数,这个方程虽然是有解,但计算毕竟太复杂了,不可能把这所有的页面放在一起去求解的。对具体的计算方法有兴趣的朋友可以去参考一些数值计算方面的书。


    总之,PageRank有效地利用了互联网所拥有的庞大链接构造的特性。 从网页A导向网页B的链接,用Google创始人的话讲,是页面A对页面B的支持投票,Google根据这个投票数来判断页面的重要性,但Google除了看投票数(链接数)以外,对投票者(链接的页面)也进行分析。「重要性」高的页面所投的票的评价会更高,因为接受这个投票页面会被理解为「重要的物品」。从新浪、雅虎、微软的首页都有我网页的三个链接的话,可能比我在其他网站找三十个链接还强。如果还有人不理解这个原理,就去想想有句成语叫:三人成虎。如果有三个人都说北京大街上有老虎,那么许多人会认为有老虎,如果这三个人都是国家领导人的话,那么所有人都会认为北京大街上有老虎。


    每个网页都会有PageRank值,如果大家想知道自己网站的网页PageRank值是多少,最简单的办法就是下载一个Google的免费工具栏( http://toolbar.google.com/),


中文搜索引擎技术揭密:排序技术


    每当你打开一个网页,都可以很清楚的看见此网页的PageRank值。当然这个值是一个大概数字。


    据Google技术负责人介绍,Google除了用PageRank衡量网页的重要程度以外,还有其它上百种因素来参与排序。其它搜索引擎也是如此,不可能按照某一种规则来进行搜索结果的排序。


其他方法


HillTop算法:


    HillTop同样是一项搜索引擎结果排序的专利,是Google的一个工程师Bharat在2001年获得的专利。Google的排序规则经常在变化,但变化最大的一次也就是基于HillTop算法进行了优化。HillTop究竟原理如何,值得Google如此青睐?


    其实HillTop算法的指导思想和PageRank的是一致的,都是通过网页被链接的数量和质量来确定搜索结果的排序权重。但HillTop认为只计算来自具有相同主题的相关文档链接对于搜索者的价值会更大:即主题相关网页之间的链接对于权重计算的贡献比主题不相关的链接价值要更高。如果网站是介绍“服装”的,有10个链接都是从“服装”相关的网站链接过来,那这10个链接比另外10个从“电器”相关网站链接过来的贡献要大。Bharat称这种对主题有影响的文档为“专家”文档,从这些专家文档页面到目标文档的链接决定了被链接网页“权重得分”的主要部分。


    与PageRank结合HillTop算法确定网页与搜索关键词的匹配程度的基本排序过程取代了过份依靠PageRank的值去寻找那些权威页面的方法。这对于两个具有同样主题而且PR相近的网页排序过程中,HillTop算法就显得非常的重要了。HillTop同时也避免了许多想通过增加许多无效链接来提高网页PageRank值的做弊方法。


锚文本(Anchor Text)


    锚文本名字听起来难以理解,实际上锚文本就是链接文本。例如,在个人网站上把中央电视台( www.cctv.com )做为新闻频道的链接,访问者通过点击网站上的“新闻频道”就能进入 http://www.cctv.com网站,那么“新闻频道”就是中央电视台网站首页的锚文本。


    锚文本可以做为锚文本所在的页面的内容的评估。正常来讲,页面中增加的链接都会和页面本身的内容有一定的关系。服装的行业网站上会增加一些同行网站的链接或者一些做服装的知名企业的链接;另一方面,锚文本能做为对所指向页面的评估。锚文本能精确的描述所指向页面的内容,个人网站上增加Google的链接,锚文本为“搜索引擎”。这样通过锚文本本身就能知道,Google是搜索引擎。


    锚文本对搜索引擎起的作用还表现为可以收集一些搜索引擎不能索引的文件。例如,网站上增加了一张张曼玉的照片,格式为jpg文件,搜索引擎目前很难索引(一般只处理文本)。若这张照片链接的锚文本为“张曼玉的照片”,那么搜索引擎就能识别这张图片是张曼玉的照片,以后访问者搜索“张曼玉”的时候,这张图片就能被搜索到。


    由此可见,在网页设计中选择合适的锚文本,会让所在网页和所指向网页的重要程度有所提升。


页面版式


    每个网页都有版式,包括标题、字体、标签等等。搜索引擎也会利用这些版式来识别搜索词与页面内容的相关程度。以静态的html格式的网页为例,搜索引擎通过网络蜘蛛把网页抓取下来后,需要提取里面的正文内容,过滤其他html代码。在提取内容的时候,搜索引擎就可以记录所有版式信息,包括:哪些词是在标题中出现,哪些词是在正文中出现,哪些词的字体比其他的字体大,哪些词是加粗过,哪些词是用KeyWord标识过的等等。这样在搜索结果中就可以根据这些信息来确定所搜索的结果和搜索词的相关程度。例如搜索“毛泽东”,假如有两个结果,一篇文章标题是《毛泽东的一生》,另一篇文章的标题是《江青的一生》但内容有提到毛泽东,这时搜索引擎会认为前者比较重要,因为“毛泽东”在标题里出现了。


    因此,合理的利用网页的页面版式,会提升网页在搜索结果页的排序位置。


收费排名


    应该说收费排名并不属于排序技术(这里指的收费排名也包括竞价排名),而是一种搜索引擎的赢利模式。但收费排名已经最直接的影响到了搜索引擎的排序,在此也略做说明。


    用户可以购买某个关键词的排名,只要向搜索引擎公司交纳一定的费用,就可以让用户的网站排在搜索结果的前几位,按照不同关键词、不同位置、时间长短来定义价格。价格从几千元到几十万元不等(像“六合彩”在3721上的排名费用大多是几十万)。


    收费排名一方面给搜索引擎公司带来收益,一方面给企业带来访问量,另外对访问者也有一定好处。因为访问者想找“西服”,企业想卖“西服”,于是出钱让访问者能找到他,这样,买家和卖家能马上见面。但收费排名给访问者带来更多的却是不真实,结果排序已经失去了公正性,有时候还带来大量垃圾。在百度搜索引擎上搜索“ 星球 ”,排在第一位的是一家做石墨的公司,排在第二位的居然是“想找星球?上易趣吧!”(见下图)。真有些让访问者哭笑不得。


中文搜索引擎技术揭密:排序技术


    当然,对于企业来说,收费排名是提升网站在搜索引擎中排名的最直接和最简单的办法。如今,如何提升网页在搜索引擎中的排序,已经形成了一门职业,叫SEO(Search Engine Optimization),即搜索引擎优化。SEO是针对搜索引擎排序的技术,通过修改网页(或者网站)结构和主动增加网站链接等方法来让搜索引擎认为这些网页是很重要的,从而提升网页在搜索引擎结果中的排序。


排序技术的发展趋势


    各种搜索引擎的技术改进和优化,都直接反应到搜索结果的排序上。许多搜索引擎都在进一步研究新的排序方法,来提升客户的满意度。专业人士认为,目前的搜索引擎排序算法上还存在两大不足。


一、没有真正解决相关性。


    相关性是指搜索词和页面的相关程度。仅仅通过链接、字体、位置等表面特征,不能真正判断搜索词和文章的相关性,更何况许多时候这些特征不会都同时存在。这也是许多对搜索引擎做弊方法能有效的原因。另外,有些文章中没有出现搜索词,但说的就是和搜索词十分相关的内容,例如搜索“恐怖分子”,但有网页是介绍本拉登的一些破坏行动,文中没有出现“恐怖分子”的子眼,搜索引擎就无法搜索到该网页。表面特征只能治标,不能治本。治本的方法应该是增加语意理解,例如主题词和关键词的提取,从语意上分析,得出搜索词和网页的相关程度,分析的越准,效果就会越好。


二、搜索结果的单一化。


    在搜索引擎上,任何人搜索同一个词的结果都是一样。这样明显不能满足访问者。科学家搜索“星球”,可能是希望了解星球的知识,但普通人可能是想找“星球大战”电影,但搜索引擎所给的都是一样的结果。如何满足这些不同类型的访问者,需要对搜索结果的个性化。国外vivisimo公司(http://www.vivisimo.com)就是想解决这个问题,他们采用对搜索结果自动聚类的办法来满足不同类型客户的需要。搜索结果排序如果要实现从单一化到个性化,vivisimo已经迈出了一步,但最理想的结果应该是针对每个访问者,排序结果直接和他们的搜索习惯和意愿有关。搜索“体育”,对喜欢足球的人应该把足球的相关结果排在前面,对喜欢篮球的人应该把篮球的相关结果排在前面。


    搜索引擎的排序技术应该也会朝着解决这两个不足的方向发展:语意相关性和排序个性化。前者需要完善的自然语言处理技术,后者需要记录庞大访问者信息和复杂的计算,要达到其中任何一个的要求均非易事,如何解决这些难题,任务落在了科学家和工程师们的肩上,哪个搜索引擎解决了这些问题,她可能会称为下一个搜索世界的霸主。

中文搜索引擎技术揭密:网络蜘蛛


    随着搜索经济的崛起,人们开始越加关注全球各大搜索引擎的性能、技术和日流量。作为企业,会根据搜索引擎的知名度以及日流量来选择是否要投放广告等;作为普通网民,会根据搜索引擎的性能和技术来选择自己喜欢的引擎查找资料;作为学者,会把有代表性的搜索引擎作为研究对象……

    而作为一个网站的经营者,其更关心的或许是如何通过网络载体让更多的网民知道自己的网站,进而获得更高的流量和知名度。这其中,搜索引擎已经成了一个重要的且是免费的宣传途径。一方面,搜索引擎会主动出击,寻找网络上的各种网页数据,并在后台按相关条件进行索引;另一方面,各大网站为了能让自己的内容更多的通过搜索引擎向网民展示,都开始对网站结构进行重大调整,其中包括扁平化结构设计、动态(网页)转静态(网页)、Sitemap等。


    这些看来不经意的举动都让我们切身感受到搜索引擎对我们网络使用方式的改变起到了重要作用。并且,正因为搜索引擎的兴起以及社会各界对其重视程度日渐高涨,还由此创造了一个全新的职位——SEO。实际上,搜索引擎经济的崛起,又一次向人们证明了网络所蕴藏的巨大商机。网络离开了搜索将只剩下空洞杂乱的数据,以及大量等待去费力挖掘的金矿。


    搜索引擎一直专注于提升用户的体验度,其用户体验度则反映在三个方面: 准、全、快 。用专业术语讲是:查准率、查全率和搜索速度(即搜索耗时)。其中最易达到的是搜索速度,因为对于搜索耗时在1秒以下的系统来说,访问者很难辨别其快慢了,更何况还有网络速度的影响。因此,对搜索引擎的评价就集中在了前两者:准、全。中文搜索引擎的“准”,需要保证搜索的前几十条结果都和搜索词十分相关,这需由“ 分词技术 ”和“ 排序技术 ”来决定;中文搜索引擎的“全”则需保证不遗漏某些重要的结果,而且能找到最新的网页,这需要搜索引擎有一个强大的网页收集器,一般称为“网络蜘蛛”,也有叫“网页机器人”。



    研究搜索引擎技术的文章不少,但大部分讨论的是如何评价网页的重要性,对于网络蜘蛛研究的文章不多。网络蜘蛛技术并不是一项十分高深的技术,但要做一个强大的网络蜘蛛,却非易事。在目前磁盘容量已经不是瓶颈的时候,搜索引擎一直在扩大自己的网页数量。最大的搜索引擎Google(http://www.google.com)从2002年的10亿网页增加到现在近40亿网页;最近雅虎搜索引擎( http://search.yahoo.com/)号称收录了45亿个网页;国内的中文搜索引擎百度( http://www.baidu.com)的中文页面从两年前的七千万页增加到了现在的两亿多。据估计,整个互联网的网页数达到100多亿,而且每年还在快速增长。因此一个优秀的搜索引擎,需要不断的优化网络蜘蛛的算法,提升其性能。


    或许有些人有疑问,为何搜索引擎需要用网络蜘蛛抓取网站所有的网页,为什么不在搜索者输入关键词后只把那些需要的结果抓取过来?这实际上是效率问题,搜索引擎不可能在搜索时实时去检查每个网页,而是需要把网页先抓取下来,按照关键词建立好索引,每次搜索的结果都会直接从搜索引擎建立好索引的数据库中查找,然后把结果返回给访问者。 关于搜索引擎系统架构方面的知识, 本文主要介绍网络蜘蛛的相关技术。


网络蜘蛛基本原理


    网络蜘蛛即Web Spider,是一个很形象的名字。把互联网比喻成一个蜘蛛网,那么Spider就是在网上爬来爬去的蜘蛛。网络蜘蛛是通过网页的链接地址来寻找网页,从网站某一个页面(通常是首页)开始,读取网页的内容,找到在网页中的其它链接地址,然后通过这些链接地址寻找下一个网页,这样一直循环下去,直到把这个网站所有的网页都抓取完为止。如果把整个互联网当成一个网站,那么网络蜘蛛就可以用这个原理把互联网上所有的网页都抓取下来。


    对于搜索引擎来说,要抓取互联网上所有的网页几乎是不可能的,从目前公布的数据来看,容量最大的搜索引擎也不过是抓取了整个网页数量的百分之四十左右。这其中的原因一方面是抓取技术的瓶颈,无法遍历所有的网页,有许多网页无法从其它网页的链接中找到;另一个原因是存储技术和处理技术的问题,如果按照每个页面的平均大小为20K计算(包含图片),100亿网页的容量是100×2000G字节,即使能够存储,下载也存在问题(按照一台机器每秒下载20K计算,需要340台机器不停的下载一年时间,才能把所有网页下载完毕)。同时,由于数据量太大,在提供搜索时也会有效率方面的影响。因此,许多搜索引擎的网络蜘蛛只是抓取那些重要的网页,而在抓取的时候评价重要性主要的依据是某个网页的链接深度。


    在抓取网页的时候,网络蜘蛛一般有两种策略:广度优先和深度优先(如下图所示)。广度优先是指网络蜘蛛会先抓取起始网页中链接的所有网页,然后再选择其中的一个链接网页,继续抓取在此网页中链接的所有网页。这是最常用的方式,因为这个方法可以让网络蜘蛛并行处理,提高其抓取速度。深度优先是指网络蜘蛛会从起始页开始,一个链接一个链接跟踪下去,处理完这条线路之后再转入下一个起始页,继续跟踪链接。这个方法有个优点是网络蜘蛛在设计的时候比较容易。两种策略的区别,下图的说明会更加明确。


中文搜索引擎技术揭密:网络蜘蛛


    由于不可能抓取所有的网页,有些网络蜘蛛对一些不太重要的网站,设置了访问的层数。例如,在上图中,A为起始网页,属于0层,B、C、D、E、F属于第1层,G、H属于第2层,I属于第3层。如果网络蜘蛛设置的访问层数为2的话,网页I是不会被访问到的。这也让有些网站上一部分网页能够在搜索引擎上搜索到,另外一部分不能被搜索到。 对于网站设计者来说,扁平化的网站结构设计有助于搜索引擎抓取其更多的网页。


    网络蜘蛛在访问网站网页的时候,经常会遇到加密数据和网页权限的问题,有些网页是需要会员权限才能访问。当然,网站的所有者可以通过协议让网络蜘蛛不去抓取(下小节会介绍),但对于一些出售报告的网站,他们希望搜索引擎能搜索到他们的报告,但又不能完全免费的让搜索者查看,这样就需要给网络蜘蛛提供相应的用户名和密码。网络蜘蛛可以通过所给的权限对这些网页进行网页抓取,从而提供搜索。而当搜索者点击查看该网页的时候,同样需要搜索者提供相应的权限验证。


网站与网络蜘蛛


    网络蜘蛛需要抓取网页,不同于一般的访问,如果控制不好,则会引起网站服务器负担过重。今年4月,淘宝网( http://www.taobao.com )就因为雅虎搜索引擎的网络蜘蛛抓取其数据引起淘宝网服务器的不稳定。网站是否就无法和网络蜘蛛交流呢?其实不然,有多种方法可以让网站和网络蜘蛛进行交流。一方面让网站管理员了解网络蜘蛛都来自哪儿,做了些什么,另一方面也告诉网络蜘蛛哪些网页不应该抓取,哪些网页应该更新。


    每个网络蜘蛛都有自己的名字,在抓取网页的时候,都会向网站标明自己的身份。网络蜘蛛在抓取网页的时候会发送一个请求,这个请求中就有一个字段为User-agent,用于标识此网络蜘蛛的身份。例如Google网络蜘蛛的标识为GoogleBot,Baidu网络蜘蛛的标识为BaiDuSpider,Yahoo网络蜘蛛的标识为Inktomi Slurp。如果在网站上有访问日志记录,网站管理员就能知道,哪些搜索引擎的网络蜘蛛过来过,什么时候过来的,以及读了多少数据等等。如果网站管理员发现某个蜘蛛有问题,就通过其标识来和其所有者联系。下面是博客中国(http://www.blogchina.com)2004年5月15日的搜索引擎访问日志:


中文搜索引擎技术揭密:网络蜘蛛
    网络蜘蛛进入一个网站,一般会访问一个特殊的文本文件Robots.txt,这个文件一般放在网站服务器的根目录下,如: http://www.blogchina.com/robots.txt。网站管理员可以通过robots.txt来定义哪些目录网络蜘蛛不能访问,或者哪些目录对于某些特定的网络蜘蛛不能访问。例如有些网站的可执行文件目录和临时文件目录不希望被搜索引擎搜索到,那么网站管理员就可以把这些目录定义为拒绝访问目录。Robots.txt语法很简单,例如如果对目录没有任何限制,可以用以下两行来描述: User-agent: * Disallow:


    当然,Robots.txt只是一个协议,如果网络蜘蛛的设计者不遵循这个协议,网站管理员也无法阻止网络蜘蛛对于某些页面的访问,但一般的网络蜘蛛都会遵循这些协议,而且网站管理员还可以通过其它方式来拒绝网络蜘蛛对某些网页的抓取。


    网络蜘蛛在下载网页的时候,会去识别网页的HTML代码,在其代码的部分,会有META标识。通过这些标识,可以告诉网络蜘蛛本网页是否需要被抓取,还可以告诉网络蜘蛛本网页中的链接是否需要被继续跟踪。例如: 表示本网页不需要被抓取,但是网页内的链接需要被跟踪。


    关于Robots.txt的语法和META Tag语法,有兴趣的读者查看文献[4]


    现在一般的网站都希望搜索引擎能更全面的抓取自己网站的网页,因为这样可以让更多的访问者能通过搜索引擎找到此网站。为了让本网站的网页更全面被抓取到,网站管理员可以建立一个网站地图,即Site Map。许多网络蜘蛛会把sitemap.htm文件作为一个网站网页爬取的入口,网站管理员可以把网站内部所有网页的链接放在这个文件里面,那么网络蜘蛛可以很方便的把整个网站抓取下来,避免遗漏某些网页,也会减小对网站服务器的负担。


内容提取


    搜索引擎建立网页索引,处理的对象是文本文件。对于网络蜘蛛来说,抓取下来网页包括各种格式,包括html、图片、doc、pdf、多媒体、动态网页及其它格式等。这些文件抓取下来后,需要把这些文件中的文本信息提取出来。准确提取这些文档的信息,一方面对搜索引擎的搜索准确性有重要作用,另一方面对于网络蜘蛛正确跟踪其它链接有一定影响。


    对于doc、pdf等文档,这种由专业厂商提供的软件生成的文档,厂商都会提供相应的文本提取接口。网络蜘蛛只需要调用这些插件的接口,就可以轻松的提取文档中的文本信息和文件其它相关的信息。


    HTML等文档不一样,HTML有一套自己的语法,通过不同的命令标识符来表示不同的字体、颜色、位置等版式,如:、、 等,提取文本信息时需要把这些标识符都过滤掉。过滤标识符并非难事,因为这些标识符都有一定的规则,只要按照不同的标识符取得相应的信息即可。但在识别这些信息的时候,需要同步记录许多版式信息,例如文字的字体大小、是否是标题、是否是加粗显示、是否是页面的关键词等,这些信息有助于计算单词在网页中的重要程度。同时,对于HTML网页来说,除了标题和正文以外,会有许多广告链接以及公共的频道链接,这些链接和文本正文一点关系也没有,在提取网页内容的时候,也需要过滤这些无用的链接。例如某个网站有“产品介绍”频道,因为导航条在网站内每个网页都有,若不过滤导航条链接,在搜索“产品介绍”的时候,则网站内每个网页都会搜索到,无疑会带来大量垃圾信息。过滤这些无效链接需要统计大量的网页结构规律,抽取一些共性,统一过滤;对于一些重要而结果特殊的网站,还需要个别处理。这就需要网络蜘蛛的设计有一定的扩展性。


    对于多媒体、图片等文件,一般是通过链接的锚文本(即,链接文本)和相关的文件注释来判断这些文件的内容。例如有一个链接文字为“张曼玉照片”,其链接指向一张bmp格式的图片,那么网络蜘蛛就知道这张图片的内容是“张曼玉的照片”。这样,在搜索“张曼玉”和“照片”的时候都能让搜索引擎找到这张图片。另外,许多多媒体文件中有文件属性,考虑这些属性也可以更好的了解文件的内容。
    动态网页一直是网络蜘蛛面临的难题。所谓动态网页,是相对于静态网页而言,是由程序自动生成的页面,这样的好处是可以快速统一更改网页风格,也可以减少网页所占服务器的空间,但同样给网络蜘蛛的抓取带来一些麻烦。由于开发语言不断的增多,动态网页的类型也越来越多,如:asp、jsp、php等。这些类型的网页对于网络蜘蛛来说,可能还稍微容易一些。网络蜘蛛比较难于处理的是一些脚本语言(如VBScript和JavaScript)生成的网页,如果要完善的处理好这些网页,网络蜘蛛需要有自己的脚本解释程序。对于许多数据是放在数据库的网站,需要通过本网站的数据库搜索才能获得信息,这些给网络蜘蛛的抓取带来很大的困难。对于这类网站,如果网站设计者希望这些数据能被搜索引擎搜索,则需要提供一种可以遍历整个数据库内容的方法。


    对于网页内容的提取,一直是网络蜘蛛中重要的技术。整个系统一般采用插件的形式,通过一个插件管理服务程序,遇到不同格式的网页采用不同的插件处理。这种方式的好处在于扩充性好,以后每发现一种新的类型,就可以把其处理方式做成一个插件补充到插件管理服务程序之中。


更新周期


    由于网站的内容经常在变化,因此网络蜘蛛也需不断的更新其抓取网页的内容,这就需要网络蜘蛛按照一定的周期去扫描网站,查看哪些页面是需要更新的页面,哪些页面是新增页面,哪些页面是已经过期的死链接。
    搜索引擎的更新周期对搜索引擎搜索的查全率有很大影响。如果更新周期太长,则总会有一部分新生成的网页搜索不到;周期过短,技术实现会有一定难度,而且会对带宽、服务器的资源都有浪费。搜索引擎的网络蜘蛛并不是所有的网站都采用同一个周期进行更新,对于一些重要的更新量大的网站,更新的周期短,如有些新闻网站,几个小时就更新一次;相反对于一些不重要的网站,更新的周期就长,可能一两个月才更新一次。
    一般来说,网络蜘蛛在更新网站内容的时候,不用把网站网页重新抓取一遍,对于大部分的网页,只需要判断网页的属性(主要是日期),把得到的属性和上次抓取的属性相比较,如果一样则不用更新。


结论


    本文主要讨论了网络蜘蛛相关的技术要点,如果要设计好的网络蜘蛛, 需要了解更多的技术细节,可以参考文献


    网络蜘蛛在搜索引擎中占有重要位置,对搜索引擎的查全、查准都有影响,决定了搜索引擎数据容量的大小,而且网络蜘蛛的好坏直接影响搜索结果页中的死链接(即链接所指向的网页已经不存在)的个数。目前如何发现更多的网页、如何正确提取网页内容、如果下载动态网页、如何提供抓取速度、如何识别网站内内容相同的网页等都是网络蜘蛛需要进一步改进的问题。

中文搜索引擎技术揭密:系统架构

     互联网发展的今天,一方面离不开其开放、共享的特性带给人们的全新体验,另一方面也离不开数以亿计的为其提供各类丰富内容的网络节点。互联网被普及前,人们查阅资料第一想到的便是拥有大量书籍资料的图书馆,到了今天你怎么想?或许今天的很多人都会选择一种更方便、快捷、全面、准确的方式——互联网。你可以坐在家里轻点几下鼠标就查到想要的各类信息,这在互联网没有被普及之前,还都仅是一个梦而已,但如今这一切已成为了可能。

  而帮助你通过整个互联网快速查找到目标信息的就是越来越被重视的搜索引擎。有关搜索引擎的技术资料网络上已经很多,关于搜索引擎经济的多方面报道各大媒体也都已经铺天盖地,因此在这里小编并不想过多的谈论这些方面的感受,只想在本次“中文搜索引擎技术揭密”系列文章全部完成之际来聊一下搜索引擎对小编的深远影响。


  记得2000年左右网络上开始大量出现免费个人主页空间,当时的小编还只是一个刚刚进入IT圈的小朋友,看着这些空间那叫一个口水横流,于是乎立刻申请了一个。又经过了一个多月的刻苦修炼和先后三次的改版,自己有生以来的第一个个人主页诞生了。可看着每天寥寥无几的访问量,心里那叫一个难受,可一时间也想不到好办法解决问题。突然有天发现一篇介绍如何在搜索引擎注册自己个人网站的文章,于是小编就照着文章所讲的分别在SOHU、网易等搜索引擎的相关分类目录下注册了自己的个人主页。直至今日,小编才确切的知道了当时盛行的搜索引擎都属“目录搜索引擎”。这实际上是小编第一次使用、认识搜索引擎,再后来通过每天个人主页不断上升的数字小编感觉到了搜索引擎的神奇。


  其实正是由于搜索引擎,才使小编的个人主页被更多的人所熟识,以至于后来有多份工作都是因为这个个人主页所带来的机会。其实这些经历或许很多人都有切身的体会,同样也有很多人因此去全身投入到互联网工作中。这正像那句话讲的“世界真奇妙,不看不知道”,小编在此多加一句“到底怎么看,搜索引擎帮你忙!”


  互联网在近10年的得到飞速发展,互联网正在逐渐深入人们的生活,改变人们的生活。互联网经济也经历了风风雨雨,从缓慢起步到急速膨胀,从泡沫破灭到逐步回暖;从“网络广告”到“拇指经济”,从“网络游戏”到“搜索力经济”。目前,搜索引擎成为最受人们关注的焦点之一,也成为亿万富翁的制造摇篮。越来越多的公司都希望在搜索引擎这座金矿中挖到筐金子,其中许多人会选择拥有自己的搜索引擎。国内著名搜索引擎公司百度(http://www.baidu.com)总裁李彦宏说:搜索引擎不是人人都能做的领域,进入的门槛比较高。


  搜索引擎的门槛到底有多高?搜索引擎的门槛主要是技术门槛,包括网页数据的快速采集、海量数据的索引和存储、搜索结果的相关性排序、搜索效率的毫秒级要求、分布式处理和负载均衡、自然语言的理解技术等等,这些都是搜索引擎的门槛。对于一个复杂的系统来说,各方面的技术固然重要,但整个系统的架构设计也同样不可忽视,搜索引擎也不例外。

  搜索引擎技术和分类


  搜索引擎的技术基础是全文检索技术,从20世纪60年代,国外对全文检索技术就开始有研究。全文检索通常指文本全文检索,包括信息的存储、组织、表现、查询、存取等各个方面,其核心为文本信息的索引和检索,一般用于企事业单位。随着互联网信息的发展,搜索引擎在全文检索技术上逐渐发展起来,并得到广泛的应用,但搜索引擎还是不同于全文检索。搜索引擎和常规意义上的全文检索主要区别有以下几点:


  1、数据量


  传统全文检索系统面向的是企业本身的数据或者和企业相关的数据,一般索引库规模多在GB级,数据量大的也只有几百万条;但互联网网页搜索需要处理几十亿的网页,搜索引擎的策略都是采用服务器群集和分布式计算技术。


  2、内容相关性


  信息太多,查准和排序就特别重要,Google等搜索引擎采用网页链接分析技术,根据互联网上网页被链接次数作为重要性评判的依据;但全文检索的数据源中相互链接的程度并不高,不能作为判别重要性的依据,只能基于内容的相关性排序。


  3、安全性


  互联网搜索引擎的数据来源都是互联网上公开的信息,而且除了文本正文以外,其它信息都不太重要;但企业全文检索的数据源都是企业内部的信息,有等级、权限等限制,对查询方式也有更严格的要求,因此其数据一般会安全和集中地存放在数据仓库中以保证数据安全和管理的要求。


  4、个性化和智能化


  搜索引擎面向的是互联网访问者,由于其数据量和客户数量的限制,自然语言处理技术、知识检索、知识挖掘等计算密集的智能计算技术很难应用,这也是目前搜索引擎技术努力的方向;而全文检索数据量小,检索需求明确,客户量少,在智能化和个性可走得更远。


  搜索引擎与全文检索除了以上的区别外,还结合互联网信息的特点形成了三个不同的类型:


  全文检索搜索引擎:全文搜索引擎是名副其实的搜索引擎,国外具代表性的有Google (http://www.google.com) 、yahoo(http://search.yahoo.com) 、AllTheWeb (http://www.alltheweb.com ) 等,国内著名的有百度(http://www.Baidu.com)、中搜(http://www.zhongsou.com)。它们都是通过从互联网上提取的各个网站的信息(以网页文字为主)而建立的数据库,检索与用户查询条件匹配的相关记录,然后按一定的排列顺序将结果返回给用户,也是目前常规意义上的搜索引擎。


  目录搜索引擎:目录索引虽然有搜索功能,但在严格意义上算不上是真正的搜索引擎,仅仅是按目录分类的网站链接列表而已。用户完全可以不用进行关键词查询,仅靠分类目录也可找到需要的信息。国外比较著名的目录索引搜索引擎有yahoo(http://www.yahoo.com)Open Directory Project(DMOZ)(http://www.dmoz.com/)、LookSmart(http://www.looksmart.com)等。国内的搜狐(http://www.sohu.com)、新浪(http://www.sina.com)、网易(http://www.163.com)搜索也都具有这一类功能。


  元搜索引擎:元搜索引擎在接受用户查询请求时,同时在其它多个引擎上进行搜索,并将结果返回给用户。著名的元搜索引擎有Dogpile(http://www.dogpile.com)、Vivisimo(http://www.vivisimo.com)等,国内元搜索引擎中具代表性的有搜星搜索引擎(http://www.soseen.com/),优客搜索(http://www.yok.com)。在搜索结果排列方面,有的直接按来源引擎排列搜索结果,如Dogpile,有的则按自定的规则将结果重新排列组合,如Vivisimo。


  其他的像新浪(http://search.sina.com.cn)、网易(http://search.163.com)、A9(http://www.A9.com)等搜索引擎都是调用其它全文检索搜索引擎,或者在其搜索结果的基础上做了二次开发。


 


    搜索引擎的系统架构


  这里主要针对全文检索搜索引擎的系统架构进行说明,下文中提到的搜索引擎如果没有特殊说明也是指全文检索搜索引擎。搜索引擎的实现原理,可以看作四步:从互联网上抓取网页→建立索引数据库→在索引数据库中搜索→对搜索结果进行处理和排序。  


  1、从互联网上抓取网页


  利用能够从互联网上自动收集网页的网络蜘蛛程序,自动访问互联网,并沿着任何网页中的所有URL爬到其它网页,重复这过程,并把爬过的所有网页收集到服务器中。


  2、建立索引数据库


  由索引系统程序对收集回来的网页进行分析,提取相关网页信息(包括网页所在URL、编码类型、页面内容包含的关键词、关键词位置、生成时间、大小、与其它网页的链接关系等),根据一定的相关度算法进行大量复杂计算,得到每一个网页针对页面内容中及超链中每一个关键词的相关度(或重要性),然后用这些相关信息建立网页索引数据库。


  3、在索引数据库中搜索


  当用户输入关键词搜索后,分解搜索请求,由搜索系统程序从网页索引数据库中找到符合该关键词的所有相关网页。


  4、对搜索结果进行处理排序


  所有相关网页针对该关键词的相关信息在索引库中都有记录,只需综合相关信息和网页级别形成相关度数值,然后进行排序,相关度越高,排名越靠前。最后由页面生成系统将搜索结果的链接地址和页面内容摘要等内容组织起来返回给用户。


  下图是一个典型的搜索引擎系统架构图,搜索引擎的各部分都会相互交错相互依赖。其处理流程按照如下描述:


搜索引擎系统架构


  “网络蜘蛛”从互联网上抓取网页,把网页送入“网页数据库”,从网页中“提取URL”,把URL送入“URL数据库”,“蜘蛛控制”得到网页的URL,控制“网络蜘蛛”抓取其它网页,反复循环直到把所有的网页抓取完成。


  系统从“网页数据库”中得到文本信息,送入“文本索引”模块建立索引,形成“索引数据库”。同时进行“链接信息提取”,把链接信息(包括锚文本、链接本身等信息)送入“链接数据库”,为“网页评级”提供依据。


  “用户”通过提交查询请求给“查询服务器”,服务器在“索引数据库”中进行相关网页的查找,同时“网页评级”把查询请求和链接信息结合起来对搜索结果进行相关度的评价,通过“查询服务器”按照相关度进行排序,并提取关键词的内容摘要,组织最后的页面返回给“用户”。


 


  搜索引擎的索引和搜索


  对于网络蜘蛛技术和排序技术请参考作者其它文章[1][2],这里以Google搜索引擎为例主要介绍搜索引擎的数据索引和搜索过程。


  数据的索引分为三个步骤:网页内容的提取、词的识别、标引库的建立。


  互联网上大部分信息都是以HTML格式存在,对于索引来说,只处理文本信息。因此需要把网页中文本内容提取出来,过滤掉一些脚本标示符和一些无用的广告信息,同时记录文本的版面格式信息[1]。词的识别是搜索引擎中非常关键的一部分,通过字典文件对网页内的词进行识别。对于西文信息来说,需要识别词的不同形式,例如:单复数、过去式、组合词、词根等,对于一些亚洲语言(中文、日文、韩文等)需要进行分词处理[3]。识别出网页中的每个词,并分配唯一的wordID号,用于为数据索引中的标引模块服务。



  标引库的建立是数据索引中结构最复杂的一部分。一般需要建立两种标引:文档标引和关键词标引。文档标引分配每个网页一个唯一的docID号,根据docID标引出在这个网页中出现过多少过wordID,每个wordID出现的次数、位置、大小写格式等,形成docID对应wordID的数据列表;关键词标引其实是对文档标引的逆标引,根据wordID标引出这个词出现在那些网页(用wordID表示),出现在每个网页的次数、位置、大小写格式等,形成wordID对应docID的列表。


  关于索引数据的详细数据结构,有兴趣的朋友可以参看文献[4]。


  搜索的处理过程是对用户的搜索请求进行满足的过程,通过用户输入搜索关键字,搜索服务器对应关键词字典,把搜索关键词转化为wordID,然后在标引库中得到docID列表,对docID列表进行扫描和wordID的匹配,提取满足条件的网页,然后计算网页和关键词的相关度,根据相关度的数值返回前K篇结果(不同的搜索引擎每页的搜索结果数不同)返回给用户。如果用户查看的第二页或者第多少页,重新进行搜索,把排序结果中在第K+1到2*K的网页组织返回给用户。其处理流程如下图所示:


搜索引擎,全文检索


 


  搜索引擎细化趋势


  随着搜索引擎市场空间越来越大,搜索引擎也分得越来越细。互联网没有国界,百度总裁李彦宏所讲:搜索引擎市场是赢家通吃的市场。如果一个搜索引擎要想在搜索市场上有自己的一席之地,必须拥有自己的特色。而且,数以亿计的网民,搜索需求不可能都一样,不同类型的用户需要不同类型的搜索引擎,网页搜索只是搜索需求中的一种,这就决定了搜索引擎会不断细化,各具特色的搜索引擎也陆续出现。


  从技术上讲,各种搜索引擎都具有类似的系统架构,其不同在于搜索的数据源的不同。除了上面提到的网页搜索引擎以外,下面列举几个典型的搜索引擎:


  新闻搜索引擎


  看新闻是许多网民上网的主要目的,新闻搜索也就成了查看新闻的重要工具。新闻搜索引擎实现的过程比较简单,一般是扫描国内外有名的新闻网站,抓取新闻网页,建立自己的新闻数据库,然后提供搜索,只是对新闻网页抓取的频率要求很高,有的需要做到几分钟扫描一次。现在许多大型的网页搜索引擎都提供相应的新闻搜索功能,如:Google新闻搜索(http://news.google.com),中搜新闻搜索(http://news.zhongsou.com),百度新闻搜索(http://news.baidu.com)等。


  音乐搜索引擎


  有了互联网以后,音乐得到了广泛的传播,对于喜欢音乐的网民来说,音乐搜索引擎成了最钟爱的工具。音乐搜索引擎需要监控互联网上大型的音乐网站,抓取其音乐数据的描述信息,形成自己的数据库,音乐的下载和试听都会在其原来的音乐网站上进行。目前有:搜刮网(http://www.sougua.com),百度mp3搜索(http://mp3.baidu.com),1234567搜索(http://www.1234567.com)等。


  图像搜索引擎


  通过图像搜索引擎可以找到自己感兴趣的图片链接,各大搜索引擎也提供了图像搜索功能。图像文件本身不能够被搜索引擎索引,但搜索引擎可以通过链接文本分析和图片注解等得到图片的信息。目前有:Google图像搜索(http://images.google.com/),VisionNext搜索(http://www.eefind.com),百度图像搜索(http://images.baidu.com)等。


  商机搜索引擎


  电子商务一直是互联网的热点,商机搜索对电子商务的发展也起到了巨大的推动作用,商机搜索让互联网经济和传统经营紧密结合在一起,给传统的企业提供了一个新的销售模式。商机搜索引擎,通过抓取电子商务网站的商品信息和其他商业信息,给访问者提供统一的搜索平台。目前有:soaso价格搜索引擎(http://www.soaso.com),8848购物搜索(http://www.8848.com),阿里巴巴商机搜索(http://www.alibaba.com)等。


  其他特色的搜索引擎还有专利搜索、软件搜索、ftp搜索、游戏搜索、法律搜索等等。

中文搜索引擎技术揭密:中文分词


    信息的飞速增长,使搜索引擎成为人们查找信息的首选工具,Google、百度、中国搜索等大型搜索引擎一直是人们讨论的话题。随着搜索市场价值的不断增加,越来越多的公司开发出自己的搜索引擎,阿里巴巴的商机搜索、8848的购物搜索等也陆续面世,自然,搜索引擎技术也成为技术人员关注的热点。


    搜索引擎技术的研究,国外比中国要早近十年,从最早的Archie,到后来的Excite,以及altvista、overture、google等搜索引擎面世,搜索引擎发展至今,已经有十几年的历史,而国内开始研究搜索引擎是在上世纪末本世纪初。在许多领域,都是国外的产品和技术一统天下,特别是当某种技术在国外研究多年而国内才开始的情况下。例如操作系统、字处理软件、浏览器等等,但搜索引擎却是个例外。虽然在国外搜索引擎技术早就开始研究,但在国内还是陆续涌现出优秀的搜索引擎,像百度(http://www.baidu.com)、中搜(http://www.zhongsou.com)等。目前在中文搜索引擎领域,国内的搜索引擎已经和国外的搜索引擎效果上相差不远。之所以能形成这样的局面,有一个重要的原因就在于中文和英文两种语言自身的书写方式不同,这其中对于计算机涉及的技术就是中文分词。


    什么是中文分词


    众所周知,英文是以词为单位的,词和词之间是靠空格隔开,而中文是以字为单位,句子中所有的字连起来才能描述一个意思。例如,英文句子I am a student,用中文则为:“我是一个学生”。计算机可以很简单通过空格知道student是一个单词,但是不能很容易明白“学”、“生”两个字合起来才表示一个词。把中文的汉字序列切分成有意义的词,就是中文分词,有些人也称为切词。我是一个学生,分词的结果是:我 是 一个 学生。


    中文分词和搜索引擎


    中文分词到底对搜索引擎有多大影响?对于搜索引擎来说,最重要的并不是找到所有结果,因为在上百亿的网页中找到所有结果没有太多的意义,没有人能看得完,最重要的是把最相关的结果排在最前面,这也称为相关度排序。中文分词的准确与否,常常直接影响到对搜索结果的相关度排序。笔者最近替朋友找一些关于日本和服的资料,在搜索引擎上输入“和服”,得到的结果就发现了很多问题。下面就以这个例子来说明分词对搜索结果的影响,在现有三个中文搜索引擎上做测试,测试方法是直接在Google(http://www.google.com)、百度(http://www.baidu.com)、中搜(http://www.zhongsou.com)上以“和服”为关键词进行搜索:


    在Google上输入“和服”搜索所有中文简体网页,总共结果507,000条,前20条结果中有14条与和服一点关系都没有。在第一页就有以下错误:


    “通信信息报:瑞星以技术和服务开拓网络安全市场”


    “使用纯HTML的通用数据管理和服务- 开发者- ZDNet ...”


    “陈慧琳《心口不一》化妆和服装自己包办”


    “::外交部:中国境外领事保护和服务指南(2003年版) ...”


    “产品和服务”


    等等。第一页只有三篇是真正在讲“和服”的结果。


    在百度上输入“和服”搜索网页,总共结果为287,000条,前20条结果中有6条与和服一点关系都没有。在第一页有以下错误:


    “福建省晋江市恒和服装有限公司系独资企业”


    “关于商品和服务实行明码标价的规定”


    “青岛东和服装设备”


    在中搜上输入“和服”搜索网页,总共结果为26,917条,前20条结果都是与和服相关的网页。


    这次搜索引擎结果中的错误,就是由于分词的不准确所造成的。通过笔者的了解,Google的中文分词技术采用的是美国一家名叫Basis Technology(http://www.basistech.com)的公司提供的中文分词技术,百度使用的是自己公司开发的分词技术,中搜使用的是国内海量科技(http://www.hylanda.com)提供的分词技术。由此可见,中文分词的准确度,对搜索引擎结果相关性和准确性有相当大的关系。


    中文分词技术


    中文分词技术属于自然语言处理技术范畴,对于一句话,人可以通过自己的知识来明白哪些是词,哪些不是词,但如何让计算机也能理解?其处理过程就是分词算法。


    现有的分词算法可分为三大类:基于字符串匹配的分词方法、基于理解的分词方法和基于统计的分词方法。


    1、基于字符串匹配的分词方法


    这种方法又叫做机械分词方法,它是按照一定的策略将待分析的汉字串与一个“充分大的”机器词典中的词条进行配,若在词典中找到某个字符串,则匹配成功(识别出一个词)。按照扫描方向的不同,串匹配分词方法可以分为正向匹配和逆向匹配;按照不同长度优先匹配的情况,可以分为最大(最长)匹配和最小(最短)匹配;按照是否与词性标注过程相结合,又可以分为单纯分词方法和分词与标注相结合的一体化方法。常用的几种机械分词方法如下:


    1)正向最大匹配法(由左到右的方向);


    2)逆向最大匹配法(由右到左的方向);


    3)最少切分(使每一句中切出的词数最小)。


    还可以将上述各种方法相互组合,例如,可以将正向最大匹配方法和逆向最大匹配方法结合起来构成双向匹配法。由于汉语单字成词的特点,正向最小匹配和逆向最小匹配一般很少使用。一般说来,逆向匹配的切分精度略高于正向匹配,遇到的歧义现象也较少。统计结果表明,单纯使用正向最大匹配的错误率为1/169,单纯使用逆向最大匹配的错误率为1/245。但这种精度还远远不能满足实际的需要。实际使用的分词系统,都是把机械分词作为一种初分手段,还需通过利用各种其它的语言信息来进一步提高切分的准确率。


    一种方法是改进扫描方式,称为特征扫描或标志切分,优先在待分析字符串中识别和切分出一些带有明显特征的词,以这些词作为断点,可将原字符串分为较小的串再来进机械分词,从而减少匹配的错误率。另一种方法是将分词和词类标注结合起来,利用丰富的词类信息对分词决策提供帮助,并且在标注过程中又反过来对分词结果进行检验、调整,从而极大地提高切分的准确率。


    对于机械分词方法,可以建立一个一般的模型,在这方面有专业的学术论文,这里不做详细论述。


    2、基于理解的分词方法


    这种分词方法是通过让计算机模拟人对句子的理解,达到识别词的效果。其基本思想就是在分词的同时进行句法、语义分析,利用句法信息和语义信息来处理歧义现象。它通常包括三个部分:分词子系统、句法语义子系统、总控部分。在总控部分的协调下,分词子系统可以获得有关词、句子等的句法和语义信息来对分词歧义进行判断,即它模拟了人对句子的理解过程。这种分词方法需要使用大量的语言知识和信息。由于汉语语言知识的笼统、复杂性,难以将各种语言信息组织成机器可直接读取的形式,因此目前基于理解的分词系统还处在试验阶段。


    3、基于统计的分词方法


    从形式上看,词是稳定的字的组合,因此在上下文中,相邻的字同时出现的次数越多,就越有可能构成一个词。因此字与字相邻共现的频率或概率能够较好的反映成词的可信度。可以对语料中相邻共现的各个字的组合的频度进行统计,计算它们的互现信息。定义两个字的互现信息,计算两个汉字X、Y的相邻共现概率。互现信息体现了汉字之间结合关系的紧密程度。当紧密程度高于某一个阈值时,便可认为此字组可能构成了一个词。这种方法只需对语料中的字组频度进行统计,不需要切分词典,因而又叫做无词典分词法或统计取词方法。但这种方法也有一定的局限性,会经常抽出一些共现频度高、但并不是词的常用字组,例如“这一”、“之一”、“有的”、“我的”、“许多的”等,并且对常用词的识别精度差,时空开销大。实际应用的统计分词系统都要使用一部基本的分词词典(常用词词典)进行串匹配分词,同时使用统计方法识别一些新的词,即将串频统计和串匹配结合起来,既发挥匹配分词切分速度快、效率高的特点,又利用了无词典分词结合上下文识别生词、自动消除歧义的优点。


    到底哪种分词算法的准确度更高,目前并无定论。对于任何一个成熟的分词系统来说,不可能单独依靠某一种算法来实现,都需要综合不同的算法。笔者了解,海量科技的分词算法就采用“复方分词法”,所谓复方,相当于用中药中的复方概念,即用不同的药才综合起来去医治疾病,同样,对于中文词的识别,需要多种算法来处理不同的问题。


    分词中的难题


    有了成熟的分词算法,是否就能容易的解决中文分词的问题呢?事实远非如此。中文是一种十分复杂的语言,让计算机理解中文语言更是困难。在中文分词过程中,有两大难题一直没有完全突破。


    1、歧义识别


    歧义是指同样的一句话,可能有两种或者更多的切分方法。例如:表面的,因为“表面”和“面的”都是词,那么这个短语就可以分成“表面 的”和“表 面的”。这种称为交叉歧义。像这种交叉歧义十分常见,前面举的“和服”的例子,其实就是因为交叉歧义引起的错误。“化妆和服装”可以分成“化妆 和 服装”或者“化妆 和服 装”。由于没有人的知识去理解,计算机很难知道到底哪个方案正确。


    交叉歧义相对组合歧义来说是还算比较容易处理,组合歧义就必需根据整个句子来判断了。例如,在句子“这个门把手坏了”中,“把手”是个词,但在句子“请把手拿开”中,“把手”就不是一个词;在句子“将军任命了一名中将”中,“中将”是个词,但在句子“产量三年中将增长两倍”中,“中将”就不再是词。这些词计算机又如何去识别?


    如果交叉歧义和组合歧义计算机都能解决的话,在歧义中还有一个难题,是真歧义。真歧义意思是给出一句话,由人去判断也不知道哪个应该是词,哪个应该不是词。例如:“乒乓球拍卖完了”,可以切分成“乒乓 球拍 卖 完 了”、也可切分成“乒乓球 拍卖 完 了”,如果没有上下文其他的句子,恐怕谁也不知道“拍卖”在这里算不算一个词。


    2、新词识别


    新词,专业术语称为未登录词。也就是那些在字典中都没有收录过,但又确实能称为词的那些词。最典型的是人名,人可以很容易理解句子“王军虎去广州了”中,“王军虎”是个词,因为是一个人的名字,但要是让计算机去识别就困难了。如果把“王军虎”做为一个词收录到字典中去,全世界有那么多名字,而且每时每刻都有新增的人名,收录这些人名本身就是一项巨大的工程。即使这项工作可以完成,还是会存在问题,例如:在句子“王军虎头虎脑的”中,“王军虎”还能不能算词?


    新词中除了人名以外,还有机构名、地名、产品名、商标名、简称、省略语等都是很难处理的问题,而且这些又正好是人们经常使用的词,因此对于搜索引擎来说,分词系统中的新词识别十分重要。目前新词识别准确率已经成为评价一个分词系统好坏的重要标志之一。


    中文分词的应用


    目前在自然语言处理技术中,中文处理技术比西文处理技术要落后很大一段距离,许多西文的处理方法中文不能直接采用,就是因为中文必需有分词这道工序。中文分词是其他中文信息处理的基础,搜索引擎只是中文分词的一个应用。其他的比如机器翻译(MT)、语音合成、自动分类、自动摘要、自动校对等等,都需要用到分词。因为中文需要分词,可能会影响一些研究,但同时也为一些企业带来机会,因为国外的计算机处理技术要想进入中国市场,首先也是要解决中文分词问题。在中文研究方面,相比外国人来说,中国人有十分明显的优势。


    分词准确性对搜索引擎来说十分重要,但如果分词速度太慢,即使准确性再高,对于搜索引擎来说也是不可用的,因为搜索引擎需要处理数以亿计的网页,如果分词耗用的时间过长,会严重影响搜索引擎内容更新的速度。因此对于搜索引擎来说,分词的准确性和速度,二者都需要达到很高的要求。目前研究中文分词的大多是科研院校,清华、北大、中科院、北京语言学院、东北大学、IBM研究院、微软中国研究院等都有自己的研究队伍,而真正专业研究中文分词的商业公司除了海量科技以外,几乎没有了。科研院校研究的技术,大部分不能很快产品化,而一个专业公司的力量毕竟有限,看来中文分词技术要想更好的服务于更多的产品,还有很长一段路。

羊群效应:搜索引擎暗规则之四

羊群效应:搜索引擎暗规则之四

“羊群效应”是指管理学上一些企业的市场行为的一种常见现象。例如一个羊群(集体)是一个很散乱的组织,平时大家在一起盲目地左冲右撞。如果一头羊发现了一片肥沃的绿草地,并在那里吃到了新鲜的青草,后来的羊群就会一哄而上,争抢那里的青草,全然不顾旁边虎视眈眈的狼,或者看不到其它还有更好的青草。

羊群效应的出现一般在一个竞争非常激烈的行业上,而且这个行业上有一个领先者(领头羊)占据了主要的注意力,那么整个羊群就会不断摹仿这个领头羊的一举一动,领头羊到哪里去吃草,其它的羊也去哪里淘金。

搜索引擎这个市场上的竞争加剧的情况下,搜索引擎的巨头之间对对手之间的动作非常的注意,一旦一个搜索引擎出现某一个新的功能,或者进入一个新的领域,众多的搜索引擎都会去尝试这个新的“水草地”是否真的肥美,是否真得有利可图。

在搜索引擎行业上,2002年以来Google确立了领头羊的位置,因此这只羊的一举一动都成了大家摹仿的标本:

1]
2003 Google 收购 blogger.com ,进入blog市场
MSN 2004 年底发布 MSN Space 进入blog市场
Yahoo 2005 发布 Yahoo 360

2]
Google 收购 Picasa
Yahoo 收购 flickr

3]
Yahoo 拥有 Map
Google 发布 Map 服务

4]
几乎同时 Yahoo 推出 video search
Google 也立刻发布了video search

5]
2004 Google desksearch
2004 Msn Desksearch
2005 Baidu desksearch
2005 Yahoo Desksearch

6]
Gmail 提供 1 G 的空间
Yahoo 扩容
Msn 扩容至250M

7]
google 的 adwords 赚了钱
现在已经成为一种标准的模式

这样的例子举不胜据,还有例如API的公开等,那么羊群效应到底是好还是坏呢?

正:羊群效应是一种减少研发和市场调研的一种策略,现在被广泛的应用在各个行业上,也叫做“Copy Strategy(复制原则)”。当一个公司通过调研和开发而投放市场的产品,会被对手轻易的复制而免去前期的研发成本,是加剧竞争的一个来源之一。

反:羊群效应更多带来的是盲目上马的项目和没有经过充分的市场调研而导致的模糊的前景,甚至会分散一个公司的精力。正所谓,没有免费的午餐,把握好羊群效应带来的利弊才能做成正确的决策,选择肥沃的草地。

让我们随着羊群效应来预测一下未来几个“必然”的事件吧:

1] Google 会进入个人门户(Personal Portal)
这个是跟随 Msn Space 和 Yahoo 360 后对blogger和现有的多种服务例如Picasa,gmail等整合在一起,加强这方面的竞争。

2] Msn 会将 Desksearch 集成在新的OS里。(现有的Windows的search不是基于搜索引擎技术的Desksearch)

3] Baidu 和 MSN 都会推出类似 Google Adsense 网页相关广告的业务。

4] Msn 和 Yahoo 会提供 音频和视频blog。

相信还会有很多的事情会发生,羊群效应,正是在培养这样一个趋同,却又激烈竞争的群体。

破窗理论:搜索引擎暗规则之三

破窗理论:搜索引擎暗规则之三

车东在Google的“破窗”里提到Spam会传播更多的Spam,这个就是搜索引擎破窗效应的体现。

首先解释一下什么是破窗理论:

"破窗理论"体现的是细节对人的暗示效果,以及细节对整体影响的扩展性

  美国心理学家詹巴斗曾经做过一个"偷车实验 ",将两辆一模一样的轿车分别放在一个环境很好的中产阶级社区和环境比较脏乱的贫民区,结果发现贫民区的车很快被偷走了,而另一辆几天后仍然完好无损;如果将中产阶级社区的那辆车的天窗玻璃打破,几个小时后,那辆车也被偷了。

  后来,在此实验基础上,美国政治学家威尔逊和犯罪学家凯林提出了有名的"破窗理论":如果有人打坏了一栋建筑上的一块玻璃,又没有及时修好,别人就可能受到某些暗示性的纵容,去打碎更多的玻璃。

对于搜索引擎来说,前面chedong的例子已经非常明显,随着更多的人认识到搜索引擎可以获取更多的利益,不法之徒当然回去尝试采用各种作弊手段进行扰乱正常的拍名和搜索结果,如果搜索引擎不即时制止, 这种情况就会泛滥,最好到严重的要影响到用户使用的地步的时候再来处理也就要麻烦的多和面临更多的作弊者。
看这个搜索结果Google 里搜索 天下无贼免费电影下载,你完全可以想像这样的结果是对Spam的一种变相的鼓励。

破窗理论的推论就是注重细节,防微杜渐。一旦有第一个spam出现,即时的制止,那么后面的spam也许就不会那么轻易的去搜索引擎上尝试,因为毕竟敢于做第一(第一个打破窗户)的人还是少数。

看看Baidu和Yahoo,在破窗上封堵的就比google快一些,因此基本上现在能够看到的spam基本上都来自google的结果。

google adsense呢?我以前曾经写过几篇关于fraud click(点击欺骗)的文章,fraud click的破窗随着google去年把修斯顿的一家公司推上了法庭而弥补好了。可是另外一种更严峻的破窗又出现了,直接危机到google adsense的存亡:广告内容作弊。

我看到几乎一半的中国的小网站刊登google adsense的时候都在作弊,这种利用iframe 的嵌套的作弊,然后调用另外的一段js代码从而刊登出与原文完全不相关的广告,例如房屋贷款,信用卡申请等价格较高的adsense,从我第一次注意到大约在2个月前到现在已经泛滥成灾。

破窗,一个小的缺口,几滴渗漏的河水,也许会冲垮一个帝国。

20 80 法则:搜索引擎暗规则之二

20 80 法则:搜索引擎暗规则之二

20/80法则也叫二八定律,即巴莱多定律。巴莱多定律是19世纪末20世纪初意大利经济学家巴莱多发明的。他认为,在任何一组东西中,最重要的只占其中一小部分,约20%,其余80%尽管是多数,却是次要的,因此又称二八定律。

在搜索引擎的设计上,可以说大多数的搜索引擎在搜索结果上第一页有80%的结果是相同的,不同的只有20%。可是正是这20% make different.

这20%的差别也是不同搜索引擎之间的差距,能够完善这20%将对一个搜索引擎来说是至关重要的。

在另外一方面,对于一个搜索引擎的完善,例如你可以花20%的努力做到80%的效果,而要想提高剩下的20%的效果也许要耗费80%的精力。

一个简单的例子,一个搜索引擎现在还比较初级,也许分词和搜索相关的准确率在80%左右,想要进一步提高,也许要调整40,50个参数,调整很多方面的因子,考虑很多的算法,花了极大的精力后发现只提高了一点,再调整,如此三番,也许很久一段时间后发现有90%了,这个10%就是大的突破,但是要花去的努力要比从0%到80%大的多的多。

搜索引擎各项参数的差距也许只有几个百分点,可是这几个百分点就是真正的差别,就真得创造了胜者和失败者。

从搜索的方面看,20 80 法则有另外的意义,80%的搜索集中在20%的少量关键词上,也就是说有20%的搜索词汇占了80%的总体搜索流量。搜索的词频可以通过搜索引擎的每天的统计得到,在某一些特殊的情况,这些分布可以用来对搜索引擎进行非常好的矫正工作。

马太效应:搜索引擎暗规则之一

搜索引擎暗规则序

中国人有句古话叫做:“学以至用”。若干年我一直对这句话封为神灵,因为只用致用才能看到学的效果,了解到学的不足,享受到学的快乐。

搜索引擎在过去两年的发展并没有大突破,但是却逐步走向成熟,走向商业。正是这两年的时间,部分的业余时间都用来做一些开发和研究,当我逐渐了解的更多的时候,也是我结束单枪披马的时候。未来的兴趣也许会转向分类技术,因此希望能够用这个系列为这段成长岁月化上句号。

所谓暗规则,其实就是一些生活中非常显然的规则,只不过在搜索引擎这个领域表现的不是那么的突出,之所以不够突出,原因最主要的是因为搜索引擎还在发展,市场还在进一步加剧竞争,这些暗规则或多或少已经在反映在现在的搜索引擎上了。相信有一天,这些规则会被看到的更清晰,更深入。


搜索引擎暗规则之一

《新约•马太福音》中有这样一个故事,一个国王远行前,交给三个仆人每人一锭银子,吩咐他们:"你们去做生意,等我回来时,再来见我。"国王回来时,第一个仆人说:"主人,你交给我们的一锭银子,我已赚了10锭。"于是国王奖励他10座城邑。第二个仆人报告说:"主人,你给我的一锭银子,我已赚了5锭。 "于是国王例奖励了他5座城邑。第三个仆人报告说:"主人,你给我的一锭银子,我一直包在手巾里存着,我怕丢失,一直没有拿出来。"于是国王命令将第三个仆人的一锭银子也赏给第一个仆人,并且说:"凡是少的,就连他所有的也要夺过来。凡是多的,还要给他,叫他多多益善。"

这就是马太效应。

简单的说,就是让富有者更加富有,让贫穷者更加贫穷。

对于搜索引擎来说,马太效应是一条暗规则。10年的发展期过后,google,yahoo等已经成为市场的绝对主流,利润的分享趋向与这些超大型的搜索引擎,而不是中小型的搜索引擎。

例如,你可以制作一个搜索引擎,流量是google的1/1000(这个数目并不大,每天的独立IP是大约6万,PageView是30万,大约和3,4个 booso.com 的规模大不了多少。),但是你拿到的回报和这个搜索引擎的价值绝对没有google的1/1000(这个大约是6000万美金,或者5亿人民币)。

马太效应还能反应在市场倾向于分化,倾向于使得市场的份额集中在一两个巨头的身上。因为市场对领头羊的投入会拿到比例更高的回报,因此广告/合作等的机会也会遵从马太效应,成为领头羊的东风。

马太效应还表现在当搜索引擎成为领头羊的时候,能够聚合更多的资金人力扩展这个优势,例如启动一系列的其它的业务Mp3,Pic search,Vedio等,巩固和扩大优势。

那么对于中小型的搜索引擎而言,价值在哪里呢?

市场是残酷的,可以预言现在大多数的中小型搜索引擎从经济的角度讲都要灭亡,能够保留下来的一定不会是单纯的搜索引擎,也许是被某一个网站收购成为其附加值,也许转向做企业内部搜索等现在市场还没有显著分化的领域。

2月14日

Google的启示

    最近在对现有的搜索引擎进行分布式的改进,回顾以前阅读过的Google file system 的文章时发现Google的思维和我们平时固守的思维很不一样,可以说很多在我们看来是有一些“偏激”的,可是正是由于这些偏激,才导致Google与其跟随者的不同。
以下为几个例子:
    1、 Google认为, 所有的硬件都是容易产生故障的, 因此Google认为故障是必然的,不产生故障才是偶然现象。这个想法和我们通常的意识是相反的。
    2. Google认为,一旦写入,再也不删除和修改。这点上Google认为修改和删除会对系统造成潜在的伤害,例如文件的不连续性,文件定位的困难。
    3. Google将Linux的 file system的block更改为 64M,也就是说,写文件的最小单元是64M,而不是我们通常的512字节,两者整整相差了128000倍。
    4. Google认为修复是没有必要的,当一个服务器出现问题的时候,撤下来,换上另外一个Google unit(Google单元)即可,因为维修的成本远远大于直接上线一个全新的服务单元的成本。说来容易,其实只有当Google结构真正实现高冗余和分布式这样的操作才可行,而这些正是Google的核心。
    当我们设计一个系统的时候,我们最简单的做法通常是会根据需求对已有的一些经验进行匹配,这个过程中我们通常走的是近路,而且我们的经验常常会束缚我们的想法,没有抛开经验进行全新的分析和设计,也自然就难以有所创新。
2月12日

元宵夜

外面起雾了,好像很冷。到处是鞭炮声,远处还不时有礼花在空中绽放,告诉我现在是节日。元宵节的夜晚,要快乐!
明天,一个全新的开始……


2月10日

中国公司如何在美国上市?


      很多人都把“上市”和“首次公开发行”(IPO)混为一谈。IPO最为人所知的是能在转眼间将辛苦奋斗的企业家变成百万富翁。但实际上,IPO只是您踏上华尔街的五种途径之一,而且可能还不是最佳的一种。IPO、DPO、买壳上市……到底哪一种最适合您呢?
       首次公开发行(IPO)
       IPO应该是最广为人知的一个上市途径,但也是最贵、风险最高、需时最长、消耗企业最多资源的途径。大部分的IPO都需要一年或以上的时间才能完成。在过程中,除了消耗管理层许多的精力,使他们无法专注于公司的业务发展外,公司还需要投入数十万,甚至上百万美元的律师费、会计师费等,但却不能保证能够成功上市。即使在成功上市后,承销商的包消费也高达融资总额的3-10%。
       IPO的成功与否很大程度上取决于承销商的能力和当时的市场状况。承销商随时会因为市场变化而撒手立场,对一家公司的现金流及士气有极大的影响。IPO最适合于根基稳固、资金雄厚和知名度高的公司。 当然,IPO的好处是在成功上市后,公司可以立即获得新的资金。
       自我公开发行(DPO)
       DPO是小型版的IPO,最适合于尚未达到上述IPO标准的小型的公司。DPO又被称为“自助式”的IPO,公司只需用简化的步骤及表格向监管机构登记发行股票,并且可以自己负责包销,直接向公司的客户、供应商、员工等出售股票。不像在IPO时,公司必须经过承销商将股票出售予承销商的客户。由于不需要承销商的介入,DPO的融资规模一般较IPO的为小,成本也要比IPO的为低。可是,DPO在登记发行和推广公司股票方面需要花费管理层许多的精力。
       根据证券交易法登记
       如果公司已经拥有一定的股东群体,可以根据证券交易法登记成为纳斯达克(NASDAQ)柜台交易行情布告板(OTC BB)的上市公司。OTC BB并不是一个交易所,而是一个电子报价系统,为全世界的经纪及投资者提供公司的报价。公司只需完成OTC BB的等级和符合其它上市条件,再加上市场庄家的推动,原本不流通的公司股票就成为了能在公开市场上交易的股票了。 
       由于成为OTC BB上市公司的成本较低,很多公司都采用这种方法作为其在公开股票市场上市的前奏。其他的好处包括为早期投资者提供股票流通性、吸引专业分析员的注意力和有利于公司的资本运作。
       买壳上市
       在美国,有许多熟悉上市法规及企业重组的专业人士专门收购一些经营不良的上市公司,并聘请律师与会计师处理该公司的法律及财务问题,使其成为一家除了上市公司的结构和状态外一无所有的“空壳公司”。我们可以通过收购一家空壳公司的股权来获得它的上市地位,同时将原有公司的业务及管理层转移至上市公司(即空壳公司),借此完成上市。一般来说,我们需要取得空壳公司80-95%的股权。
       买壳上市最适合于不急需资金的中小型企业,因为它并不能为公司带来新的资金。公司需要在完成买壳后,通过新闻发布、分析员评论及相关的造市活动来推动股票,以增资发股在二级市场为公司融集资金。但是,由于不需要经过漫长的登记和公开发行的手续,买壳上市大约只需要四个月的时间便可以完成,而成本方面也只需要50-60万美元左右。买壳上市最大的好处是可以100%保证成功上市,避免了在花费了高昂的费用后上市计划却因为许多不可预知的因素而流产的风险。
       分拆上市
       一家非上市公司可以通过向一家上市公司增发新股来达到上市的目的。所增发的新股首先需要向美国证券交易委员会进行登记,再由上市公司以放股利的形式将该新股发放给股东,将其“分拆”。在完成上述过程后,原本的非上市公司已拥有了原上市公司的股东群体,加上市场庄家的推动,“分拆”上市的公司就成为了一家拥有流通股的新的上市公司了。
       在上述的五种方法中,最广为中国公司采用的是IPO和买壳上市。采用IPO的有早期的华晨汽车、玉柴机械等,到科技股蓬勃时的UT斯达康、亚信、新浪等。但在近年,利用买壳上市的例子越来越多,其中包括山东龙凤集团、浙江万向集团、深圳明达汽车、四川电器、深圳雅图等。主要的原因还是在上市所牵涉到的成本、时间及风险的考虑上。
       相对于IPO,买壳上市无论是在时间或成本上都较为经济,风险也较低:
       ●  买壳上市不需要经过漫长的登记和公开发行的手续,因此大约只需要四个月的时间就够了。IPO需要的时间则较长,通常要一年或以上才能完成。
       ●     买壳上市的费用要比IPO的费用低得多。通常IPO的前期费用至少要100万美元。买壳上市只需支付买壳费及股票推动费等其它杂费。据我们了解,一般空壳公司的售价,如果是OTCBB市场上的上市公司,售价为30 - 35万美元;如果是OTC – PS市场上的公司,售价为15 – 18万美元。再加上其它有限的费用,买壳上市只需要50-60万美元便可完成。
       ●  买壳上市不会受承销商的能力及市场状况的影响。IPO有时因为承销商推广不力或市场疲弱而遭遇困难,导致公司长时间无法上市。买壳上市则没有这个问题,可以100%保证成功上市。
       ●     IPO需要公司拥有一定的经营历史和业绩。相反的,不具备任何盈利历史的公司也能通过买壳上市而取得上市公司的地位。
       ●     IPO是将“上市”和“融资”同时进行,而买壳上市则是将“上市”和“融资”分开进行。将“上市”和“融资”分开进行大大简化了买壳上市的程序,但却不能为公司直接带来新的资金。公司在IPO时有一定的估价,一旦完成上市过程及股票发行,公司就可以获得预期的资金。买壳上市需要在过后通过新闻发布、分析师评论及相关的造市活动来推动股票,以增资发股在二级市场为公司融集资金,融资额则取决于当时股票市场的表现。
        既然买壳上市不能为公司直接带来新的资金,公司在花费数十万美元完成买壳上市后,又该如何进行融资呢?
       一家上市公司(包括买壳上市的公司)可以用下列方式为公司融集资金:
       ●  向投资者以低于公开市场价的价格发售新流通股,新股发售价约在公司股票过去20天平均市场价的90-95%之间。基本上,公司在上市20天后就可以借此获得第一笔资金。
       ●  在市场允许的情况下,进行二次上市,融资规模将取决于当时的市场状况。
       ● 发售可转换债券,转换价一般低于股票的平均市场公开价,而转换也可以分期进行。
       无论是采用上述哪一种方式,股票的流动性及市场价格都是至关紧要的。因此,投资者关系是不可忽略的一环。我们需要聘用一家公关公司为公司制定一套宣传方案,以提高个人及机构投资者对公司的认识,获得投资者的广泛认同,促进公司与投资者之间的良性沟通,从而推动公司股票在二级市场的价格及流通性。
       当然,在美国上市也必须遵守美国的上市法规,承担上市公司的责任。尤其是在发生了接二连三的财务丑闻之后,美国政府制定了更严格的公司责任法,对财务报告和信息披露有了更严厉的要求,以改进上市公司的透明度。
       一般来说,美国上市公司应承担的责任包括:
       ●  按季度编制未经审计的财务报告
       ●  编制年度审计报告
       ●  向美国证券交易委员会披露所有重大事项
       ●  披露公司所有管理人员、董事及其它内幕人士所拥有股票的变动情况
      承担上述责任的成本依赖于公司所雇佣的专业机构的水平及公司业务的规模的复杂性,一般包括:
       ●  财务报告的季度复核和年度审计费用(可以由公司现任审计师来进行估算)
       ●  每一次向美国证券交易委员会进行申报的律师费(一般每年不超过7,500美元)
       ●  每一次向美国证券交易委员会进行申报的文件归档费(一般每年大约需要4,500美元)
       ●  向证券交易所缴纳的股票交易费
       ●  聘用公关公司向投资者进行宣传的公关费
       如果您正在考虑公司在美国上市的事宜,现在就应该开始制定一个详尽的计划,并采取相应的步骤,为公司的资本运作跨出迈向国际化的第一步。
       成为上市公司前必须完成的八个步骤:
       ●  编制详尽的商业计划书
       ●  编制详尽的财务模型
       ●  挑选专业的顾问团队
       ●  聘用一家公关公司
       ●  聘用有信誉和经验丰富的外部董事
       ●  将公司的管理层结构正规化
       ●  加强内部财务报告的体系
       ●  完成财务报表的年度审计

关于乳房的审美权利

    由于我受的教育和看见的事情,老实说,迄今不能完全理解卢索说的“天赋人权”是什么意思。实际上,凡是别人说什么公共生活领域的权利是“天赋”的,我都无法心悦诚服。我说这话,是依据我受的教育和我看见的事情,所以一点也不害臊。即使卢索他老兄有机会炸尸,直不楞登地站在我面前和我辩论,我也不怕。

    但是也有一个唯一例外,就是我们作为男人,有一个权利是天生的,就是欣赏女人的乳房。不承认的就是伪君子。凡是伪君子,不足与语。按照社会主义市场经济条件下的乳房运行规则,可欣赏的乳房大约分为四种:老婆或女朋友的,情人的,妓女的,和女明星的。其他的,想欣赏也不大容易欣赏。万一欣赏错了要动刀子或者赔银子。各位如果是男人一定同意我的观点。即使是女人也没关系,如果阁下的乳房过去、现在和将来都没有人欣赏,那阁下就完蛋了。为严谨起见,我还要特别声明,我对玻璃和拉拉都没有任何个人意见,而我认识的全体玻璃和拉拉,他们对不靠谱的尼安德特人灭绝后现代人类进化最伟大成就之一的女性乳房,都是有很大好感的。

    由于天赋人权是扯淡,因此,我们唯一天赋的权利,也就是由衷欣赏和赞美女性乳房的权利,就显得无比珍贵。任何一种欣赏,如果要达到较高境界,都需要有一种纯真心态。从操作层面上看,纯真心态将导致无法预见的大量原创性审美运作,这是纯真为什么可贵的一个重要原因。从动机层面看,作为男人,我们欣赏女人的乳房,一定是因为源自我们作为人类的本能,以及基于此的审美追求。倘若我们欣赏女性的乳房是为了捞取什么,那这个欣赏就堕落为扯淡。同理,如果女性乳房的被欣赏,如果是为了捞取什么,那这个被欣赏,也就堕落为扯淡,并且严重连累我们男人,一起被迫堕落为扯淡。这进一步说明,妓女扒光了上身被人欣赏,在吃饭层面上可以理解,在审美层面上就扯淡。女明星扒光了上身,基本上因为其功利目的,而在审美上堕落为扯淡。中华民族到了有清一代,戏子见了妓女要行礼。说明我民族在乳房审美的纯真性上,有分类学方面的先见之明。

    基于以上严谨逻辑,我们必须声讨一种错误行径。我在N久以来,不只一次地看到一类瞎编出来的故事,破坏我们欣赏女性乳房这一神圣权利的纯洁性,亵渎良家妇女以多姿多彩的乳房被男性纯真欣赏的神圣权利。这些故事不止一次地让我泛堵,直到恶心得实在不行,不吐不快了。

    这些不靠谱的故事的基本版本就是,在前世纪下半叶中越战争中,出征将士即将赴死,首长做了慷慨激昂的号召,并为大家有什么要求。于是战士中一名处男说,要看女人的乳房。于是,有的版本是一名女医生毅然扒光了上身。有的版本是,一名女护士毅然扒光了上身。有的版本是,一名女记者毅然扒光了上身。有的版本是,一名慰问演出的女演员毅然扒光了上身。总之,有一名良家妇女就露出乳房来给大家看。然后,部队士气高涨,将越南鬼子打得犀利哗啦。最后的结局,该名提出看乳房的处男战士战死沙场。云云。

    我本人在部队的时候,已经是农村包产到户、城市性解放之后N年。即使时代已经充分进步,也无法设想在战前动员中,战士可以跳出来要求看女人乳房。简直是天方夜谈。这个问题,在N久前,埃德嘉斯诺与毛主席关于红军建设的对话中,就已经有定论。而毛主席的建军思想,是从来没有改变过的。

    实际上,这么多年来,《读者》和《知音》经常编一些类似的甜丝丝的故事出来,在世道良心的层面,确实给血糖低的群众营造了和谐庸懒的氛围,就算做一种低收费的心理保健,也无可厚非。对此,我们姑且可以放下“可以正视淋漓的鲜血,可以直面惨淡的人生”这样的骠悍追求,不说什么。但是以上这些故事,直接侵犯了我们唯一的天赋权利,就是纯真欣赏女人乳房,尤其是欣赏女人处于人类进化的审美和繁殖本能,纯真地交出乳房来给男人欣赏这一无比优美的天赋权利,因此实在是恶劣之极,极为恶劣。

    女人拿出自己的乳房来,利己地讲,是因为自恋;利他地讲,是为悦那该悦之人。这两个动机,因为纯真,所以可取。因为纯真,所以优美。因为这些可取和优美,所以私奔万岁,所以成都人迄今还郑重修了文君楼,吃卓文君姐姐的饭。此外,不论是为了赚钱,为了换馒头,还是为了革命,为了七国争霸,都是一类,都是扯淡的搞法。打越南鬼子是革命,革命当然没错。虽然咱们不知道红色高棉到底在柬朴寨干了什么,不知道柬朴寨人对越南人是什么感觉,不知道前世纪七十年代东南亚是怎么回事,但是我军和敌军开打,当然向着我军。不过,让一名女人露出乳房,是在这个背景下,为了这个目的,就大倒胃口,太不应该。

    要说这个传统已经很悠久,不是刚来的发明。比如,西元前几百年,浙江的范蠡对自己的情人西施说,为了大王勾践的事业,你主动去向咱们的大对头夫差露出乳房吧。几千年来的大部分时候,一个有教养的男人在洞房的时候,面对一个从来没见过的女人,多半会说,不好意思,为了贯彻父母之命,媒妁之言,请您把乳房露出来吧。前世纪40年代,在西北一个小镇,组织上会找上海和北平来的进步女青年谈话,说为了革命,你需要向38年参加革命的现任团职干部某某露出乳房。20多年后,组织上安排8000名湖南女人去某边远地区,向驻扎在那里的完全陌生的8000名军人露出乳房。以上事件,如果说伟大,我没有意见。但是从审美意义上说,我就倒胃口的很。

    现在,我们国家在作战飞机数量上已经排世界第一了;在GDP总数上也早就TOP10了;已经不是很需要把良家妇女的乳房拿来促革命促生产了。除了部分地区确实认为开放性产业有利于招商引资,决心“牺牲一代少女乳房”之外,也有相当部分地区不靠这个吃饭了。所以,编故事的人可以反省一下自己,建议可以放弃冷战思维,别再拿女孩子的乳房激励革命斗志了。

    历史和实践反复证明,不论一个人还是一伙人,如果在拳头上堕落了,还能翻本。要是在审美上堕落了,那就彻底完蛋了。因此,要尊重男性以纯真心态欣赏女性乳房的天赋权利,要尊重女性以纯真心态把乳房拿出来给男人欣赏的天赋权利。那些编造不靠谱的伪煽情故事,拿女孩子的乳房激励革命斗志的人,如果为了讨好组织或者赚银子,那建议改行。如果是为了自娱自乐或者欺骗群众,那就纯粹是傻cunt无疑。

2月9日

ASP调用存储过程访问SQL Server


     存储过程就是作为可执行对象存放在数据库中的一个或多个SQL命令。
     定义总是很抽象。存储过程其实就是能完成一定操作的一组SQL语句,只不过这组语句是放在数据库中的(这里我们只谈SQL Server)。如果我们通过创建存储过程以及在ASP中调用存储过程,就可以避免将SQL语句同ASP代码混杂在一起。这样做的好处至少有三个:
     第一、大大提高效率。存储过程本身的执行速度非常快,而且,调用存储过程可以大大减少同数据库的交互次数。
     第二、提高安全性。假如将SQL语句混合在ASP代码中,一旦代码失密,同时也就意味着库结构失密。
     第三、有利于SQL语句的重用。
   
     在ASP中,一般通过command对象调用存储过程,根据不同情况,本文也介绍其它调用方法。为了方便说明,根据存储过程的输入输出,作以下简单分类:
     1. 只返回单一记录集的存储过程
     假设有以下存储过程(本文的目的不在于讲述T-SQL语法,所以存储过程只给出代码,不作说明):
    /*SP1*/
    CREATE PROCEDURE dbo.getUserList
    as
    set nocount on
    begin
       select * from dbo.[userinfo]
    end
    go
    以上存储过程取得userinfo表中的所有记录,返回一个记录集。通过command对象调用该存储过程的ASP代码如下:
   
    '**通过Command对象调用存储过程**
    DIM MyComm,MyRst
    Set MyComm = Server.CreateObject("ADODB.Command")
    MyComm.ActiveConnection = MyConStr          'MyConStr是数据库连接字串
    MyComm.CommandText     = "getUserList"      '指定存储过程名
    MyComm.CommandType     = 4                      '表明这是一个存储过程
    MyComm.Prepared            = true                  '要求将SQL命令先行编译
    Set MyRst = MyComm.Execute
    Set MyComm = Nothing
    存储过程取得的记录集赋给MyRst,接下来,可以对MyRst进行操作。
    在以上代码中,CommandType属性表明请求的类型,取值及说明如下:
      -1   表明CommandText参数的类型无法确定
      1    表明CommandText是一般的命令类型
      2    表明CommandText参数是一个存在的表名称
      4    表明CommandText参数是一个存储过程的名称
   
    还可以通过Connection对象或Recordset对象调用存储过程,方法分别如下:
    '**通过Connection对象调用存储过程**
    DIM MyConn,MyRst
    Set MyConn = Server.CreateObject("ADODB.Connection")
    MyConn.open MyConStr                            'MyConStr是数据库连接字串
    Set MyRst  = MyConn.Execute("getUserList",0,4)  '最后一个参断含义同CommandType
    Set MyConn = Nothing
    '**通过Recordset对象调用存储过程**
    DIM MyRst
    Set MyRst = Server.CreateObject("ADODB.Recordset")
    MyRst.open "getUserList",MyConStr,0,1,4
    'MyConStr是数据库连接字串,最后一个参断含义与CommandType相同
   
    2. 没有输入输出的存储过程
    请看以下存储过程:
    /*SP2*/
    CREATE PROCEDURE dbo.delUserAll
    as
    set nocount on
    begin
       delete from dbo.[userinfo]
    end
    go
    该存储过程删去userinfo表中的所有记录,没有任何输入及输出,调用方法与上面讲过的基本相同,只是不用取得记录集:
    '**通过Command对象调用存储过程**
    DIM MyComm
    Set MyComm = Server.CreateObject("ADODB.Command")
    MyComm.ActiveConnection = MyConStr          'MyConStr是数据库连接字串
    MyComm.CommandText      = "delUserAll"      '指定存储过程名
    MyComm.CommandType      = 4                 '表明这是一个存储过程
    MyComm.Prepared         = true              '要求将SQL命令先行编译
    MyComm.Execute                              '此处不必再取得记录集
    Set MyComm = Nothing 
    当然也可通过Connection对象或Recordset对象调用此类存储过程,不过建立Recordset对象是为了取得记录集,在没有返回记录集的情况下,还是利用Command对象吧。

    3. 有返回值的存储过程
    在进行类似SP2的操作时,应充分利用SQL Server强大的事务处理功能,以维护数据的一致性。并且,我们可能需要存储过程返回执行情况,为此,将SP2修改如下:
    /*SP3*/
    CREATE PROCEDURE dbo.delUserAll
    as
    set nocount on
    begin
       BEGIN TRANSACTION
       delete from dbo.[userinfo]
       IF @@error=0
          begin
             COMMIT TRANSACTION
             return 1
          end
       ELSE
          begin
             ROLLBACK TRANSACTION
             return 0
          end        
       return
    end
    go
    以上存储过程,在delete顺利执行时,返回1,否则返回0,并进行回滚操作。为了在ASP中取得返回值,需要利用Parameters集合来声明参数:
    '**调用带有返回值的存储过程并取得返回值**
    DIM MyComm,MyPara
    Set MyComm = Server.CreateObject("ADODB.Command")
    MyComm.ActiveConnection = MyConStr          'MyConStr是数据库连接字串
    MyComm.CommandText      = "delUserAll"      '指定存储过程名
    MyComm.CommandType      = 4                 '表明这是一个存储过程
    MyComm.Prepared         = true              '要求将SQL命令先行编译
    '声明返回值
    Set Mypara = MyComm.CreateParameter("RETURN",2,4)
    MyComm.Parameters.Append MyPara
    MyComm.Execute
    '取得返回值
    DIM retValue
    retValue = MyComm(0)    '或retValue = MyComm.Parameters(0)
    Set MyComm = Nothing
   
    在MyComm.CreateParameter("RETURN",2,4)中,各参数的含义如下:
    第一个参数("RETURE")为参数名。参数名可以任意设定,但一般应与存储过程中声明的参数名相同。此处是返回值,我习惯上设为"RETURE";
    第二个参数(2),表明该参数的数据类型,具体的类型代码请参阅ADO参考,以下给出常用的类型代码:
    adBigInt: 20 ;
    adBinary : 128 ;
    adBoolean: 11 ;
    adChar: 129 ;
    adDBTimeStamp: 135 ;
    adEmpty: 0 ;
    adInteger: 3 ;
    adSmallInt: 2 ;
    adTinyInt: 16 ;
    adVarChar: 200 ;
    对于返回值,只能取整形,且-1到-99为保留值;
    第三个参数(4),表明参数的性质,此处4表明这是一个返回值。此参数取值的说明如下:
    0 : 类型无法确定; 1: 输入参数;2: 输入参数;3:输入或输出参数;4: 返回值
   
    以上给出的ASP代码,应该说是完整的代码,也即最复杂的代码,其实
    Set Mypara = MyComm.CreateParameter("RETURN",2,4)
    MyComm.Parameters.Append MyPara
       
    可以简化为
    MyComm.Parameters.Append MyComm.CreateParameter("RETURN",2,4)
    甚至还可以继续简化,稍后会做说明。
    对于带参数的存储过程,只能使用Command对象调用(也有资料说可通过Connection对象或Recordset对象调用,但我没有试成过)。
    4. 有输入参数和输出参数的存储过程
    返回值其实是一种特殊的输入参数。在大多数情况下,我们用到的是同时有输入及输出参数的存储过程,比如我们想取得用户信息表中,某ID用户的用户名,这时候,有一个输入参数----用户ID,和一个输出参数----用户名。实现这一功能的存储过程如下:
    /*SP4*/
    CREATE PROCEDURE dbo.getUserName
       @UserID int,
       @UserName varchar(40) output
    as
    set nocount on
    begin
       if @UserID is null return
       select @UserName=username
           from dbo.[userinfo]
           where userid=@UserID
       return
    end
    go
    调用该存储过程的ASP代码如下:
    '**调用带有输入输出参数的存储过程**
    DIM MyComm,UserID,UserName
    UserID = 1
    Set MyComm = Server.CreateObject("ADODB.Command")
    MyComm.ActiveConnection = MyConStr          'MyConStr是数据库连接字串
    MyComm.CommandText      = "getUserName"     '指定存储过程名
    MyComm.CommandType      = 4                 '表明这是一个存储过程
    MyComm.Prepared         = true              '要求将SQL命令先行编译
    '声明参数
    MyComm.Parameters.append MyComm.CreateParameter("@UserID",3,1,4,UserID)
    MyComm.Parameters.append MyComm.CreateParameter("@UserName",200,2,40)
    MyComm.Execute
    '取得出参
    UserName = MyComm(1)
    Set MyComm = Nothing
    在以上代码中,可以看到,与声明返回值不同,声明输入参数时需要5个参数,声明输出参数时需要4个参数。声明输入参数时5个参数分别为:参数名、参数数据类型、参数类型、数据长度、参数值。声明输入参数时,没有最后一个参数:参数值。
    需要特别注意的是:在声明参数时,顺序一定要与存储过程中定义的顺序相同,而且各参数的数据类型、长度也要与存储过程中定义的相同。
    如果存储过程有多个参数,ASP代码会显得繁琐,可以使用with命令简化代码:
    '**调用带有输入输出参数的存储过程(简化代码)**
    DIM MyComm,UserID,UserName
    UserID = 1
    Set MyComm = Server.CreateObject("ADODB.Command")
    with MyComm
       .ActiveConnection = MyConStr          'MyConStr是数据库连接字串
       .CommandText      = "getUserName"     '指定存储过程名
       .CommandType      = 4                 '表明这是一个存储过程
       .Prepared         = true              '要求将SQL命令先行编译
       .Parameters.append .CreateParameter("@UserID",3,1,4,UserID)
       .Parameters.append .CreateParameter("@UserName",200,2,40)
       .Execute
    end with
    UserName = MyComm(1)
    Set MyComm = Nothing
   
    假如我们要取得ID为1到10,10位用户的用户名,是不是要创建10次Command对象呢?不是的,如果需要多次调用同一存储过程,只需改变输入参数,就会得到不同的输入参数:
    '**多次调用同一存储过程**
    DIM MyComm,UserID,UserName
    UserName = ""
    Set MyComm = Server.CreateObject("ADODB.Command")
    for UserID = 1 to 10
       with MyComm
          .ActiveConnection = MyConStr          'MyConStr是数据库连接字串
          .CommandText      = "getUserName"     '指定存储过程名
          .CommandType      = 4                 '表明这是一个存储过程
          .Prepared         = true              '要求将SQL命令先行编译
          if UserID = 1 then
             .Parameters.append .CreateParameter("@UserID",3,1,4,UserID)
             .Parameters.append .CreateParameter("@UserName",200,2,40)
             .Execute
          else
             '重新给入参赋值(此时参数值不发生变化的入参以及出参不必重新声明)
             .Parameters("@UserID") = UserID
             .Execute
          end if
       end with
       UserName = UserName + MyComm(1) + ","    '也许你喜欢用数组存储
    next
    Set MyComm = Nothing
    通过以上代码可以看出:重复调用同一存储过程时,只需为值发生改变的输入参数重新赋值即可,这一方法在有多个输入输出参数,且每次调用时只有一个输入参数的值发生变化时,可以大大减少代码量。

    5. 同时具有返回值、输入参数、输出参数的存储过程
    前面说过,在调用存储过程时,声明参数的顺序要与存储过程中定义的顺序相同。还有一点要特别注意:如果存储过程同时具有返回值以及输入、输出参数,返回值要最先声明。
    为了演示这种情况下的调用方法,我们改善一下上面的例子。还是取得ID为1的用户的用户名,但是有可能该用户不存在(该用户已删除,而userid是自增长的字段)。存储过程根据用户存在与否,返回不同的值。此时,存储过程和ASP代码如下:
    /*SP5*/
    CREATE PROCEDURE dbo.getUserName
       --为了加深对"顺序"的印象,将以下两参数的定义顺序颠倒一下
       @UserName varchar(40) output,
       @UserID int
    as
    set nocount on
    begin
       if @UserID is null return
       select @UserName=username
           from dbo.[userinfo]
           where userid=@UserID
       if @@rowcount>0
          return 1
       else
          return 0
       return
    end
    go
    '**调用同时具有返回值、输入参数、输出参数的存储过程**
    DIM MyComm,UserID,UserName
    UserID = 1
    Set MyComm = Server.CreateObject("ADODB.Command")
    with MyComm
       .ActiveConnection = MyConStr          'MyConStr是数据库连接字串
       .CommandText      = "getUserName"     '指定存储过程名
       .CommandType      = 4                 '表明这是一个存储过程
       .Prepared         = true              '要求将SQL命令先行编译
       '返回值要最先被声明
       .Parameters.Append .CreateParameter("RETURN",2,4)
       '以下两参数的声明顺序也做相应颠倒
       .Parameters.append .CreateParameter("@UserName",200,2,40)
       .Parameters.append .CreateParameter("@UserID",3,1,4,UserID)
       .Execute
    end with
    if MyComm(0) = 1 then
       UserName = MyComm(1)
    else
       UserName = "该用户不存在"
    end if
    Set MyComm = Nothing

    6. 同时返回参数和记录集的存储过程
    有时候,我们需要存储过程同时返回参数和记录集,比如在利用存储过程分页时,要同时返回记录集以及数据总量等参数。以下给出一个进行分页处理的存储过程:
    /*SP6*/
    CREATE PROCEDURE dbo.getUserList
       @iPageCount int OUTPUT,   --总页数
       @iPage int,               --当前页号
       @iPageSize int            --每页记录数
    as
    set nocount on
    begin
       --创建临时表
       create table #t (ID int IDENTITY,   --自增字段
                        userid int,
                        username varchar(40))
       --向临时表中写入数据
       insert into #t
          select userid,username from dbo.[UserInfo]
             order by userid
      
       --取得记录总数
       declare @iRecordCount int
       set @iRecordCount = @@rowcount
       --确定总页数
       IF @iRecordCount%@iPageSize=0
          SET @iPageCount=CEILING(@iRecordCount/@iPageSize)
       ELSE
          SET @iPageCount=CEILING(@iRecordCount/@iPageSize)+1

       --若请求的页号大于总页数,则显示最后一页
       IF @iPage > @iPageCount
          SELECT @iPage = @iPageCount
       --确定当前页的始末记录
       DECLARE @iStart int    --start record
       DECLARE @iEnd int      --end record
       SELECT @iStart = (@iPage - 1) * @iPageSize
       SELECT @iEnd = @iStart + @iPageSize + 1
       --取当前页记录   
       select * from #t where ID>@iStart and ID<@iEnd
       --删除临时表
       DROP TABLE #t
       --返回记录总数
       return @iRecordCount
    end
    go
   在上面的存储过程中,输入当前页号及每页记录数,返回当前页的记录集,总页数及记录总数。为了更具典型性,将记录总数以返回值的形式返回。以下是调用该存储过程的ASP代码(具体的分页操作略去):
    '**调用分页存储过程**
    DIM pagenow,pagesize,pagecount,recordcount
    DIM MyComm,MyRst
    pagenow = Request("pn")
    '自定义函数用于验证自然数
    if CheckNar(pagenow) = false then pagenow = 1
    pagesize = 20
    Set MyComm = Server.CreateObject("ADODB.Command")
    with MyComm
       .ActiveConnection = MyConStr          'MyConStr是数据库连接字串
       .CommandText      = "getUserList"     '指定存储过程名
       .CommandType      = 4                 '表明这是一个存储过程
       .Prepared         = true              '要求将SQL命令先行编译
       '返回值(记录总量)
       .Parameters.Append .CreateParameter("RETURN",2,4)
       '出参(总页数)
       .Parameters.Append .CreateParameter("@iPageCount",3,2)
       '入参(当前页号)
       .Parameters.append .CreateParameter("@iPage",3,1,4,pagenow)
       '入参(每页记录数)
       .Parameters.append .CreateParameter("@iPageSize",3,1,4,pagesize)
       Set MyRst = .Execute
    end with
    if MyRst.state = 0 then        '未取到数据,MyRst关闭
       recordcount = -1
    else
       MyRst.close    '注意:若要取得参数值,需先关闭记录集对象
       recordcount = MyComm(0)
       pagecount   = MyComm(1)
       if cint(pagenow)>=cint(pagecount) then pagenow=pagecount
    end if
    Set MyComm = Nothing
    '以下显示记录
    if recordcount = 0 then
       Response.Write "无记录"
    elseif recordcount > 0 then
       MyRst.open
       do until MyRst.EOF
       ......
       loop
       '以下显示分页信息
       ......
    else  'recordcount=-1
       Response.Write "参数错误"
    end if
    对于以上代码,只有一点需要说明:同时返回记录集和参数时,若要取得参数,需先将记录集关闭,使用记录集时再将其打开。

    7. 返回多个记录集的存储过程
    本文最先介绍的是返回记录集的存储过程。有时候,需要一个存储过程返回多个记录集,在ASP中,如何同时取得这些记录集呢?为了说明这一问题,在userinfo表中增加两个字段:usertel及usermail,并设定只有登录用户可以查看这两项内容。
    /*SP7*/
    CREATE PROCEDURE dbo.getUserInfo
       @userid int,
       @checklogin bit
    as
    set nocount on
    begin
       if @userid is null or @checklogin is null return
       select username
          from dbo.[usrinfo]
          where userid=@userid
       --若为登录用户,取usertel及usermail
       if @checklogin=1
          select usertel,usermail
             from dbo.[userinfo]
             where userid=@userid
       return
    end
    go
    以下是ASP代码:
    '**调用返回多个记录集的存储过程**
    DIM checklg,UserID,UserName,UserTel,UserMail
    DIM MyComm,MyRst
    UserID = 1
    'checklogin()为自定义函数,判断访问者是否登录
    checklg = checklogin()
    Set MyComm = Server.CreateObject("ADODB.Command")
    with MyComm
       .ActiveConnection = MyConStr          'MyConStr是数据库连接字串
       .CommandText      = "getUserList"     '指定存储过程名
       .CommandType      = 4                 '表明这是一个存储过程
       .Prepared         = true              '要求将SQL命令先行编译
       .Parameters.append .CreateParameter("@userid",3,1,4,UserID)
       .Parameters.append .CreateParameter("@checklogin",11,1,1,checklg)
       Set MyRst = .Execute
    end with
    Set MyComm = Nothing
    '从第一个记录集中取值
    UserName = MyRst(0)
    '从第二个记录集中取值
    if not MyRst is Nothing then
       Set MyRst = MyRst.NextRecordset()
       UserTel  = MyRst(0)
       UserMail = MyRst(1)
    end if
    Set MyRst = Nothing
    以上代码中,利用Recordset对象的NextRecordset方法,取得了存储过程返回的多个记录集。

    至此,针对ASP调用存储过程的各种情况,本文已做了较为全面的说明。最后说一下在一个ASP程序中,调用多个存储过程的不同方法。
    在一个ASP程序中,调用多个存储过程至少有以下三种方法都是可行的:
    1. 创建多个Command对象。
   
    DIM MyComm
    Set MyComm = Server.CreateObject("ADODB.Command")
    '调用存储过程一
    ......
    Set MyComm = Nothing
    Set MyComm = Server.CreateObject("ADODB.Command")
    '调用存储过程二
    ......
    Set MyComm = Nothing
    ......
    2. 只创建一个Command对象,结束一次调用时,清除其参数
    DIM MyComm
    Set MyComm = Server.CreateObject("ADODB.Command")
    '调用存储过程一
    .....
    '清除参数(假设有三个参数)
    MyComm.Parameters.delete 2
    MyComm.Parameters.delete 1
    MyComm.Parameters.delete 0
    '调用存储过程二并清除参数
    ......
    Set MyComm = Nothing
    此时要注意:清除参数的顺序与参数声明的顺序相反,原因嘛,我也不知道。
    3. 利用Parameters数据集合的Refresh方法重置Parameter对象
    DIM MyComm
    Set MyComm = Server.CreateObject("ADODB.Command")
    '调用存储过程一
    .....
    '重置Parameters数据集合中包含的所有Parameter对象
    MyComm.Parameters.Refresh
    '调用存储过程二
    .....
    Set MyComm = Nothing
 
    一般认为,重复创建对象是效率较低的一种方法,但是经测试(测试工具为Microsoft Application Center Test),结果出人意料:
    方法2 >= 方法1 >> 方法3
    方法2的运行速度大于等于方法1(最多可高4%左右),这两种方法的运行速度远大于方法3(最多竟高达130%),所以建议在参数多时,采用方法1,在参数较少时,采用方法2。
    花了一天的时间,终于把我对于在ASP中调用存储过程的一些粗浅的经验形成了文字。这其中,有些是我只知其果而不明其因的,有些可能是错误的,但是,这些都是经过我亲身实践的。各位看官批判地接受吧。有不同意见,希望一定向我指明,先谢了。

asp存储过程使用大全

存储过程使用大全

1,调用没有参数的存储过程
<%
set conn=server.CreateObject("adodb.connection")
set cmd=server.CreateObject("adodb.command")
strconn="dsn=pubs;uid=sa;pwd"

conn.Open strconn
set cmd.ActiveConnection=conn

cmd.CommandText="{call nono}"

'set rs=cmc.exe 或者cmd.execute

set rs=cmd.Execute()

%>

2,一个输入的参数的存储过程
<%
set conn=server.CreateObject("adodb.connection")
set cmd=server.CreateObject("adodb.command")
strconn="dsn=pubs;uid=sa;pwd"

conn.Open strconn
set cmd.ActiveConnection=conn

cmd.CommandText="{call oneinput(?)}"
cmd.Parameters.Append cmd.CreateParameter("@aaa",adInteger ,adParamInput )
cmd("@aaa")=100

cmd.Execute()

%>

3,一个输入参数和一个输出的参数
<%
set conn=server.CreateObject("adodb.connection")
set cmd=server.CreateObject("adodb.command")
strconn="dsn=pubs;uid=sa;pwd"

conn.Open strconn
set cmd.ActiveConnection=conn

cmd.CommandText = "{call oneinout(?,?)}"
cmd.Parameters.Append cmd.CreateParameter("@aaa",adInteger,adParamInput)
cmd("@aaa")=10
cmd.Parameters.Append cmd.CreateParameter("@bbb",adInteger,adParamOutput)

cmd.Execute()

bbb=cmd("@bbb")
%>

4,一个输入参数,一个输出参数,和一个返回值
<%
set conn=server.CreateObject("adodb.connection")
set cmd=server.CreateObject("adodb.command")
strconn="dsn=pubs;uid=sa;pwd"

conn.Open strconn
set cmd.ActiveConnection=conn

cmd.CommandText="{?=call onereturn(?,?)}"

cmd.Parameters.Append cmd.CreateParameter("@return_value",adInteger,adParamReturnValue )
cmd.Parameters.Append cmd.CreateParameter("@aaa",adInteger,adParamInput )
cmd("@aaa")=10
cmd.Parameters.Append cmd.CreateParameter("@bbb",adInteger,adParamOutput)

cmd.Execute()

bbb=cmd("@bbb")
rrr=cmd("@return_value")
%>

如何在ASP中调用SQL存储过程

<%set connection1 = Server.CreateObject("ADODB.Connection")
connection1.open ... '联接
set command1=Server.CreateObject("ADODB.command")
set command1.activeconnection=connection1
command1.commandtype=4
command1.commandtext="sp_1"  'SP 名
command1.parameters(1)=... '参数值
command1.parameters(2)=...
set recordset1=command1.execute()
%>

ASP调用存储过程的技巧

1、最简单的如下
     Dim objConn
     Set objConn = Server.CreateObject("ADOBD.Connection")
     objConn.Open Application("Connection_String")
     'Call the stored procedure to increment a counter on the page
     objConn.Execute "exec sp_AddHit"
    没有参数,没有返回,没有错误处理,就是这个了
   
2、带参数的一种调用
    objConn.Execute "exec sp_AddHit 'http://www.aspalliance.com', 1"
    请注意分割参数,该方法也不返回记录
   
3、返回记录的
     Dim objConn
     Dim objRs
     Set objConn = Server.CreateObject("ADOBD.Connection")
     Set objRs = Server.CreateObject("ADOBD.Recordset")
     objConn.Open Application("Connection_String")
     'Call the stored procedure to increment a counter on the page
     objRs.Open objConn, "exec sp_ListArticles '1/15/2001'"
     'Loop through recordset and display each article

4、……
     Dim objConn
     Dim objCmd
   
    'Instantiate objects
    Set objConn = Server.CreateObject("ADODB.Connection")
    set objCmd = Server.CreateObject("ADODB.Command")
    conn.Open Application("ConnectionString")
   
    With objCmd
     .ActiveConnection = conn 'You can also just specify a connection string here
     .CommandText = "sp_InsertArticle"
     .CommandType = adCmdStoredProc 'Requires the adovbs.inc file or typelib meta tag
    
     'Add Input Parameters
     .Parameters.Append .CreateParameter("@columnist_id", adDouble, adParamInput, , columnist_id)
     .Parameters.Append .CreateParameter("@url", adVarChar, adParamInput, 255, url)
     .Parameters.Append .CreateParameter("@title", adVarChar, adParamInput, 99, url)
     .Parameters.Append .CreateParameter("@description", adLongVarChar, _
     adParamInput, 2147483647, description)
    
     'Add Output Parameters
     .Parameters.Append .CreateParameter("@link_id", adInteger, adParamOutput, , 0)
    
     'Execute the function
     'If not returning a recordset, use the adExecuteNoRecords parameter option
     .Execute, , adExecuteNoRecords
     link_id = .Parameters("@link_id")
    End With
   
5、存储过程的代码
    Create PROCEDURE dbo.sp_InsertArticle
    (
     @columnist_id int,
     @url varchar(255),
     @title varchar(99),
     @description text
     @link_id int OUTPUT
    )
    AS
    BEGIN
     INSERT INTO dbo.t_link (columnist_id,url,title,description)
     VALUES (@columnist_id,@url,@title,@description)
   
     SELECT @link_id = @@IDENTITY
    END

ASP调用带参数存储过程的几种方式

    最近有很多的朋友问到调用存储过程的问题,这里简单介绍几种ASP调用带参数存储过程的方法。

1 这也是最简单的方法,两个输入参数,无返回值:
set connection = server.createobject("adodb.connection")
connection.open someDSN
Connection.Execute "procname varvalue1, varvalue2"

''将所有对象清为nothing,释放资源
connection.close
set connection = nothing


2 如果要返回 Recordset 集:
set connection = server.createobject("adodb.connection")
connection.open someDSN
set rs = server.createobject("adodb.recordset")
rs.Open "Exec procname varvalue1, varvalue2",connection

''将所有对象清为nothing,释放资源
rs.close
connection.close
set rs = nothing
set connection = nothing


3 以上两种方法都不能有返回值,(Recordset除外),如果要得到返回值,需要用Command的方法。
首先说明,返回值有两种。一种是在存储过程中直接return一个值,就象C和VB的函数返回值那样;另一种是可以返回多个值,存
储这些值的变量名称需要在调用参数中先行指定。
这个例子要处理多种参数,输入参数,输出参数,返回记录集以及一个直接返回值(够全了吧?)


存储过程如下:

use pubs
GO

-- 建立存储过程
create procedure sp_PubsTest

-- 定义三个参数变量,注意第三个,特别标记是用于输出
@au_lname varchar (20),
@intID int,
@intIDOut int OUTPUT

AS

SELECT @intIDOut = @intID + 1

SELECT *
FROM authors
WHERE au_lname LIKE @au_lname + ''%''

--直接返回一个值
RETURN @intID + 2


调用该存储过程的asp程序如下:

<%@ Language=VBScript %>
<%
Dim CmdSP
Dim adoRS
Dim adCmdSPStoredProc
Dim adParamReturnValue
Dim adParaminput
Dim adParamOutput
Dim adInteger
Dim iVal
Dim oVal
Dim adoField
Dim adVarChar

‘这些值在 VB 中是预定义常量,可以直接调用,但在 VBScript 中没有预定义
adCmdSPStoredProc = 4
adParamReturnValue = 4
adParaminput = 1
adParamOutput = 2
adInteger = 3
adVarChar = 200

iVal = 5
oVal = 3

''建一个command对象
set CmdSP = Server.CreateObject("ADODB.Command")

''建立连结
CmdSP.ActiveConnection = "Driver={SQL Server};server=(local);Uid=sa;Pwd=;Database=Pubs"

''定义command 对象调用名称
CmdSP.CommandText = "sp_PubsTest"

''设置command调用类型是存储过程 (adCmdSPStoredProc = 4)
CmdSP.CommandType = adCmdSPStoredProc

''往command 对象中加参数
''定义存储过程有直接返回值,并且是个整数,省缺值是4
CmdSP.Parameters.Append CmdSP.CreateParameter("RETURN_VALUE", adInteger, adParamReturnValue, 4)
''定义一个字符型输入参数
CmdSP.Parameters.Append CmdSP.CreateParameter("@au_lname", adVarChar, adParaminput, 20, "M")
''定义一个整型输入参数
CmdSP.Parameters.Append CmdSP.CreateParameter("@intID", adInteger, adParamInput, , iVal)
''定义一个整型输出参数
CmdSP.Parameters.Append CmdSP.CreateParameter("@intIDOut", adInteger, adParamOutput, oVal)

''运行存储过程,并得到返回记录集
Set adoRS = CmdSP.Execute


''把每个记录打印出来,其中的字段是虚拟的,可以不用管
While Not adoRS.EOF

for each adoField in adoRS.Fields
Response.Write adoField.Name & "=" & adoField.Value & "<br>" & vbCRLF
Next
Response.Write "<br>"
adoRS.MoveNext
Wend

''打印两个输出值:
Response.Write "<p>@intIDOut = “ & CmdSP.Parameters("@intIDOut").Value & "</p>"
Response.Write "<p>Return value = " & CmdSP.Parameters("RETURN_VALUE").Value & "</p>"


''大扫除
Set adoRS = nothing
Set CmdSP.ActiveConnection = nothing
Set CmdSP = nothing
%>

此外还有其他方式,稍微偏门一些,以后慢慢再说
本文参考了多篇文章,这里不一一列出。

在Asp中使用存储过程

  为了提高Asp程序的效率,有时需要在Asp中使用使用Sql Server的存储技术,下面简单作一个介绍。

存储过程的建立

  这里只简单介绍如何在Sql Server的企业管理器中如何建立存储过程:

(1)打开企业管理器Enterprise manager

(2)选择服务器组(SQL Server Group)、服务器、数据库(Database)以及相就的数据库,鼠标右击对应数据库下的Stored Procdures项,在弹出的菜单中选择New Stored Procedure,在Stored Procedures Properties中输入建立存储过程的语句。下面是一个例子:


  CREATE PROCEDURE proctest @mycola Char(10),@mycolb Char(10),@mycolc text  AS

  Insert into chatdata (mycola,mycolb,mycolc) values(@mycola,@mycolb,@mycolc)

在Sql Server的文档中它的语法为:

  CREATE PROC[EDURE] procedure_name [;number]   [

  {@parameter data_type} [VARYING] [= default] [OUTPUT]   ]

  [,...n]  [WITH    {   RECOMPILE   | ENCRYPTION

  | RECOMPILE, ENCRYPTION   }  ]  [FOR REPLICATION]  AS

   sql_statement [...n]

  如果你对Sql语法不熟悉,可以使用Check Syntax来检查语法。在上例中,表示建立存储过程名为mycola,带3个参数的存储过过程,其中第一个参数mycola数据类型为char,宽度10;第2个参数数据类型为char,宽度为10,第3个参数数据类型为text,在这里使用的是Sql Server的数据类型。

  存储过程建立后,下面就是如何在Asp程序中调用该存储过程:在Asp中调用存储过程 为了提高Asp程序的效率,有时需要在Asp中使用使用Sql Server的存储技术,下面简单作一个,在上面的增加参数的语句p.Append cm.CreateParameter("@mycolc",201,1,250)中,格式为:

p.Append cm.CreateParameter("参数名称",类型,方向,大小)

参许参数值的类型的意义如下:

名称值 整数值 功能


  adDBTimeStamp 135 日期时间数据类型

  adDecimal 14 十进制整数值

  adDouble 5 双精度小数值

  adError 10 系统错误信息

AdGUID 72 全域性唯一识别字(Globally unique identifier)

adDispath 9 COM/OLE自动对象(Automation Object)

adInteger 3 4字节有符号整数

adIUnknown 13 COM/OLE对象

adLongVarBinary 205 大型2字节值

adLongVarChar 201 大型字符串值

adLongVarWChar 203 大型未编码字符串

adNumeric 131 十进制整数值

adSingle 4 单精度浮点小数

adSmallInt 2 2字节有符号整数

adTinyInt 16 1字节有符号整数

adUnsignedBigInt 21 8字节无符号整数

adUnsignedInt 19 4字节无符号整数

adUnsignedSmallInt 18 2字节无符号整数

adUnsignedTinyInt 17 1字节无符号整数

adUserDefined 132 用户自定义数据类型

adVariant 12 OLE对象

adVarBinary 204 双字节字符变量值

adVarChar 200 字符变量值

advarchar 202 未编码字符串变量值

adWchar 130 未编码字符串


方向值的意义如下:


名称值 整数值 功能


adParamInput 1 允许数据输入至该参数当中

adParamOutput 2 允许数据输出至该参数当中

adParamInputOutput 3 允许数据输入、输出至该参数当中

adparamReturnValue 4 允许从一子程序中返回数据至该参数当中

更多详细资源请参考Sql Server的文档和IIS的文档资源。

存储过程介绍及asp存储过程的使用

一、先介绍一下什么是存储过程

存储过程是利用SQL Server所提供的Tranact-SQL语言所编写的程序。Tranact-SQL语言是SQL Server提供专为设计数据库应用程序的语言,它是应用程序和SQL Server数据库间的主要程序式设计界面。它好比Oracle数据库系统中的Pro-SQL和Informix的数据库系统能够中的Informix-4GL语言一样。这类语言主要提供以下功能,让用户可以设计出符合引用需求的程序:
1)、变量说明
2)、ANSI兼容的SQL命令(如Select,Update….)
3)、一般流程控制命令(if…else…、while….)
4)、内部函数

二、存储过程的书写格

CREATE PROCEDURE [拥有者.]存储过程名[;程序编号]
[(参数#1,…参数#1024)]
[WITH
{RECOMPILE | ENCRYPTION | RECOMPILE, ENCRYPTION}
]
[FOR REPLICATION]
AS 程序行

其中存储过程名不能超过128个字。每个存储过程中最多设定1024个参数
(SQL Server 7.0以上版本),参数的使用方法如下:

@参数名 数据类型 [VARYING] [=内定值] [OUTPUT]

每个参数名前要有一个“@”符号,每一个存储过程的参数仅为该程序内部使用,参数的类型除了IMAGE外,其他SQL Server所支持的数据类型都可使用。
[=内定值]相当于我们在建立数据库时设定一个字段的默认值,这里是为这个参数设定默认值。[OUTPUT]是用来指定该参数是既有输入又有输出值的,也就是在调用了这个存储过程时,如果所指定的参数值是我们需要输入的参数,同时也需要在结果中输出的,则该项必须为OUTPUT,而如果只是做输出参数用,可以用CURSOR,同时在使用该参数时,必须指定VARYING和OUTPUT这两个语句。

例子:
CREATE PROCEDURE order_tot_amt @o_id int,@p_tot int output AS
SELECT @p_tot = sum(Unitprice*Quantity)
FROM orderdetails
WHERE ordered=@o_id

例子说明:
该例子是建立一个简单的存储过程order_tot_amt,这个存储过程根据用户输入的定单ID号码(@o_id),由定单明细表(orderdetails)中计算该定单销售总额[单价(Unitprice)*数量(Quantity)],这一金额通过@p_tot这一参数输出给调用这一存储过程的程序

三、在SQL Server中执行存储过程

在SQL Server的查询分析器中,输入以下代码:
declare @tot_amt int
execute order_tot_amt 1,@tot_amt output
select @tot_amt

以上代码是执行order_tot_amt这一存储过程,以计算出定单编号为1的定单销售金额,我们定义@tot_amt为输出参数,用来承接我们所要的结果

四、在ASP中调用存储过程

<!--           必须加载adovbs.inc文件,否则将出错        -->
<!--#include file="adovbs.inc"-->
<%
dim objCnn
dim objCmd
dim Rs
const o_id=112

'-----建立Connection对象----------
set objCnn=Server.CreateObject("Adodb.connection")
objCnn.Open "driver={sql server};server=localhost;uid=sa;pwd=cncanet;database=check;"
'-----建立Command对象-----------
set objCmd=Server.CreateObject("Adodb.Command")
objCmd.ActiveConnection=objCnn
objCmd.CommandText="order_tot_amt" '指定存储过程名称
objCmd.CommandType=adCmdStoredProc '其为Stored Procedure
'-----准备stored procedure 的参数-------
objCmd.Parameters.Append _
objCmd.CreateParameter("o_id",adInteger,adParamInput,,o_id)
objCmd.Parameters.Append _
objCmd.CreateParameter("p_tot",adBigInt,adParamOutput,,0)
'-----执行存储过程----------------------
objCmd.Execute

'-----输出参数以及处理结果--------------
for each parm in objCmd.Parameters
Response.Write parm.name &"="& trim(parm) &"<br>"
next
%>

2月8日

又见出租车“出走”

刚刚从公司出来,今天下班后在办公室做测试查资料,忙得不亦乐乎,却很开心。上了公交车,车上的移动电视正在播出杭州电视台的《新闻夜班车》,优优正在播一条让我哭笑不得的新闻:有10多辆“红旗”出租车被司机开回老家了!这次,又是河南人,理由又是不堪高额承包费。
可以想象明天的网上的新闻又会相当的热闹!才一个多月,“奔驰”被开回河南已经在网上闹得沸沸洋洋,质疑司机的,支持司机的,质疑出租车公司的,支持出租车公司的,质疑政府的,甚至上升至对整个河南人的人格攻击的……无论如何,当时的“奔驰”事件最终以出租车公司做出让步,满足司机的要求而得以解决,也许,这也为今日的“红旗”出走埋下了祸根!
这回怎么办?
出租车公司已经做出强硬的表态了,一切按照合同办。可是,这样真的能化解矛盾解决问题么?
回过头来想想,为什么“奔驰”可以,“红旗”不可以?可是,如果大家都把车往老家开,还要合同做甚?想来想去,我也不知道该“同情”谁了,只是觉得这年头很多事情实在是太搞笑了……
公交车上实在无聊,就随便写写几个字打发这无聊的时光吧,嘿嘿……


使用 Microsoft SQL Server 2000 的全文搜索功能构建 Web 搜索应用程序

使用 Microsoft SQL Server 2000 的全文搜索功能构建 Web 搜索应用程序

Andrew B. Cencini
Microsoft Corporation
2002年12月

http://www.microsoft.com/china/msdn/archives/library/dnsql2k/html/sql_fulltextsearch.asp

适用于:
    Microsoft SQL Server 2000

摘要:学习如何充分利用 SQL Server 2000 的全文搜索功能。本文包含有关实现最大吞吐率和最佳性能的几点提示和技巧。

目录
简介
全文搜索功能简介
配置全文搜索功能
全文查询
排位和优化
其他性能技巧
小结
附录 A:实现全文搜索功能的最佳选择
附录 B:使用最佳选择、结果分页和有效全文查询逻辑的示例应用程序
附录 C:资源
简介
使用 Microsoft? SQL? Server 2000 的全文搜索功能,可以对在非结构化文本数据上生成的索引执行快速、灵活的查询。常用的全文搜索工具是网站的搜索引擎。为了帮助读者理解全文搜索功能的最佳使用方法,本文介绍了大量抽象概念;并对优化全文索引和查询以实现最大吞吐率和最佳性能,提供了几点提示和技巧。

全文搜索功能简介
全文搜索功能在 SQL Server 7.0 中引入。全文搜索的核心引擎建立在 Microsoft Search (MSSearch) 技术上,Microsoft Exchange 和 Microsoft SharePoint? Portal Server 等产品中也采用了此项技术。

SQL Server 7.0 全文搜索中公开的功能可提供基本的文本搜索功能,并使用早期版本的 MSSearch;而 SQL Server 2000 的全文搜索实现则包含一组可靠的索引和查询功能,并在 SQL Server 7.0 的基础之上添加了几项增强功能。这些增强功能包括:通过 Microsoft 群集服务完全支持群集操作,能够过滤和索引 IMAGE 列中存储的文档,提供改进的语言支持,以及在性能、可缩放性和可靠性方面进行了改进。

MSSearch 生成、维护和查询文件系统中(而不是 SQL Server 中)存储的全文索引。MSSearch 进行全文索引时使用的逻辑和物理存储单元是目录。全文目录在每个数据库中包含一个或多个全文索引 - 可以为 SQL Server 中的每个表创建一个全文索引,且索引中可以包含该表中的一列或多列。每个表只能属于一个目录,且每个表只能创建一个索引。我们将简单介绍有关组织全文目录和索引的最佳方案 - 但首先,让我们来简单了解一下全文搜索的工作原理。

配置全文搜索功能
要为 SQL Server 中存储的文本数据创建全文索引,应该先完成以下几步准备工作。第一步是以全文方式启用包含要生成索引的文本数据的数据库(如果您尚未执行此操作)。

注意:执行以下语句将丢弃并重新创建属于要启用全文搜索的数据库的所有全文目录。除非要重新创建全文目录,否则请确保在要启用的特定数据库中未创建任何全文目录。
如果您是 sysadmin 角色的成员或此数据库的 db_owner,可以继续进行并发出以下语句:

use Northwind
exec sp_fulltext_database 'enable'

接下来,您需要创建全文目录,以存储全文索引。正如前面所提到的,此目录中的数据存储在文件系统中(而不是 SQL Server 中),因此,在考虑全文目录的存储位置时应该仔细选择。除非指定其他位置,否则全文目录将存储在 FTDATA 目录(位于 Microsoft SQL Server/MSSQL 存储位置中)的子目录中。以下是在非默认位置创建全文目录的方法:

exec sp_fulltext_catalog 'Cat_Desc', 'create', 'f:/ft'

在本例中,全文目录将创建为“f:/ft”的子目录,如果您查看文件系统的该部分,将看到它有了自己的目录。MSSearch 使用的全文目录的命名规则是:

SQL+dbid+catalogID

目录 ID 从 00005 开始,并且每新建一个目录就递增 1。

如果可能的话,最好在其所在的物理驱动器上创建全文目录。如果生成全文索引的进程需要进行大量的 I/O 操作(具体而言,就是从 SQL Server 中读取数据,然后向文件系统写入索引),则应避免使 I/O 子系统成为瓶颈。

那么,全文目录有多大呢?通常情况下,全文目录的系统开销比 SQL Server 中存储的数据(对其进行全文索引)量高出大约 30%;但是,此规则取决于数据中唯一单词(或主键)的分布,以及被您视为是干扰词的单词的分布。干扰词(或终止词)是指要排除在全文索引和查询以外的词语(因为它们不是您感兴趣的搜索词,而且出现频率很高,所以只会使索引变得很大,而不会有实际效果)。稍后,我们将介绍有关干扰词选择方面的注意事项,以及如何优化干扰词以改善查询性能。

如果您尚未执行此操作,请在每个要生成全文索引的表上创建一个唯一的单列非空索引。这个唯一索引用于将表中的每一行映射到 MSSearch 内部使用的一个唯一可压缩主键。接下来,您需要让 MSSearch 知道您要为表创建全文索引。对表发出以下语句可将该表添加到所选的全文目录中(在本例中,它是我们在前面创建的“Cat_Desc”):

exec sp_fulltext_table 'Categories', 'create', 'Cat_Desc',
   'PK_Categories'

下一步是向此全文索引添加列。您可以为每一列选择一种语言,如果该列的类型为 IMAGE,则必须再指定一列,以指示 IMAGE 列的每一行中存储的文档类型。

在列语言选择方面,有一些重要但尚未成文的注意事项。这些注意事项与文本的标记方式以及 MSSearch 对文本的索引方式有关。被索引的文本是通过一个称作单词分隔符(用作单词边界标记)的组件提供的。在英文中,单词分隔符通常是空格或某种形式的标点符号;而在其他语言中(例如德语),单词或字符可以组合在一起;因此,所选的列语言应表示要存储在该列的行中的语言。如果不确定,最好的方法通常是使用中性单词分隔符(只使用空格和标点符号执行标记功能)。选择列语言的另一个好处是“寻根溯源”。全文查询中的寻根溯源是指在特定语言中搜索某一单词的所有变化形式的过程。

选择语言的另一个考虑因素与数据的表示方法有关。对于非 IMAGE 列数据来说,不需要执行特殊的过滤操作;而文本通常需要将单词分隔组件按原样传递。单词分隔符主要用于处理书面文本。因此,如果文本中有任何类型的标记(例如 HTML),则在索引和搜索过程中,语言精确性将不会很高。这种情况下,您有两种选择 - 首选方法是只将文本数据存储在 IMAGE 列中,并指明其文档类型,以便对其进行过滤。如果不选择此方法,则可以考虑使用中性单词分隔符,并且可能的话,在干扰词列表中添加标记数据(例如 HTML 中的“br”)。在指定了中性语言的列中不能进行任何基于语言的寻根溯源,但有些环境可能会要求您选择此方法。

在知道列选项后,通过发出以下语句在全文索引中添加一列或两列:

exec sp_fulltext_column 'Categories', 'Description', 'add'

您可能注意到,此处未指定任何语言 - 这种情况下,将使用默认的全文语言。可以通过系统存储过程“sp_configure”为服务器设置默认全文语言。

将所有列添加到全文索引后,即可执行填充操作。填充方法之多实在是不胜枚举,此处不作详细介绍。在本例中,只需对表启动完全填充,并等待它执行完毕:

exec sp_fulltext_table 'Categories', 'start_full'

您可能希望使用 FULLTEXTCATALOGPROPERTY 或 OBJECTPROPERTY 函数来监视填充状态。要获取目录填充状态,可以执行:

select FULLTEXTCATALOGPROPERTY('Cat_Desc', 'Populatestatus')

通常情况下,如果完全填充正在进行,则返回的结果是“1”。有关如何使用 FULLTEXTCATALOGPROPERTY 和 OBJECTPROPERTY 的详细信息,请参阅 SQL Server Books Online。

全文查询
查询全文索引与执行 SQL Server 中的标准关系型查询略有不同。由于索引是在 SQL Server 外部进行存储和管理的,因此全文查询处理大部分由 MSSearch 完成(因此,那些一部分是关系型、一部分基于全文的查询将被单独处理),这样做有时会损害性能。

从本质上说,执行全文查询时,查询词传递给 MSSearch,后者遍历其内部数据结构(索引),并向 SQL Server 返回主键和排位值。如果执行 CONTAINS 或 FREETEXT 查询,则通常看不到主键或排位值,但如果执行 CONTAINSTABLE 或 FREETEXTTABLE 查询,则将获得这些值,然后这些值通常会与基表合并在一起。与基表合并主键的进程需要很高的系统开销 - 稍后,我们将向您介绍一些巧妙的方法以尽量减少或完全避免这种合并。

如果您通过不断思考,对全文查询如何返回数据有了一个初步了解,就可以推测出 CONTAINS/FREETEXT 查询仅执行 CONTAINSTABLE/FREETEXTTABLE 查询并与基表进行合并。有了这样的了解,您应该避免使用这些类型的查询,除非不这样做的开销更高。在 Web 搜索应用程序中,使用 CONTAINSTABLE 与 FREETEXTTABLE 比使用不带 TABLE 的同类函数好得多。

到现在为止,您已经知道全文查询是用来从 SQL Server 之外存储的 MSSearch 索引中访问数据的特殊方法,还知道如果盲目地与基表进行合并,就会遇到麻烦。应该了解的另外一个重要内容是 CONTAINS 样式查询与 FREETEXT 样式查询之间的本质差别。

CONTAINS 查询用于对所查询的所有词语执行完全匹配查询。无论您只查找单个单词,还是查找以“orange”开头的所有单词,系统只返回包含所有搜索词的结果。因此,CONTAINS 查询速度很快,因为它们通常返回很少的结果,并且不需要执行过多的附加处理。CONTAINS 查询的缺点包括令人生厌的干扰词过滤问题。经验丰富的开发人员以及过去使用过全文搜索的数据库管理员,在试图匹配只包含单个干扰词的单词或词组时,曾遇到过“您的查询只包含干扰词”这样令人吃惊的错误。要避免收到此错误,方法之一是在执行全文查询之前过滤出干扰词。向包含干扰词的 CONTAINS 查询返回结果是不可能的,因为此类查询只返回与整个查询字符串完全匹配的结果。由于干扰词不是全文索引项,因此包含干扰词的 CONTAINS 查询不会返回任何行。

FREETEXT 查询消除了 CONTAINS 查询中偶尔出现的所有警告说明。当发出 FREETEXT 查询时,实际上发出的是词根查询。因此,当您搜索“root beer”时,“root”和“beer”包含其所有形式(寻根溯源与语言相关;所用的语言由生成索引时指定的全文列语言确定,并且在所有查询的列中必须相同),并且系统将返回至少与这些词语之一匹配的所有行。

FREETEXT 查询的负面影响是它们通常比 CONTAINS 查询耗用更多的 CPU - 因为要寻根溯源以及返回更多的结果,就需要包含更复杂的排位计算。不过,基于 FREETEXT 的查询非常灵活,而且速度非常快,是基于 Web 的搜索应用程序中通常使用的最佳选择。

排位和优化
我经常遇到使用全文搜索的用户,他们问我排位编号是什么意思,以及如何将排位编号转换成某种用户可以理解的值。对这个问题,回答可长可短,在这里我将进行简要回答。简单而言,这些排位编号不如结果返回的顺序那样重要。也就是说,当您按照排位对结果进行排序时,总是首先返回关联程度最高的结果。排位值本身常常变化 - 全文搜索使用概率排位算法,即返回的每个文档的关联性受全文索引中的任何或所有其他文档的直接影响。

有些人认为,一种有助于增加某些行排位的技巧是在这些行的全文索引列中重复常用的搜索关键字。尽管在某种程度上,这种方法可能会提高这些行因某些关键字而首先返回的几率,但在其他情况下,可能会适得其反 - 而且还存在使词语查询性能降低的风险。较好的解决方案是为搜索应用程序实现“最佳选择”系统(请参阅以下示例),这样就可以确保首先返回某些文档。多次重复使用关键字会使这些特定关键字的全文索引扩大,并使得 MSSearch 在查找正确行和计算排位时浪费时间。如果全文索引数据量很大,并尝试使用了此方法,您可能会发现某些全文查询很耗时。如果能够实现更细致(也可能更精确)的“最佳选择”系统,您会发现它明显改善了查询性能。

多次重复数据的另一个问题与用于组合关系型查询和全文查询的常用技巧有关。许多使用全文搜索的用户都深受此问题的困扰,每当他们试图将某种过滤器应用于全文查询返回的结果时,便会遇到这样的问题。正如前面所说的,全文查询为每个匹配行返回一个主键和一个排位 - 要收集有关这些行的任何详细信息,必须与它的基表进行合并。由于从无限制的全文查询中可能会返回任意数量的结果,因此合并可能需要大量系统开销。人们发现避免合并的一个有效方法是只在全文索引中添加要过滤的数据(如果可能)。换句话说,如果用户要从报纸上所有文章的正文中搜索关键字“Ichiro”,并且只希望返回该报上体育专栏中的文章,则查询语句通常如下所示:

-- [方法 1:]
-- 开销最高:先全部选择,然后再合并和过滤
SELECT ARTICLES_TBL.Author, ARTICLES_TBL.Body, ARTICLES_TBL.Dateline,
   FT_TBL.[rank]
FROM FREETEXTTABLE(Articles, Body, 'Ichiro') AS FT_TBL
INNER JOIN Articles AS ARTICLES_TBL
ON FT_TBL.[key] = ARTICLES_TBL.ArticleID
WHERE ARTICLES_TBL.Category = 'Sports'

-- [方法 2:]
-- 可以使用,但会导致意外结果并变慢,或者会返回不准确的结果:
-- 执行全文过滤,并且只提取主键和排位
-- (处理在 Web 服务器上完成)
SELECT [key], [rank]
FROM CONTAINSTABLE(Articles, *, 'FORMSOF(INFLECTIONAL('Ichiro')
      AND "sports"')

这两个查询要么不必要地占用大量系统开销,要么存在返回错误结果的可能性(在第二个查询中,“sports”很可能出现在所有类型的文章中)。这两项技术还存在其他变体,但这是两种非常简单的模型。如果可行,我通常建议您对数据进行水平划分。即,“类别”列的每个可能值都自成一列(或表),并且与该文章相关的可搜索关键字仅存储在此列中。采用此方法,而不是使用一个“正文”列和一个“类别”列,可以去掉“类别”列,而使用存储可搜索关键字的“Body_<category>”列。如以下示例所示:

-- 如果您可以调整架构,这非常有效 – 每个类别
-- 都成为自己的列(或表格),并且需要命中的
-- 全文索引也较少。这明显需要作一些解释……
SELECT [key], [rank]
FROM FREETEXTTABLE(Articles, Body_Sports, 'Ichiro')

对于包含大量数据,且这些数据可适应此架构(或许是主架构)更改的系统,其性能会得到显著的提高。但在何时应用多个过滤器或不应用过滤器方面却有着明显的限制。当然,还有其他的方法可以解决这些问题。通过以上示例,您会了解一种将某些搜索条件抽象到架构的方法 - 实际上是“欺骗”优化程序(更确切的说是“成为”优化程序),因为在 SQL Server 本身的全文查询中当前不存在本地优化。

其他性能技巧
人们在聊天时常常问我的另一个问题是如何才能分页显示全文查询结果。换句话说,如果我要发出“root beer”查询,一次在某一 Web 页上显示 40 个结果,并且只希望返回该页面上的 40 个结果(例如,如果我在第三页,我希望仅返回第 81 至第 120 条结果)。

对于分页显示结果,我曾见过多种方法,但没有一种方法能够做到百分之百有效。我所推荐的方法可以最大程度地减少全文查询执行的次数(实际上,对于要分页显示的每个结果集只需执行一次),并将 Web 服务器用作一个简单的缓存。从更高的层面来讲,您只需在全文查询中检索一个完整的主键和排位值行集合(如果需要,可以在架构中使用最佳选择并提取常用过滤器),并将其存储在 Web 服务器的内存中(这取决于您的应用程序和负载,想象将 <32 字节的典型主键大小与 <4 字节的排位大小相加 [等于 <36 字节],然后乘以通常返回的结果集 <1000 行,最后等于 <35K。假定一个在任何给定时间返回 <1000 个活动查询结果集中的一个活动缓存集,您将发现此活动缓存集在 Web 服务器上占用的内存少于 35MB - 这还可以接受)。

为了分页显示结果,该进程只遍历 Web 服务器的内存中存储的数组,并对 SQL Server 发出 SELECT 以便只显示需要显示的行和列。这又回到了全文查询仅返回主键和排位的概念中 - SELECT(甚至许多这样的查询语句)比全文查询的速度快许多倍。使用 SELECT 而不是与基表合并多个行,并结合多个其他策略,您可以保留 SQL Server 计算机上更多的 CPU 周期,并且更有效、更划算地利用 Web 领域。

另一种可以替代 Web 服务器端缓存的方法是在 SQL Server 自身中缓存结果集,并定义多种用于浏览这些结果的方法。虽然本文着重说明 Web 服务器 (ASP) 级别的应用程序设计,但 SQL Server 的可编程功能还为生成高性能的 Web 搜索应用程序提供了强大的框架。

小结
Microsoft SQL Server 2000 的全文搜索功能为索引和查询数据库中存储的非结构化文本数据提供了可靠、快速而灵活的方法。如果要广泛地将这种快速、准确的搜索功能应用于各种应用程序,那么很有必要充分利用其速度和精确性,来实现全文搜索解决方案。通过分布计算负载并通过某些巧妙的方式对数据进行组织,可以省下钱来购买其他硬件和软件,以摆脱因不必要的缓慢查询带来的困扰。在开发优秀的搜索应用程序时,通常要考虑到许多因素和注意事项,希望本文提供的信息和示例对您学习使用 SQL Server 2000 生成出色的 Web 搜索应用程序会有所帮助。

附录 A:实现全文搜索功能的最佳选择
改进全文查询性能和有效性的一种可行方法是实现“最佳选择”系统。此系统是一种很简单的方法,可确保某些与特定查询表达式匹配的行先于其他行返回。最佳选择没有复杂的预编程逻辑(例如,SharePoint Portal Server 就包含这样的逻辑),因此,通常是首选办法。

在本示例中挑选出最佳选择,并将唯一的主键和一些关键字存储在单独的表中。FREETEXTTABLE 查询对(非常小的)最佳选择表执行,并且从该查询中返回的任何结果都与对基表的 FREETEXTTABLE 查询结果一同返回。在给定这些搜索条件下,最先返回的将是所有“最佳选择”行,随后是被 MSSearch 视为关联程度最高的行(以递减顺序返回)。

下面是一个非常简单的用于创建最佳选择系统的示例脚本。

use myDb

create table documentTable(ftkey int not null, document ntext)
create unique index DTftkey_idx on documentTable(ftKey)

/*
   在此插入文档
   (要生成全文索引的所有文档)
*/

-- 为所有文档表创建全文目录和索引
exec sp_fulltext_catalog 'documents_cat', 'create', 'f:/ftCats'
exec sp_fulltext_table 'documentTable', 'create', 'documents_cat',
      'DTftkey_idx'
exec sp_fulltext_column 'documentTable', 'document', 'add'
exec sp_fulltext_table 'documentTable', 'start_change_tracking'
exec sp_fulltext_table 'documentTable', 'start_background_updateindex'

/*
   现在创建最佳选择表和索引
   (添加应该始终最先返回的文档)
*/
create table bestBets(ftKey int not null, keywords ntext)
create unique index BBftkey_idx on bestBets(ftKey)

/*
   在此插入最佳选择
*/

-- 为最佳选择表创建全文目录和索引
exec sp_fulltext_catalog 'bestBets_cat', 'create', 'f:/ftCats'
exec sp_fulltext_table 'bestBets', 'create', 'bestBets_cat', 'BBftkey_idx'
exec sp_fulltext_column 'bestBets', 'keywords', 'add'
exec sp_fulltext_table 'bestBets', 'start_change_tracking'
exec sp_fulltext_table 'bestBets', 'start_background_updateindex'

首先创建了一个通用的“所有文档”表,用于存储所有要全文索引的文档。通常情况下,文档表中包含其他列,但在本文中,只包含两列 - 主键索引和文档本身。全文目录和索引是为文档表而创建的。

接着创建了“最佳选择”表,用于存储所有全文查询中首先返回的特殊文档。此表只需具有全文主键列和文档本身(对将某些文档作为查询目标的策略进行优化,包括在该文档本身不包含的文档中添加其他关键字)。全文目录和索引是为最佳选择表而创建的。

最佳选择表和文档表可以共享文档(最佳选择文档还存储在常规文档表中,它们共享同一个主键值),也可以相互排斥(最佳选择文档只存储在最佳选择表中)。为便于检索,使最佳选择表与文档表互斥更为容易 - 这样做就无需从最佳选择和返回的普通搜索结果行集合中删除共享操作。另一方面,使用此方法维护文档可能很难实现,因为在此方法中,要在查询中添加逻辑来删除返回的行集合之间的共享文档。

如果给定上面的表,则可以创建两个存储过程,以便对最佳选择表和文档表进行搜索。可使用 Web 服务器级别的逻辑或其他存储过程来缓存和显示所需结果(与最佳选择一起使用时,请参阅下面有关缓存、显示和分页的一个完整、有效的示例)。

首先,创建一个用于检索最佳选择行(如果有)的存储过程:

create procedure BBSearch @searchTerm varchar(1024) as

select [key], [rank] from freetexttable(bestBets, keywords, @searchTerm) order by [rank] desc

确保已对传入搜索字符串进行清理,以避免在服务器上随意执行 T-SQL,并确保用单引号将该字符串括起。这种情况下,使用 FREETEXTTABLE 比使用 CONTAINSTABLE 要好,因为 FREETEXTTABLE 将采用寻根溯源功能,并找到与任何搜索词相匹配的最佳选择。

接下来,第二个存储过程检索与常规搜索标准匹配的文档(如果有):

create procedure FTSearch @searchTerm varchar(1024) as

select [key], [rank] from freetexttable(documentTable, keywords, @searchTerm) order by [rank] desc

此外,请确保已清理传入搜索字符串,并用单引号将该字符串括起。

执行这些存储过程时,应该在两个存储过程中传入相同的搜索词,首先执行最佳选择搜索,然后执行普通全文搜索。下一节更全面地介绍了在构建 Web 搜索应用程序时,如何与其他全文搜索技术一起使用最佳选择。

附录 B:使用最佳选择、结果分页和有效全文查询逻辑的示例应用程序
在本例中,我们实现了一个几乎利用了本文介绍的所有优化方案的 Web 搜索应用程序。我们对联机零售商目录使用简单的搜索引擎方案,并假定在通信量很高的情况下,所有用户都期待在很短的响应时间内获得结果。本示例使用了前一节中的最佳选择表和存储过程。

此应用程序只是一些可用于实现最佳全文搜索性能的高级策略的简单示例。本示例使用了 ASP,也可使用 ISAPI、ASP.NET 或其他平台来实现具有各自优缺点的类似解决方案。会话对象并不一定对所有应用程序都适用,如果使用不当,可能带来一定程度的危险。在本例中,我们使用会话对象来实现快速有效的缓存机制 - 当然还有许多其他方法可以在不同程度上实现该功能。

下面是 ASP 页的通用代码:

<% @Language = "VBScript" %>
<% Response.buffer = true %>
<html>
   <head>
      <title>FT 测试</title></head>
   <body>
<pre>
----------------- 开始测试 ------------------

<%

Dim firstRow   ' 分页显示行时的第一行
Dim lastRow      ' 分页显示行时的最后一行
Dim pageSize   ' 页面大小(每次的行数)
Dim cn      ' 连接对象
Dim rs      ' FT 主键/排位返回的结果集(重复使用)
Dim useCache   ' 使用缓存或命中 FT(0:不使用;1:使用)
Dim alldata      ' 要缓存的结果行集合
Dim bbdata      ' 要缓存的最佳选择行集合
Dim connectionString   ' SQL 连接字符串

' 确定是否要从缓存获取数据
' 默认为否,否则接受传入的数据
if (request.Form("useCache") <> "") then
   useCache = request.Form("useCache")
elseif (request.QueryString("useCache") <> "") then
   useCache = request.QueryString("useCache")
else
   useCache = 0
end if

' 设置常量
pageSize = 24
firstRow = 0
lastRow = 23
connectionString = <在此输入您的连接字符串>

'----------------------------------------------------------------'
' 显示与最佳选择/搜索词匹配的简单主键/排位                       '
'----------------------------------------------------------------'
Private Sub SearchNPage()

   Dim p         ' 循环通过行时的计数器
   Dim numRows      ' 缓冲/结果集中的总行数

   if (useCache <> "1") then ' 获取最佳选择/结果并将其缓存

      Dim queryArg   ' 传入的查询词
      if (request.Form("searchTerm") <> "") then
         queryArg = request.Form("searchTerm")
      elseif (request.QueryString("searchTerm") <> "") then
         queryArg = request.QueryString("searchTerm")
      else
         response.Write("未提供搜索词" & VbCrLF)
         exit sub
      end if     

      ' 理想情况下,应该在此清理查询词...
      ' 添加自定义的清理逻辑,以防止
      ' 随意执行 SQL

      ' 调用 CleanString(queryArg)

      ' 建立与 SQL 的连接
      Set cn = Server.CreateObject("ADODB.Connection")
      cn.Open connectionString

      ' 从传入的干净字符串中获取最佳选择匹配项
      set rs = cn.Execute("exec BBSearch '" & queryArg & "'")

      ' 如果有最佳选择,则获取最佳选择
      if not(rs.EOF) then
         bbData = rs.GetRows
      end if

      ' 现在从传入的干净字符串中获取普通匹配项
      set rs = cn.Execute("exec FTSearch '" & queryArg & "'")

      ' 如果未返回任何结果,则结束
      if (rs.EOF and IsEmpty(bbdata)) then
         response.Write("没有匹配的行" & VbCrLF)
         call ConnClose
         exit sub
      end if

      ' 否则,获取行
      if not(rs.EOF) then
         alldata = rs.GetRows
         Session("results") = alldata
      end if

      call ConnClose

   else ' 从缓存加载 (usecache=1)

      alldata = Session("results")

      ' 在此获取要使用的行范围
      if (request.Form("firstRow") <> "") then
         firstRow = request.Form("firstRow")
         lastRow = firstRow+pageSize
      elseif (request.QueryString("firstRow") <> "") then
         firstRow = request.QueryString("firstRow")
         lastRow = firstRow+pageSize
      end if

   end if ' useCache<>TRUE

   ' 对于本应用程序,只是打印出所有最佳选择
      ' (可能比页面大小大),然后分页显示普通结果
      ' 此处假设:在使用缓存时,如果没有新的最佳选择,
' 则使用以前显示的最佳选择
   if not(IsEmpty(bbdata)) then
      response.Write("最佳选择:" & VbCrLf)
      for p = 0 to ubound(bbdata, 2)
response.Write(bbData(0,p) & " "  & bbData(1,p) & VbCrLf)
      next
      response.Write(VbCrLf)
   end if

   ' 返回搜索结果(可能只有最佳选择)
   if not(IsEmpty(alldata)) then
      if uBound(alldata, 2) < lastRow then
         lastRow = uBound(allData, 2)
      end if

      response.Write("搜索结果:" & VbCrLf)

      for p = firstRow to lastRow
response.Write(allData(0,p) & " "  & allData(1,p) & VbCrLf)
      next
   end if  ' not(IsEmpty(alldata))

End Sub

'----------------------------------------------------------------'
' 关闭并清除连接对象                                             '
'----------------------------------------------------------------'
Private Sub ConnClose
   rs.Close
   Set rs = Nothing
   cn.Close
   Set cn = Nothing
End Sub

call SearchNPage

%>

---------------- 测试结束 ----------------

<form action="<本页>" method="post">
<input type=submit value="next <%=pageSize%> rows" NAME="Submit1">
<input type=hidden name="useCache" value="1">
<input type=hidden name="firstRow" value=<%=lastrow+1%>>
</form>

</pre>
   </body>
</html>

一个简单的 HTML 窗体页面即可像下面一样利用上面的脚本:

<html>
<head><title>输入搜索词</title>
</head>

<body>

<form action="<搜索 ASP 页面>" method="post">
搜索词:<input name="searchTerm">
<p>
<input type="submit" value="Search">
</form>

</body>
</html>

正如以上两个代码示例所示,创建可执行有效全文查询(用最佳选择完成)并缓存和分页显示结果的 Web 应用程序,并不需要花费太多的工夫。只需使用最低的系统开销,即可添加用于提供其他数据、增强最佳选择的外观以及在搜索结果中导航的逻辑(此外,强烈建议您实现其他用于错误处理、安全设置和清理传入数据的严密逻辑)。

通过上面的高级建议和示例,使用 SQL Server 2000 全文搜索设计和实现快速可缩放的 Web 搜索应用程序就是轻而易举的事情了。

附录 C:资源
Full-Text Search Deployment(英文)

是那些初次接触全文搜索的用户的最佳参考。介绍了填充方法及硬件和软件需求,并为使用 SQL Server 2000 全文搜索提供了提示、技巧和其他文档。

全文搜索公共新闻组 (microsoft.public.sqlserver.fulltext)

查找有关全文搜索问题的答案以及有用提示和技巧的理想场所。全文搜索新闻组是 SQL Server 开发小组和博学的 Microsoft MVP 成员经常光顾的场所。

SQL Server中删除重复数据的几个方法


    数据库的使用过程中由于程序方面的问题有时候会碰到重复数据,重复数据导致了数据库部分设置不能正确设置…… 
  方法一
代码内容
declare @max integer,@id integer 
declare cur_rows cursor local for select 主字段,count(*) from 表名 group by 主字段 having count(*) > 1 
open cur_rows 
fetch cur_rows into @id,@max 
while @@fetch_status=0 
begin 
select @max = @max -1 
set rowcount @max 
delete from 表名 where 主字段 = @id 
fetch cur_rows into @id,@max 
end 
close cur_rows 
set rowcount 0 
  方法二
  有两个意义上的重复记录,一是完全重复的记录,也即所有字段均重复的记录,二是部分关键字段重复的记录,比如Name字段重复,而其他字段不一定重复或都重复可以忽略。
  1、对于第一种重复,比较容易解决,使用
代码内容
select distinct * from tableName 
  就可以得到无重复记录的结果集。
  如果该表需要删除重复的记录(重复记录保留1条),可以按以下方法删除
代码内容
select distinct * into #Tmp from tableName 
drop table tableName 
select * into tableName from #Tmp 
drop table #Tmp 
  发生这种重复的原因是表设计不周产生的,增加唯一索引列即可解决。
  2、这类重复问题通常要求保留重复记录中的第一条记录,操作方法如下
  假设有重复的字段为Name,Address,要求得到这两个字段唯一的结果集
代码内容
select identity(int,1,1) as autoID, * into #Tmp from tableName 
select min(autoID) as autoID into #Tmp2 from #Tmp group by Name,autoID 
select * from #Tmp where autoID in(select autoID from #tmp2) 
  最后一个select即得到了Name,Address不重复的结果集(但多了一个autoID字段,实际写时可以写在select子句中省去此列)

SQL server全文检索建议


    全文检索允许在 Microsoft SQL Server 2000 表内对字符数据进行基于单词或短语的索引。全文检索由下面的基本组件组成:
    全文索引启用了全文目录的创建和填充,它们在 SQL Server 以外维护并且由 Microsoft 搜索服务管理。
    全文检索使用新的 Transact-SQL 谓词(CONTAINS、CONTAINSTABLE、FREETEXT 和 FREETEXTTABLE)来查询这些已填充的全文目录。
全文索引
    如果正在对不到一百万行的表进行全文索引,那么几乎不需要进行性能优化。如果对大型 SQL Server 表(其中包含创建大型全文目录的上百万行)进行全文索引,那么这会持续进行大量的读取和写入活动,因此必须配置 SQL Server 和全文目录,以通过跨多个硬盘驱动器进行负载平衡来获得最优磁盘 I/O 性能。同时还需要考虑硬件配置、Microsoft Windows? 2000 或 Windows NT? 4.0 系统配置、SQL Server 2000 配置、全文目录和数据库文件的实际位置。
硬件考虑事项
    多个 CPU:一到四个 500 MHz Xeon III 处理器。
    内存:1 到 4 GB 的物理 RAM。
    具有几个通道的多磁盘控制器,或具有多通道的单磁盘控制器。
    磁盘 I/O 子系统:RAID0(没有容错保护的磁盘条带化)、RAID0+1 和 RAID5。
Windows 2000 或 Windows NT 4.0 系统配置的考虑事项
    如果在 Windows NT Server 4.0 上安装 SQL Server,那么 pagefile.sys 文件的大小需要设置为可用物理 RAM 量的 1.5 到 2 倍。在有较大 RAM 量的 Windows 2000 Server 上安装 SQL Server 时,可以不用考虑这一条。
    Pagefile.sys 文件需要放置在它们自己的驱动器(RAID0 或 RAID0+1)上,最好在单独的控制器或至少是与共享控制器分离的单独通道上。
SQL Server 配置的考虑事项
    在大型表(超过一百万行)中进行完全填充后,考虑将新特性 Change Tracking 与 Update Index in Background 和 Update Index 对 Incremental Population 一起使用。有关何时使用 Change Tracking 对 Timestamp-based incremental 填充的更多信息,请参见维护全文索引。
全文索引和目录考虑事项
    全文索引或填充全文目录应该在系统活动较少时进行,通常在数据库维护窗口完成。
    将全文目录文件放置在它自己的磁盘控制器上,或有多个通道的单个磁盘控制器上单独的通道中。
    将数据库文件放置在与全文目录文件不同的独立的磁盘控制器上,或有多个通道的单个磁盘控制器上单独的通道中。
    具有四百万或两千万行的 SQL 表的全文索引可能要花费几个小时或几天来完成。考虑知识库文章 Q240867 中提供的选项:"INF:如何移动、复制和备份 SQL 7.0 全文目录文件夹和文件"。
全文检索
     如果正在对不到一百万行的表进行全文检索,那么几乎不需要任何性能优化。(一百万行只是常规的分界点。)如果要对超过一百万行的表进行全文检索,请考虑适当的全文检索谓词(CONTAINS 与 CONTAINSTABLE 或 FREETEXT 与 FREETEXTTABLE)以及平均行数和查询超时的考虑事项。
    使用带有新的 top_n_by_rank 参数的 CONTAINSTABLE 或 FREETEXTTABLE 来限制返回的行数。top_n_by_rank 指定只返回以降序排列的前 n 个最高等级的匹配项。仅当指定了整数值 n 时应用。另外,应该考虑使用 TOP 子句来限制在 CONTAINTSTABLE 或 FREETEXTTABLE 的结果集中返回的行数。请查阅知识库文章 Q240833:"FIX:通过支持 TOP 改善全文检索性能",以获得更多信息。
    如果试图通过附加的 WHERE 子句限制从全文查询得到的结果,那么 WHERE 子句是在与 SQL 表结果联接 (JOIN) 之后而非之前应用。否则,结果集会不正确,因为合格的行会从结果集中省略掉,而没有任何对客户端的提示。若要限制全文检索查询的结果,请使用 CONTAINSTABLE 或 FREETEXTTABLE 谓词中的 Top_N_Rank 参数。
    如果通过 Web 或 Microsoft Internet Information 服务 (IIS) 接口使用 SQL Server 全文检索,并且正在搜索大型表(超过一百万行),那么当使用 CONTAINS 或 FREETEXT 谓词时,请考虑将 IIS 查询超时默认值增加 20 秒到 30 秒。
    如果在 SQL 查询中使用多个 CONTAINS 或 FREETEXT 谓词,并发现全文检索查询性能较差,请减少 CONTAINS 或 FREETEXT 谓词的数量,或使用"*"在查询中使用所有全文索引列。
    在全文查询中使用任何一个全文谓词时,例如 CONTAINS (pr_info, 'between AND king'),还可能遇到错误 7619:"查询只包含忽略的单词"。单词"between"是一个忽略词或干扰词,即使带有 OR 子句,全文查询语法分析器也会将其视为一个错误。考虑将查询重新写为基于短语的查询并删除干扰词,或考虑知识库文章 Q246800 中提供的选项:"INF:正确分析 FTS 查询中的引号"。还请考虑使用 Windows 2000 Server:它对索引服务的断词文件有一定增强。
    什么是 RANK 以及当与 CONTAINSTABLE 和 FREETEXTTABLE 谓词一起使用时如何确定 RANK?全文 RANK 值是根据包含唯一词的行出现的频率确定的。在确定返回行的 RANK 值中发挥作用的一个因素是这个唯一词在该行的全文索引列中出现的频率。另一个因素是唯一词在表中出现的总次数(这用于规范化概率)。结果集中返回的 RANK 值是互相关联的。因此,不可能将 RANK 值解释为百分比,或将 RANK 值分组为高/中/低范围。请将 RANK 视为一种对特定查询和结果集排序的方法。
    当确定是在一个全文目录中包括多个 SQL 表,还是每个全文目录一个 SQL 表时,还有一些全文索引和检索的考虑事项。当考虑大型 SQL 表的这种设计问题时,在性能和维护之间应进行权衡,而您可能希望对您的环境测试这两种选项。如果选择在一个全文目录中有多个 SQL 表,则会因运行全文检索查询时间较长而带来开销,因为增量填充会强制所有其它 SQL 表的全文索引都放在该全文目录中。如果选择每个全文目录具有一个单独的 SQL 表,并对多个 SQL 表进行全文索引,就会引起维护单独的全文目录的开销(每个服务器总共只能有 256 个全文目录)。

 

原创粉丝点击