友好连接
2008年11月27日星期四
敏捷测试的最佳实践,三向敏捷测试转变
简洁,轻量的敏捷开发模型是为了提供给软件开发团队一种迅速应对客户需求变化,能够高效完成项目工作,降低整体风险的开发模式。敏捷的测试也是服务于这个目标的测试团队对测试工作的敏捷定义。
传统测试模式下成长起来的测试团队要如何转向敏捷,从个人和团队的两个层面又要做出那些转变呢?有什么方法和标准衡量敏捷测试团队的绩效?如何帮助团队的每个人规划正确的发展路线?团队在部署敏捷的过程中又会遇到哪些问题呢?本文主体就这些问题展开论述。
有关敏捷测试团队和个人的绩效
在我们过去一年多开发敏捷项目的令人难忘的经历中,测试团队携手开辟了一条新的道路,并发展至今。测试团队也曾多次因其突出的能力,积极的态度以及卓有成效的工作成果屡受嘉奖。今天我们仍然在思考怎样做好敏捷测试,因为我们仍然遇到更新的问题,我们在积累成熟经验的同时也在不断尝试改进原有方式和突破对新问题的困扰。在这里,我们的实践或许因基于幸运的历史背景和人文环境,能够基本成功的部署敏捷,但我们对敏捷测试在整个软件开发过程中的角色定位,和职责的理解,仍然对将要采用敏捷测试,以及感兴趣于敏捷的测试团队,和对那些需要从传统测试转变到敏捷测试模式的团队起到参考作用。
“如何看待敏捷测试和对测试人员做绩效考评呢?”——敏捷团队中无论开发还是测试都不是个人的开发和测试,这是团队的工作。一名好的测试人员除了能够做好本职测试工作外,表现为愿意并能够做超出原有范围的工作,能够并愿意帮助团队其他成员解决其他复杂问题,实现团队的共同目标。测试人员能够主动发现并弥补团队中的重要缺失的环节,帮助团队其他成员完成工作任务。
测试团队的职责也从仅仅发现问题的工作中向着眼于整个项目质量保障转变。因此不难得到结论,在敏捷团队中,优秀的测试人员身上有其他成员的影子。在时间紧迫的情况下,他能转变成其他角色,做出更多的创造性的成绩。充分地发挥了个人战斗力,在帮助他人的同时,自信的态度和各项工作中的技能得到增强,自身也可以获得更大发展。
在一次关于敏捷开发、测试经验交流中,我们认为敏捷测试团队更需要其他团队的协助,在设计测试用例时,团队的设计人员应该帮助测试团队设计测试用例,并帮助测试团队做更多的面向客户环境的真实测试。开发团队也要确保开发任务的按时推进,和测试团队保持紧密的合作,在测试任务紧要时,也能够转变其职能帮助测试团队完成测试任务。
测试人员向敏捷转变所需要的技能储备
这引出我们今天要探讨的一个问题,测试人员如何在技能上做好准备以面临敏捷开发的全新挑战。我们认为,一名优秀的敏捷测试人员,需要有较强的学习能力,至少有主动学习的意愿。除了需要了解各种类型测试以及各种测试工具,测试技术外,也需要了解项目中软件设计模式,软件语言,以及项目的程序组织架构以便建立和团队的共同语言空间。
因为敏捷团队是一个高度协作的团队,在这样的团队中工作需要很好的沟通技巧,语言能力和协作能力。
除此之外,团队的每个成员,都应该认真学习并努力寻找适合自己团队的敏捷模式,并沟通以达到团队成员对敏捷开发模式的统一认识,使得团队其他成员对自己工作的充分理解和建立起成员之间的相互信任。
测试人员向敏捷转变所需要的方法
培养好的敏捷测试人员,需要培养其技术能力,也需要用正确的培养成员的敏捷思想。敏捷的方法指导敏捷团队行动,是敏捷测试原则的实践。从一开始,就深刻影响着团队中每个人。当然,方法不是放之四海皆准的,需要团队对敏捷原则深入理解,执行敏捷测试实践后逐渐形成的规律。而一个传统的测试团队,在固有的行为规律下,在成熟的产品线里,或者层次分明的复杂组织结构里如何做好向敏捷的转变呢?似乎这种改变给许多人带来希望的同时伴随着一点恐慌?我们有没有可行的策略、方法可以遵循呢?可否让团队又能够发挥在传统开发模式下的力量集中的优势,又能够做到敏捷的随需应变呢?回答是肯定的。
在做转变的实施前,我们需要有心里准备,任何从传统开发到敏捷开发转变不可能一蹴而就,自然也没有人能够将一个传统开发模式下的测试团队一夜之间变成彻底的敏捷。对这些还没有敏捷起来,但仍然以此作为目标的项目团队我们建议循序渐进,基于笔者的亲身体验,提供以下实施的方法请大家参考。
首先我们建议采用迭代的开发模式作为向敏捷的模式转变的起点。很多传统开发模式或者基本上还是瀑布式的开发,或者是周期性的瀑布式开发,这些都不是敏捷的迭代。敏捷的迭代是高度的迭代,不是瀑布开发的不断累加。换句话说,传统开发是传递性的工作,一方完成,另一方接手。而敏捷活动的迭代行为更强调尽早开展各项活动,从迭代的一开始就协同工作,共同实现团队迭代的目标。而一旦抵达迭代的周期中最后一个工作日,此迭代宣布退出。
当完成了向迭代活动的转变完成后,接着,我们开始寻找项目过程、管理、执行中最紧要的问题,并使用敏捷开发中的最佳实践来一一解决这些实际问题。也许,一开始这个过程是很缓慢,而且很难做到一步成功,但是必须通过不屑的努力和足够的耐心,慢慢转变团队的固有思维方式,并最终努力获得团队对改进后结果的统一认可。而一个问题被解决,或者不再是项目中最严峻的问题时,我们应该开始寻找下一个待解决的困难了。重复这个过程直至成功的将团队中有悖于敏捷原则和实践的过程和方法调整过来,同时将正确的思路和方法带给团队。
在最近的几次与其他敏捷测试团队的讨论中,我们同时了解到许多软件开发项目中的测试团队遇到过类似的一些问题,如开发团队没有做单元测试或做得太少,继而在开发过程中的遗留了大量质量缺陷和频繁的回归现象。这使得测试压力急剧加大,测试过程严重受阻,甚至影响到整个迭代的退出和项目的输出结果等等。又或者传统的开发中的测试团队因为很少有条件去认识客户,了解和实际用户相关业务需求。测试脚本和用例的设计只是基于开发人员撰写的功能说明。因此,难以做到对需求变化做出快速反应。经过讨论,我们推荐给对这样的团队如下参考方案。
首先开始采用测试驱动开发 (Test Driven)。
开发人员首先要善于使用测试驱动开发方法写每一行代码,先写测试脚本后写代码,并反复使用单元测试脚本验证所写代码的正确性。
列出需求
为需求撰写一个单元测试脚本
执行测试确信测试结果是失败的
然后,写上仅仅足够的代码以使得先前的测试可以通过
当所有测试通过了,便可以开始写下一个测试脚本
针对需求有效的实现所有测试脚本
另外,当需要代码重构时候,也应该先重构单元测试脚本,在改动代买之前同样先改写测试脚本。
尽早的开始测试,开始系统测试,不要等待到功能完全做好才开始。
了解计划中的待实现的功能,了解其权重分配,设计系统测试和功能测试用例。测试执行的一开始可以是针对部分功能的,之后可以逐步扩展。接着开始采用迭代的过程完成测试任务,即将测试任务划分为多个周期,一开始可以做些关键的功能性测试,可以对代码中的可复用部分(组件,构件)做完整的安全测试,性能测试,压力测试,并发测试,全球化测试等。接着的迭代周期可以做边缘化的功能测试和其他测试,最后的几个迭代应该用于回归测试,和关键的性能和稳定性测试。
然后策略性的进行自动化测试,设计并开发可以用于日后回归测试(Regression)和用户接收测试(Acceptance Test)的自动化脚本,持续维护与开发这些脚本。自动化测试为团队带来的是长期效益,自动化测试的开发也应该首先选择部分测试对象,例如,API,框架等比较稳定和关键的功能做功能测试的自动化;对产品的性能指标,压力测试也要较早的制定自动化测试的计划。
最后,要学会做静态测试,做好需求分析,做好对设计逻辑的分析。测试人员要更多的思考需求的可实现性,将自身作为第一用户积极参与项目和系统的需求分析,设计和开发。积极地参与前期工作,并迅速反馈给设计和开发其静态测试结果。
而且,要做好敏捷测试,我们需要转变测试等待开发的思想,测试人员需要了解开发,需要读懂代码,才能够更好的帮助开发人员分析和分离复杂问题。有甚者,测试人员可以成为开发人员的后备力量。当团队中需要更多的人撰写代码时,测试人员应该勇当其职。
敏捷测试的最佳实践:一敏捷的实质
从游戏开始……
有个非常有意思的游戏能够帮助大家理解敏捷和传统开发的差异。游戏有两个角色,一个是“老板”,另一个是“员工”,在 2 分钟内,“员工”需要在“老板”的完全指挥下,即“向前一步,向后一步,停,向左一步,向右一步”,完成 60 步移动的任务。“员工”需要执行“老板”的每一个指令,不允许做出相违背的动作。“老板”则不参与行动,只发出指令指挥“员工”的活动。我们体验这个游戏时,当场 60% 的参与者成功完成了任务,大致估计出我们的工作效率是 50%*60%=30%。游戏后,参与者被问及对这种行为方式的感受时,无论是“员工”还是“老板”都表示非常不满。
接着,大家又做了另一组游戏。2 分钟内参与者被要求独立的、自主的完成 60 步移动任务,在这次游戏里,所有参与者任务相同,大家可以自行决定、并依据自己的判断随时调整其步伐方向,快慢。最后,我们发现所有参与者不但毫无折扣的按时完成了任务,因而工作效率也达到 100%*100%=100%,而且所有人对于这种新的工作方式更是产生了极大的兴趣。
以上两个游戏方式的对比就折射出传统开发(前者)与敏捷开发、测试活动方式的对比,其中优劣不言而喻。
而敏捷开发、敏捷测试又是怎样一个概念呢?他们是否能够帮助我们的团队突破束缚,在日益激烈的竞争环境里表现得更为出色呢 ? 请参考我的这个系列文章——“敏捷测试的最佳实践”。
敏捷的价值
首先我们解释一下什么是敏捷,在字典中我们得到解释,敏捷,即反应迅速、可以快速变化。如今敏捷开发已成为众所周知的时髦 IT 词汇,在这个领域里敏捷又被诠释为迭代的,快速应对需求变化,轻量级,并且简洁。
图 1. 面对客户业务复杂度问题提出敏捷的解决方案
IBM 重视敏捷开发,敏捷的软件开发策略之也被广泛推广开来。中国软件开发中心是 IBM 软件部部署敏捷开发方法的重点实验室之一。我们也是 IBM 中国软件开发中心最早使用敏捷方法的开发、测试的团队之一。这篇文章主旨为帮助那些愿意采用敏捷,和正在采用敏捷开发、测试的团队正确了解敏捷的实质。
笔者做敏捷项目已经近两年时间,对于敏捷的理解,认为最为关键的是需要注意两个方面,它们是“高度迭代”和“持续不断的客户反馈”。
高度迭代:迭代就是指产品的开发过程中,一个完整的开发活动周而复始的进行,产品的功能、性能、可用性在周期活动的叠加中不断得到更新和加强。甚至指在一个迭代周期内产品活动也具显著的周期性。同时,团队间、团队内部成员的高度协作及时帮助解决了各成员的依赖性问题,因此,也促进了各个成员工作的顺利开展,保障了产品活动稳定的持续性、周期性。以测试为例,传统开发模式下,测试人员可以因进入测试阶段的条件不完全满足而继续的等待。而在高度迭代的敏捷项目里,不同的是,我们希望测试人员能够尽可能的做能够做的工作,尽可能的早工作。“等待”在敏捷开发、敏捷测试范畴里已是一种错误概念了。
持续不断的客户反馈:指在产品开发任何时期,代表项目业务(Business)的利益干系人(Stakeholder)都要参与到产品的需求分析,设计,以及其他活动的决策制定中来。致力于在短时间内帮助团队实现将客户的需求转化为高质量的可消费产品,并转化成利润。
敏捷测试的最佳实践,二方法与实践
如果您已经阅读过敏捷测试系列文章的第一篇,敏捷的实质,您应该已经了解敏捷的定义,了解什么样的团队是敏捷的团队了。而您也可能早已开始思考,什么是敏捷测试的实质?敏捷的测试团队又是如何形成自我管理、自我发展的组织呢?测试团队又是如何安排日常工作呢?敏捷测试活动与传统测试活动有很大差异吗?为了进一步让您了解如何将敏捷原则运用到活生生的日常测试活动中,我们为您推荐敏捷测试系列文章的第二篇——敏捷测试的实践。
在敏捷活动如火如荼的推广运动中,我们显然无法预知如何在您的特定的复杂环境中您能否最后达成所愿,也无法为您预测出前进道路的分岔口可能唯一的正确的线路,我们却可以为您点起一盏明亮“街灯”,在这迷雾中驱除黑暗。我们将为您提供一个可以借鉴和可供参考的成功的敏捷测试实践案例。我们将逐一向您介绍、分析这个案例中的敏捷团队的组织结构,主要的敏捷测试行为,迭代的测试模型和一套以四周为周期的敏捷测试活动时间表。
请您运用您已具备的敏捷实质、敏捷原则的知识,并结合您的独特项目环境、带着您的问题,与笔者一起再度分析这个案例,希望您最终也能得到满意的答案,并随后开始实施部署敏捷测试。
敏捷测试的实质
测试不仅仅是测试软件本身,还包括软件测试的过程和模式。产品发布后才发现很多问题,很可能是软件开发过程出了问题。因此测试除了需要确保软件的质量,即软件做了正确的事情,以及软件做了应该做的事情以外,敏捷的测试团队还要保证整个软件开发过程是正确的。
敏捷开发的最大特点是高度迭代,有周期性,并且能够及时、持续的响应客户的频繁反馈。敏捷测试即是不断修正质量指标,正确建立测试策略,确认客户的有效需求得以圆满实现和确保整个生产的过程安全的、及时的发布最终产品。敏捷测试人员因而需要在活动中关注产品需求,产品设计,解读源代码;在独立完成各项测试计划、测试执行工作的同时,敏捷测试人员需要参与几乎所有的团队讨论,团队决策。作为一名优秀的敏捷测试人员,他(她)需要在有限的时间内完成更多的测试的准备和执行,并富有极强的责任心和领导力。更重要的是,优秀的测试人员需要能够扩展开来做更多的与测试或许无关,但与团队共同目标直接相关的工作。他(她)将帮助团队其他成员解决困难、帮助实现其预期目标,发扬高度协作精神以帮助团队的最终获取成功。需要指出的是,团队的高度协作既需要团队成员的勇敢,更需要团队成员的主动配合和帮助。对于测试人员如此,对于开发、设计人员,其他成员也是如此。
敏捷测试的方法与实践
是的,敏捷测试也需要高度迭代工作、频繁得到 STAKEHOLDER、客户的反馈,需要动态调整测试计划、测试的执行。并且,敏捷测试人员参与到了更多的敏捷生产活动中,积极的影响了团队做出的决定和计划。
是的,“人”才是敏捷的实体,敏捷测试也是以人为本的。不难理解,“敏捷”的一切都围绕着人展开,如敏捷鼓励直接,平行的沟通;敏捷需要持续的客户反馈以及敏捷活动的设计,方案和决策需要团队协同制定等等,敏捷测试需要一支非同寻常的团队,不同于以往传统开发模式下的团队结构。关于敏捷团队、敏捷测试团队的组成和介绍,将是我们讲述敏捷测试实践的第一步。
“人”是重心,方法、策略是辅。为了适应不同的团队结构,不同的项目环境,敏捷项目和敏捷活动的实践也应该因“人”而异,但是,并不是说可以天马行空,我行我素。一旦脱离了正确的敏捷方法、和敏捷原则的指导,我们的敏捷活动就好比摸黑前行了。
这正是我们需要学习前辈和敏捷主义大师们的经验意义所在了,笔者在过去的实践中受益颇多的也正是前人的实践经验和方法。因此,学习前人的经验和方法,并运用这些最佳实践来帮助敏捷开发团队,甚至是传统团队来解决时下重要的问题是十分有意义的事情。笔者不敢妄自尊大将自己的一般实践纳入经典方法范畴,但经历了两年的研究和改进,笔者提出的敏捷测试的原则也得到了业内同僚和“大师”的普遍认可。经过多次和其他项目团队的经验交流,我们也不断的改进着我们的原则、方法。因此,笔者要非常感谢参与讨论的同僚们,没有你们的热情参与,也不会有今天的笔者信心百倍的执笔了。正如笔者在借鉴了前人的成功案例中的经验和方法之后定制了符合项目需求的测试原则一样,相信,读者们在阅读了笔者的敏捷测试原则和方法后,同样也会有所收获。而对笔者经历的敏捷实践活动中的方法和测试模型的讲解将成为我们讲述敏捷实践的第二步,也是本文的重点。
综上所述,笔者将运用本文的主要篇幅为大家讲解这个敏捷实践。它们是:
敏捷团队组织构成,敏捷测试团队的任务和使命;
敏捷开发团队以测试为驱动的开发方式——测试驱动开发,这是种独特的测试?还是开发?
递增型的迭代测试,它首先是对敏捷测试过程活动和生命周期模型的介绍,通过学习经典的敏捷增量测试模型,我们将敏捷测试的各类活动有机的组合到了一起。在此之上,对定制后的独特敏捷增量测试模型的分析和理解,帮助我们理解测试活动的规划和管理;
以及需要特别关注的递增型迭代测试的关键活动之一——“静态测试”;这也是笔者认为的最高难度、最具影响力的敏捷测试活动。它将测试团队最早的引入产品开发环节,测试人员以第一用户的角度判断设计的有效性,此活动较早的暴露了设计缺陷、避免了团队对目标的不一致理解等,是测试活动中最有创造性价值的部分;
最后,笔者将谈谈测试活动中的测试计划和管理,即关于测试任务估计,测试活动计划,各个重要测试活动时间分配与安排的介绍。
然而,敏捷测试不是一蹴而就的,做到真正的敏捷,无论是从传统测试模式向敏捷测试的过渡,还是组建全新的团队都是需要循序渐进的,同时也需要团队成员的通力合作和不断的实践来完善敏捷测试的实践原则和方法。
敏捷团队
考虑到敏捷团队的组织结构,让我们以笔者亲身经历的项目为例来说明。笔者曾共事的整支产品开发团队被划分成 4 个相对独立的敏捷开发队伍,而每支队伍拥有相同配置的 7 名成员,他们分别具有不同的职能属性。如图 1 所示,每支敏捷队伍组成成员角色包括 1 名 UCD(User Centered Designer),主要负责产品的主要设计,其工作主要包括界面设计、用户的用例设计等等; 1 名 Visual Designer,主要负责产品界面的色彩搭配、控件的外观设计和 UCD 界面设计方案的初步实现和美化;1 名 Information Developer,主要负责产品中信息的编辑和重要文档的撰写工作; 3 名开发人员,主要负责产品的实现。和 1 名 QA,主要负责产品质量的保障。(更多的我们将 QA 定义为具有相比于测试人员拥有更多责任的一个职能,在本文中,为了简便起见,我们仍称之测试人员)。4 支敏捷的队伍拥有相同的 SCRUM MASTER STAKEHOLDER。通常会在同一时间进入一个迭代周期,制定各自的敏捷计划,并在同一时间退出,发布各自功能实现。而 4 支队伍的劳动果实被集成到一起就形成了可发布的产品了。
图 1. 敏捷开发团队的组织结构
因为敏捷团队中只有 1 名测试人员,因此需要一臂承担测试策略的制定,测试计划,测试脚本,测试用例设计以及测试的执行,帮助团队发现潜在问题,并协助解决问题的工作。敏捷团队的敏捷原则也是测试人员敏捷活动的规范,测试也需要拥有和团队的良好沟通,高度迭代的活动和不断的获得 STAKEHOLDER 的反馈。那团队的结构与敏捷本身有什么直接关系呢?与敏捷测试又有多少关联呢?
谈到这里,想起曾经有朋友向我咨询有关敏捷团队的某些职能的人力配备的问题。其实,笔者也无意论证 7 个人为什么是最佳组合,为什么不是 17 个,20 个人的组合。但是,敏捷原则告诉我们敏捷团队是高度协同、民主和平等的团队,为了让团队中每个人充分高效的工作。相同职能下的组员至多不好超过 3 名,最佳配置也是不同职能下配置 1 个人头。因此、在这样一个小型、平行的组织结构里,沟通更加易于建立,沟通复杂度也相对较低,相比 17、20 人的团队组织,沟通的代价也小很多。相反,很难想象在一个敏捷团队中会拥有诸多不同风格的执行者,决策者将是个怎样的混乱情况。
此外,经历过敏捷测试的体验,我们发现一个单一的敏捷团队最好保持较小的“尺寸”。这是因为拥有很多测试人员的敏捷团队通常不但需要更大的实际工作量来匹配庞大的机体而导致团队任务量更巨大,更复杂,失去自我管理的信心,而每个测试人员也将要花费大量精力和时间投入到内部沟通,和可能因为内部缺乏一致而导致的更加频繁的反复沟通中。
图片垂直居中的使用技巧
“使用纯CSS实现未知尺寸的图片(但高宽都小于200px)在200px的正方形容器中水平和垂直居中。”
当然出题并不是随意,而是有其现实的原因,垂直居中是 淘宝 工作中最常遇到的一个问题,很有代表性。
题目的难点在于两点:
垂直居中;
图片是个置换元素,有些特殊的特性。
至于如何解决,下面是一个权衡的相对结构干净,CSS简单的解决方法:
.box {
/*非IE的主流浏览器识别的垂直居中的方法*/
display: table-cell;
vertical-align:middle;
/*设置水平居中*/
text-align:center;
/* 针对IE的Hack */
*display: block;
*font-size: 175px;/*约为高度的0.873,200*0.873 约为175*/
*font-family:Arial;/*防止非utf-8引起的hack失效问题,如gbk编码*/
width:200px;
height:200px;
border: 1px solid #eee;
}
.box img {
/*设置图片垂直居中*/
vertical-align:middle;
}

标准化设计解决方案 - 标记语言和样式手册 短语元素
在现实情况下,经常有必要加上非语义标签,以便实现特定的设计目标,主要是因为现在著名的浏览器都无法百分之百支持标准的缘故.有些CSS规则在部分浏览器中无法显示正确的效果,而这不幸的让我们在达成某些设计目标的过程中必须使用额外的标签.
有个重要的概念必须放在心上:那就是尽量尝试撰写语义化结构将能带来实际的好处.同时,对标准的支持虽然没有达到百分之百,但也已经越过临界点让我们现在就能使用符合网络标准的方法撰写网页.有些时候必须做点牺牲,但是坚持撰写越多的符合标准的标签,未来的工作就会越轻松.
显示效果 VS 结构标签
本章节将会讨论显示效果与结构标签的不同,更确切的说,是讨论使用< strong>替换< b>,以及使用< em>替换< i>的差异.在本章稍后,我们也会讨论几个其他短语元素以及它们在符合标准,结构化标签语法内的重要性.
你或许听说过某些人建议在需要粗体文字时要用< strong>替换< b>,但是他却没有进一步告诉你为什么需要这样的替换.在不知道"为什么"的情况下,实在很难期待其他网页设计者只因为听过需要这样做就改变他们对标签的使用习惯.
为什么< strong>和< em>比< b>和< i>好?
去掉< b>和< i>标签,替换成< strong>和< em>到底是有什么好处呢?其实这一切都是为了表达语义和结构,而不是为了只是显示效果,本书的所有示例也都努力遵循这个概念.
看看专家怎么说
首先,来看看W3C在HTML4.01的短语元素规范里是怎么叙述< strong>和< em>的(http://www.w3.org/TR/html4/struct/text.html#h-9.2.1):
短语元素能在文字片段之内加上结构信息,常见的短语元素意义如下:
< em> 代表强调
< strong> 代表更强烈的强调
所以在此讨论的是两种不同程度的强调.举例来说,就是一个单字或者短语,念的时候应该比较大声,音调较高,念的快些,或者是...嗯,就是比一般文字内容更强调.
W3C接着还叙述了下面这段内容:
短语元素的展示效果随着浏览器的不同,一般来说可视化浏览器应该以斜体显示< em>的文字内容,以粗体显示< strong>的文字内容.语音合成软件则能配合内容改变合成参数,像是音量,音调与速度等等.
啊哈!最后一句特别有意思,语音合成软件(之前我们称之为屏幕阅读器)将会正确处理必须强调的文字,这的确是件好事.
相对之下,< b>或是< i>只是单纯的显示效果标签.如果我们的目标是将结构与显示效果分离的话,使用< strong>和< em>就是正确的选择,单纯想要显示粗体,斜体文字的时候用css就好了.本章稍后会讨论更多例子.
接着看两个标识示例,帮助我们了解它们的差异.
方法A
your order number for future reference is: < b>6474-82071< /b>.
方法B
your order number for future reference is: < strong>6474-82071< /strong>.
又粗又美丽
这个情况是使用< strong>比< b>更适合的完美例子,我们打算让句子内的特定文字显示的更加重要.除了粗体显示订单编号以外,我们也希望屏幕阅读器也改变它们表达这段内容的方式:提升音量,改变音调或速度.方法B能够同时达到这两个目的.
< em>又如何?
同样的,以< em>取代< i>,能够同时表达重要性,而不只单纯的以斜体显示文字内容.来看看这两个例子:
方法A
It took me not one,but < i>three< /i> hours to shovel my driveway this morning.
方法B
It took me not one,but < em>three< /em> hours to shovel my driveway this morning.
强调语气
在前面的例子里(本书撰写时的真实情况),我的目的是使"three"这个字以强调语气表现,如同我大声念出这个字,视觉上,方法B在大多数浏览器里都会以斜体显示,而屏幕阅读器也会适当的调整音色,速度或音量.
只要粗体或斜体就好
必须注意的是,很多情况下只需要视觉上展示粗体,斜体的文字效果,换句话说,假设你的侧边栏里有一串链接列表,而你喜欢让所有链接用相同的效果显示:也就是粗体(图6-1)
图6-1.粗体链接放在侧边栏里的示例
除了视觉特色外,我们并不打算特别强调链接内容,这里就是以CSS改变链接外观显示效果的好地方,让他们不会被屏幕阅读器以及其他非可视化浏览器特别强调.
举例来说,你真的想让粗体链接被念得更快,更大声,音调更高吗?大概不会,这边的粗体完全只是显示效果.
font-weight相当于粗体
为了达到图6-1的显示效果,让我们假设链接栏放在id设为sidebar的< div>中,这样我们就能用CSS指定#sidebar之内的链接要以粗体显示:
#sidebar a{
font-weight:bold;
}
极度简单,让我觉得一提起就有些可笑,但是这的确是个帮助分离结构与显示效果的好方法.
那是粗体!
同样的,在思考斜体文字的时候也能应用类似的想法,在你不想强调的内容,只单纯想以斜体显示文字时,你能再度使用font-style属性通过CSS处理这些状况
让我们使用相同的#sidebar作为示例,举例来说如果你想使#sidebar里的所有链接显示成斜体,那么可以这样写:
#sidebar a{
font-style:italic;
}
又是个简单至极的概念,但是在结构化标记语法的领域里,我觉得讨论这些情况十分重要--使用CSS处理央视,代替显示效果标签的状况.有些时候最简单的解决方法也最容易被忽略.
共用粗体与斜体
在打算同时用粗体和斜体显示文字内容的时候,我觉得必须先思考一个问题,你打算传达什么程度的强调?根据这个问题的答案,我会选择适当的标签:< em>(强调)或< strong>(更强烈的强调),然后以选择的标签标记文字.
举例来说,以下面的例子来说,我原本打算让"fun"同时以粗体,斜体显示,最后我选择用< em>来强调这个字.
Building sites with web standards can be < em>fun< /em>!
大多数浏览器只会以斜体来显示这个字,要同时使用粗体和斜体,我们有几种选择.哦,我真的希望你同意上面这句话.
普通的< span>
方法之一,是以普通的< span>包在"fun"之外,并且指定CSS规则将所有< em>之内的< span>以粗体显示.标记语法看起来像这样:
Building sites with web standards can be < em>< span>fun< /span>< /em>!
而CSS看起来则像这样:
em span{
font-weight:bold;
}
明显的语义部分并不好,因为我们加上了额外的标签,但是这个方法人人仍然有用.
以class强调
方法则是为< em>标签指定一个class,并且以CSS加上粗体效果,标记语法看起来像这样:
Building sites with web standards can be < em class="bold">fun< /em>!
而CSS看起来则像这样:
em.bold{
font-weight:bold;
}
使用< em>就能达成斜体效果(同时在语义上强调了文字内容),而为它加上bold class则会使< em>之内的文字以粗体显示.
类似的方法也能用来修饰< strong>.这时我们能在加重强调某段文字的时候,设计italic class加上斜体效果,再配上< strong>原来就有的粗体效果.
标记语言看起来像这样:
Building sites with web standards can be < strong class="italic">fun< /strong>!
而CSS则是这样:
strong.italic{
font-style:italic;
}
概述
我想讨论这个主题十分必要,因此这是本书核心主题之一的良好范例:将内容与展示效果分开十分重要,很有好处:而将< b>与< i>标签换成结构上对等的标签(强调内容的时候)是个辅助达成这类分离目标的简单方法.
因此,下次当你听到某人提到"没错,你永远应该使用< strong>替换< b>"的时候,你就有足够的理由去支持这个论点了.
大多数情况下,很适合以< strong>或< em>传达强调语气,而当你只是要追求视觉上的粗体,斜体效果时,就用CSS吧.
技巧延伸
本章到目前为止,都把焦点集中在< strong>和< em>上,这两者只是W3C所谓的"短语元素"的一部分,在这本分我们来多看一些短语元素以及他们和标准之间的关系.
短语元素
除了< strong>和< em>之外,W3C HTML 4.01规范里完整的短语元素列表还包括:
< cite>: 包含其他引用,参考来源的资讯
< dfn>: 代表这是名词的定义示例
< code>: 代表一段代码
< samp>: 代表一段程序,script的输出示范
< kbd>: 代表要使用者输入的文字
< var>: 代表程序的变量
< abbr>: 代表缩写名词(像WWW,HTML,URL,Mass.等)
< acronym>: 代表略称(像WAC,radar等)
让我们深入地看几个标签,首先从< cite>开始:
< cite>的设计
< cite>是个值得讨论的有趣元素,在替换掉单纯指定显示效果的< i>标签时尤为重要.< cite>的用途是参照引用来源:标识作者或出版物.历史上来说,设计者或许会用< i>把书籍标题显示成斜体,但是在本章稍早的地方我们学到了CSS是指定显示效果的最佳工具.
你或许会建议用< em>表示出版物的标题,但是在引用书籍或其他出版物的时候,我们并不打算去强调,也就是将书籍标题显示成斜体(在印刷界也常使用下划线,但是这明显会和链接混淆).
于是就出现了为这个工作量身打造的< cite>标签,大多数浏览器甚至预设会以斜体显示< cite>标签中的内容.而我们也能加上CSS规则达成相同的目的.
规范
W3C的< cite>标签规范有点简略,在HTML 4.01规范中只简单提了(www.w3.org/TR/html4/struct/text.html#h-9.2.1):
< cite>: 包含其他引用,参考来源的资讯
这差不多是我们能掌握的说法了,但是我们也不清楚到底哪类资料可以放在< cite>中,但是从"来源"来看,我们至少可以把作者,出版物放进去.
让我们看看< cite>的用法:
The novel, < cite>The Scarlet Letter< /cite> is set in Puritan Boston and like this book, was written in Salem, Massachusetts
使用了< cite>标签之后,标题The Scarlet Letter 在大多数浏览器中都显示成斜体,我们将加上下面这段十分简单的CSS规则,以便浏览器在没有预设的时候也显示出正确的效果.
cite {
font-style: italic;
}
回顾一下,我们把标题书籍,其他出版物标题的< i>标签替换成< cite>,在大多数浏览器中,仍然能得到斜体显示效果,也再度使页面内容变得结构化语义化.当然这个结构一样能发挥CSS,让我们一起来瞧一瞧.
改变< cite>的样式
在我们讨论以结构,语义构建页面内容的过程中,我们也同样让页面变得更容易用CSS指定样式(以及修改样式).以< cite>标签为例,如果我们固定使用这个标签标记出版物的话,我们就能完全掌握现实的样式,在任何需要的时候都能轻易的修改.
假设所制作的整个网站,过程中使用了< cite>标签标记书籍参考资料以及出版物标题,我们加上全局CSS规则用斜体显示所有的< cite>元素,但是在几个月之后,我们希望不仅以斜体显示书籍出版无标题,还要用粗体,红色文字灰色背景.
当然,可以用几个CSS规则快速完成这项任务,立刻改变先前以< cite>标记的所有参考资料标题,如果使用< i>或者< em>的话就没办法明确指明对象了.
cite {
font-style: italic;
font-weight: bold;
color: red;
background-color: #ddd;
}
图6-2使大多数浏览器的显示效果,而这是另一个先写结构化标记的好例子,让你稍后能轻易修改全站的设计风格.
图6-2. 以< cite>标记,使用CSS样式的效果
发挥结构的潜力
除了容易使用样式之外,结构化标记也便于服务器端软件进行处理,带来有趣的应用.
举例来说,易用性提倡者Mark pilgrim在他个人网站"Dive into mark"(http://www.diveintomark.com/)里使用了不少< cite>标签,由于在网志内使用< cite>标签标记引用的任何出版物,因此Mark能够撰写程序,解析所有文章建立资料库吗然后根据参考到的引用来源进行分类(http://www.diveintomark.com/archives/citations/)
下图6-3是搜索我自己的结果,在Mark的网志中能找到两篇相关的文章,这全是靠< cite>标注"Dan Cederholm"完成的.
图6-3 Mark Pilgrim在"Dive into mark"制作的"posts by citation"结果(http://www.diveintomark.org/archives/citations/dan_cederholm/)
< abbr>和< acronym>
我还想提出两个短语元素,就是< abbr>(缩写名词)和< acronym>(略称).使用这些标签能够为缩写名词提供定义,让所有使用者看懂内容,提升网页的易用性.
让我们从新看看W3C在HTML4.01规范内定义的< abbr>和< acronym>用途:
< abbr> 代表缩写名词(像WWW,HTTP,URL,Mass.等)
< acronym> 代表略称(像WAC,radar等)
配合适当的title属性是这些元素能帮助不了解特定名词的使用者,举例来说,在标识"XHTML"缩写的时候,我们能这样使用< abbr>标签:
< abbr title="eXtensible HyperText Markup Language">XHTML< /abbr>
在这里使用< abbr>会使屏幕阅读器拼出缩写内容(X-H-T-M-L),而不是念出完整内容,相对的,使用< acronym>的话则会让他念出完整内容,而不是缩写.
使用< acronym>标签的示例如下:
< acronym title="North Atlantic Treaty Organization">NATO< /acronym>
我们也能使用两条听觉CSS规则,再次强调这些差异:
abbr {
speak:spell-out;
}
acronym {
speak:normal;
}
听觉样式让作者能特别为屏幕阅读器指定朗读方式,这能修改页面的听觉表达方式,引导标记结构,改变音调,音色等,让页面朗读的效果与视觉效果更一致.
定义一次
许多人都建议只定义一次在页面内反复出现的缩写,略称,他们认为每次名词出
现时重复定义太浪费空间了,而最好只在首次出现的时候加上title属性,我认为这么做有些道理,虽然当使用者被导引到页面特定段落时,可能因为看不到页首展开缩写,略称,从而无法从定义中获利.
利用你的判断力,决定何时(以及要多频繁)定义放在< abbr>和< acronym>内的名词吧.
显示效果
要在视觉上吸引读者,有些浏览器预设会在< abbr>和< acronym>下面加上1像素的点状底部边线,引诱使用者将鼠标移到缩写名词,略称上面,当鼠标移到上面后,浏览器就会以"工具提示"的方式显示title属性提供的定义内容.
对那些不预设显示点状底部边线的浏览器来说,可以通过定义CSS达到同样的效果.
abbr, acronym {
border-bottom: 1px dotted;
cursor: help;
}
我们也加上额外的规则,把光标转成"求助"(大多数浏览器都会支持),帮助使用者看出这不是可以点选的链接,而是以"工具提示"显示的定义内容(Mark Newhouse,"Real World Style:CSS Help",http://realworldstyle.com/css_help.html).
图6-4就是浏览器的显示效果,将"XHTML"扩展成定义文字,以及点状底部边线,求助光标:
图6-4.一般浏览器显示< abbr>的示例
兼容性问题
值得一提的是,在本书撰写的时候,windows版的Internet Explorer还不支持为< abbr>标签指定样式,显示工具提示. IE/Win支持< acronym>标签,这是为了鼓励某些设计者只使用< acronym>处理缩写名词与略称.
这么做或许很有吸引力,但是为了解决现实问题而使用错误的元素并不是件好事,对这个特定问题来说,我偏好根据规范表示名词,让正确支持的浏览器处理< abbr>标签的样式.让我们很快地看看几个还没提到的短语元素.
< code>
< code>元素的设计目的是在XHTML页面内展示代码示例,举例来说,当你想分享某段CSS时,你可以这样写:
< code>
#content {
width: 80%;
padding: 20px;
background: blue;
}
< /code>
一般来说,可视化浏览器会以定宽serif字体显示< code>标签中的内容.当然,我们也能加上CSS规则,指定我们喜欢的显示方式.
code {
font-family: Courier, serif;
color: red;
}
如此一来,< code>的内容就会变成红色的Courier字体了.
< samp>
< samp>元素是用来标识程序与script的示例输出的,举例来说,如果我想讨论正在编写的Perl script输出结果,我可能会标集成这样:
< p>When the script has executed, at the command line you will see the message < samp>script was successful!< /samp>.< /p>
这边我以< samp>把script的输出示例围起来,同时我们也能以CSS规则为程序输出示例设定独特的样式,就像刚才为< code>做的一样.
< var>
与< samp>相关的,< var>是用来标记程序的变量和引用用的,举例来说,如果我们正在讨论XSLT样式表,那么我能写下:
< p>I'm going to pass the parameter < var>lastUpdated< /var> to my main.xsl file.< /p>
许多浏览器会以斜体显示< var>标签的内容,但是你可以写一条简单的规则去掉预设值,如果你不喜欢斜体的话.我们能用CSS的font-style属性改变显示效果:
var {
font-style: normal;
font-family: Courier, serif;
color: purple;
}
最后让我们看看< kbd>元素,完成短语元素的部分.
< kbd>
< kbd>元素可以用来标记使用者要输入的文字,举例来说,如果我正在解释如何使用刚才指定的accesskey把光标切换到搜索框内,我可能会这样写:
< p>To quickly change focus to the search input field, Mac users type < kbd>Command+9< /kbd>.< /p>
你大概猜得到我接下去要说什么了,没错,只要使用简单的CSS规则,你就能调整所有的< kbd>元素的样式,与其他短语元素一样.
结论
回顾一下在这章中看过的内容,首先探讨< strong>和< em>优于同等显示效果的< b>和< i>的原因,同时也研究了如何单纯设定粗体或斜体显示效果,CSS是正确的方法.
也讨论了其他的短语元素,以< cite>标记人物,出版物开始,并证明了结构化标记语法对显示效果,资料解析的好处.
并示范了如何以适当的元件标记缩写名词,略称,提升网页的易用性,同时以额外的CSS展示,语音规则强化定义内容.最后则看到剩下的短语元素,每个元素都具备一般文字不同的预设样式,但是我们也能快速地为单一页面或者整个网站轻易的设计简单的CSS规则,为这些元素指定想要的显示样式.
WEB前端开发经验总结 Ⅰ
呵呵,说是WEB标准,不过我这里主要是对XHTML1.1 和 CSS2.1的一些经验总结。因为WEB含盖的内容实在是太多了,“WEB标准”是一系列标准的总称,包括HTML4.0、XHTML1.1、CSS2.1、XML1.0、RSS2.0、ECMAScript1.1、DOM1.0等等。所以这里要跟大家指出来一下,WEB标准不是我们所说的DIV+CSS。
刚刚上面提到了——DIV+CSS,呵呵,这里要说明下,这样说其实是不正确的。DIV+CSS准确的说法(个人的理解)应该是:采用W3C推荐的WEB标准中的XHTML1.1结合CSS2.0样式表制作页面的方法,DIV应该指的是XHTML标签,而CSS显示是指的CSS样式表了。
采用WEB标准开发的好处
那么W3C为什么会推荐这样的页面制作方法呢?下面我们就简单的看看采用WEB标准开发(个人理解的)相对以前TABLE布局的优势有哪些?
1、节约运营成本,省钱啊!
呵呵,能帮你省钱的东西,你会不会有兴趣?当然是十分的有兴趣了。看看我们的WEB标准制作方法是如何做到的?
采用WEB标准制作,我们可以做到表现很形式的分离,我们用XHTML来表现(数据),用CSS来控制(页面元素呈现的)形式。写的好的页面,XHTML代码中基本上都是用户要看的数据,还其他修饰性的东西,全部由我们的CSS来控制。这样一来我们的(XHTML)页面的体积就大大减小了,这样你在带宽上的费用就会大家降低了,这个怎么降低的,你可以想象一下,YAHOO的首页小1K,100W个人一起访问,那么带宽节约了多少?而且可以更充分的利用带宽。
而我们的CSS控制了,所有的页面元素的样式,现在想改网站的整体风格,你只需要花几分钟修改一下一个CSS文件,就可以轻松搞定了。维护的成本也下来了,省了不少钱了吧?还有,你开这个页面的速度会快很多啊,一个让你等半分钟的页面,除非里面的信息对你很有用,不然我们大家基本都没有太多的时间去用来等待的。
2、对用户友好更友好,且有机会获得更多的用户
现在来说说用户友好。首先我想把我们的用户来分下类。
第一类:普通用户(每个访问我们网站的人);
第二类:搜索引擎;
采用WEB标准开发的页面,结构清晰,页面体积小,浏览器兼容性好。普通用户访问的时候,页面打开速度快,而且不管用户使用那种浏览器,都能够正常访问(显示)页面,且页面的结构清晰,要找的数据可以很方便的浏览到。
而对搜索引擎来说,一个好的采用WEB标准开发的页面,都是做过SEO优化的,它访问起来很友好,很容易理解你的页面中哪里是标题(H1~H6标签),哪里是段落(p标签),哪里是段落里要强调的内容(strong标签) 等,它可以很容易的分析出来。而一个SEO好的站点,大家都知道,被搜索引擎收录的机会更多,这个也意味着您的网站会被更多的普通用户访问到,给你的站点带来更多的用户。
一个能帮我们省下大笔费用,提高工作效率。同时又能够提高页面浏览速度,对用户友好,甚至能够不花钱宣传,就能给你带来更多用户的技术。你说你会不会去使用它?呵呵,这个也正式我们的W3C推荐使用WEB标准开放网站的原因啊。而这个技术也得到了我们广大用户的认可,所以您现在需要学习WEB标准啊。^-^!
呵呵,温习完了基础课程,现在正式开始讲XHTML和CSS的技巧了。
合理的布局
有朋友会开始问了,怎么一开始就开始讲合理的布局了呢?呵呵,前面我们提到了一些知识点——“结构清晰、SEO优化、页面体积小、XHTML代码中基本上都是用户要看的数据”。这些东西,都是我们做了合理布局的结果。而且我个人觉得,我们采用WEB标准制作的一切都是从这个知识点开始的,所以我这里就先来说这个话题。
那么大家又会开始问,怎样的一个页面,才算是合理的布局的呢?恩,这个问题问题问得好,也是我们大家刚开始学用WEB标准的问得最多的问题之一,我也曾经常被这个问题所困扰,这里就说说我对合理布局的一些理解。
在开始讲合理布局的页面要达到的要素前,我们还是用个实例来讲解会更直观些。先来看看这个图片:
http://www.yaohaixiao.com/samples/myblog/index.htm
不错,这个是一个文章详细页,没有左右两栏布局,不过这里我重点要讲的是合理的布局,在稍后的文章中我会详细的介绍浮动元素。好,回到刚才的话题,大家看到了这个页面了。
我这里先把代码写给大家看看(省略了部分代码):
< html xmlns="http://www.w3.org/1999/xhtml">
< head>
< meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
< title>Ajax标签导航实例详解< /title>
< link href="css/article.css" rel="stylesheet" type="text/css" media="all" />
< /head>
< body>
< h1>海啸的地盘--享受生活,享受每一天!< /h1>
< form name="frmsearch" id="frmsearch" action="" method="post">
< label for="keyword">站内搜索:
< select id="topics">
< /select>
< input type="text" name="keyword" id="keyword" value="请输入搜索关键字" maxlength="60" />
< input type="reset" name="btnsearch" id="btnsearch" value="开始搜索" />
< /form>
< h2>Ajax标签导航实例详解< /h2>
< h3>代码篇< /h3>
之前整理发表了《XMLHTTPRequest的属性和方法简介》,它Ajax要使用的核心的技术之一,现在就来实际运用它。这个Ajax标签导航,是我很久前就写的一个脚本,很实用的(还被很多网站收录了哦),现在拿它来做实例讲解吧!当然个人能力有限,有什么不对的地方还请多包含!
演示地址:http://www.yaohaixiao.com/code/ajaxtab/index.htm
效果大家看到了,核心功能有:
1、将当前选中标签以特殊的样式显示
2、将异步加载的页面信息显示到指定的DOM节点中
我们来看看处理脚本的代码吧:
id="news" - news就是我们的导航标签的ID;
id="newsCnt" - newsCnt就是我们要写入信息的目标DOM节点;
class="first" - first当前(第一个)标签的样式;
id="news-0" - news-0 通过”-“分开,我们就分别可以得到news(导航标签ID),0(标签[li]在导航标签中的索引值)
网站重构 - 超链接
- 标签间的分割线
我罗列的这些东西,相信大家开始看出了些头绪了,呵呵,不过别急!在我们看处理的脚本之前,先让我们来看看导航标签的样式,主要是看看我们对分割线的处理(一点CSS处理的技巧)。
本来想偷个懒,让大家看我上边说的那篇文章,想想也就是Ctrl+C&Ctrl+V,都贴出来吧!呵呵!!!
不过还没有完,最后要说的就是innerHTML这个特性,这里我们还要感谢微软啊,innerHTML就是它的专利,我们就是用它来改变指定DOM内的HTML字符串的,而不用刷新页面。详细的信息大家还是google一下吧,我也要休息下啊!!喝口茶先!!^-^!
以上讲了这么多,我们最后来看看,我们这个ajax标签导航都用到了那些技术吧:
- XHTML
- CSS
- Javascript
- DOM
- XMLHttpRequest对象
- innerHTML
还有XML,我们这个例子没有涉及到。东西虽小,包含的(web前端开发)知识可是都用到了啊,我把我会的点东西都端出来了(要失业了),呵呵!
当然我很喜欢跟大家多交流,以后有时间,我们在来谈谈CSS的HACKS技巧,Javascript DOM编程等等的,今天就收工了,谢谢捧场先!!!
Copyright © 2007-2008 yaohaixiao.com, All rights reserved. Powered By: Yaohaixiao
< /body>
< /html>
看出来什么没有?(代码是很多,呵呵!)可能大家已经发现,整个页面里基本上都是用户要看的数据,其中只包含了很少(必要)的布局(XHTML)标签(请允许我这么说)。整个页面基本都是由最基础的h1~h6、p、ul、ol、li、form、div标签来实现的。
说到这里就要讲到我在前面提到的“结构清晰、SEO优化、页面体积小、XHTML代码中基本上都是用户要看的数据”,看看我的这个例子做到了没有?
结构清晰--也就是我们常说的,XHTML标签要结构化(语意化)。
什么叫结构化?
由于个人认为这个知识点是十分重要的,所以请允许我在这里多罗嗦几句,我们采用WEB标准的方法制作页面的优势就体现在页面结构清晰。我们以前用table布局的时候,我们的表现(数据)和形式(布局样式)是混在一起的,有很多冗余的数据混杂在一起,而大家再看看我上面给大家展示的代码,很明显,结构十分清晰。
呵呵,说了半天,还是没有说什么是结构化,什么才是结构清晰啊?不要急。还记得我刚才提到的那几个标签吗?
h1~h6--如果你要显示的数据是作为标题显示的时候,就用这些标签,因为这个标签的意思就是说,这个是一个标题,不仅我们的用户很容易理解h-head的英文字母缩写,我们的另外一个用户搜索引擎也可以很容易理解它。呵呵,看到了吧,一个充分结构化的页面,对用户是十分友好的。
p--Paragraph(段落)
ul--unorglized list(无序的列表)
ol--orglized list(有序的列表)
li--list item(列表项)
form--表单
div--division(区域)
我这么一写,大家估计开始明白了,原来XHTML标签是有着自己的意义的(至于其他的标签的语意义,大家可以自己到W3C看看它的解释,也可以查看语义化你的HTML标签和属性),所以我们讲要语意化的意思,就是用合理的标签来显示数据,比如前面提到的,是标题,就应该用h1~h6标签,如果是一个段落的介绍文字,那么就应该使用p标签,如果是显示的一个没有顺序的列表,就应该用ul标签,如此...
所以看这个例子里
Ajax标签导航实例详解
这么写就是很合理的,h2标签就说明这里是个标题。而这么写:
复制内容到剪贴板代码:
.title{
font-size:18px;
...
}
虽然你用了strong来强调说明这部分文字,但是还是没有h2标签来的直接明了。
还有这么写
Ajax标签导航实例详解
也不是我们推荐的,可以不用嵌套层的地方,我们尽量不要去过多的嵌套层,减少嵌套会让我们的浏览器解析起来会更容易,速度更快。
所我们使用WEB标准开发页面,绝对不是单纯的把以前的table换成DIV就OK了。而采用标准制作页面,也不意味着我们就不使用table。只要把握我刚才说的原则,使用合理的标签显示相应特征的数据。
其实我们的table标签是一个很好的用来显示二维数据的标签,而table标签也确实是设计出来用来显示数据的,而不是用来布局的,只是我们当时的设计师们都用table标签来布局了。关于table标签的使用,大家也可以去看看上面提供那篇参考文章《语义化你的HTML标签和属性》。
上面,我们讲到了要充分的语意化,其中我们提到了嵌套的问题,这个也是我们做到结构化的一个重要部分。个人认为一个最基本的原则就是尽量减少嵌套。至于为什么这么做,我刚才也给大家做了一个简单的介绍,结构清晰,浏览器解析快。其实结构清晰(够简单,嵌套少),对我们的搜索引擎同样是做起到了SEO的效果。怎么讲?大家想想,嵌套N多层,跟我们以前使用table设计时,table套table的区别就不大了,而那种含有大量冗余信息的页面,搜索引擎解析的时候也很费力啊。所以我们现在有了WEB标准,就不要再去犯以前的错误了。
说到SEO优化,让我们来看看我的例子中对LOGO的处理,XHTML代码如下:
< div id="topbar">
< h1>海啸的地盘--享受生活,享受每一天!< /h1>
< form name="frmsearch" id="frmsearch" action="" method="post">
< input type="text" name="keyword" id="keyword" value="请输入搜索关键字" maxlength="60" />
< input type="reset" name="btnsearch" id="btnsearch" value="开始搜索" />
< /form>
呵呵,当大家用浏览器浏览时,可能会以为这里是一个
对SEO有些了解的朋友都知道,搜索引擎搜索关键字的时候,是先对XHTML标签里的< meta>标签的keyword部分的文字,然后就是标题里的文字,接着就是正文中的h1~h6标签,然后是strong标签中的文字。而我们网站的名称,毫无疑问的会作为关键字的。
所以我这里用h1标签模拟了< img />标签的效果,将网站名称这个关键字在页面里出现了一次,而且是以正文中搜索引擎分析的优先级最好的h1标签,以说明该内容在页面中的关键性。具体的实现代码这里就直接写出来:
#topbar h1{
float:left;
width:220px;
height:58px;
line-height:58px;
overflow:hidden;
font-size:18px;
color:#060;
text-indent:-999px;
background-image:url(../img/logo.png);
}
#topbar h1 a:link,
#topbar h1 a:visited,
#topbar h1 a:hover{
display:block;
width:220px;
height:58px;
line-height:58px;
overflow:hidden;
background-color:transparent;
}
< h1>海啸的地盘--享受生活,享受每一天!< /h1>
看了代码,我想大家也很明白了,我们这里利用了text-indent、overflow:hidden;属性以及display:block。我们把LOGO图片作为h1标签的背景,然后用text-indent设置一个超大的负值,然后结合overflow:hidden;属性,让h1标签里面的字都超出到它的布局范围内,overflow:hidden后就在h1标签里看不到文字了,而LOGO图片背景显示出来了。
接着一个关键就是将a标签模拟成图片链接的效果。我们首先将a标签display属性设置为block,强制将a标签显示成块级元素(块级元素和行内元素的知识我们下面马上给大家介绍),给它设置布局大小,这样我们a标签里就是一个块了,而这个块刚好是我们背景logo图片的大小,让你点击的时候感觉是点的一个图片链接,而我们的a标签是嵌套在h1标签里的,继承了H1的text-indent属性,里面的文字超出了a标签的布局范围,没有显示出来,而显示了图片(其实是背景图)。呵呵,图片链接就这样被我们模拟出来了,不过为了更像图片链接,我们又给a标签添加了title属性,来模仿img标签的alt属性,呵呵,模拟得够像吧。
上面介绍了,一个技巧,其实主要是利用我们的CSS控制来实现的,不过我这里还不打算介绍CSS的技巧,接着我们的结构化的话题说下去。看看,其实在制作一个结构化充分的页面的时候,我们同时也在做SEO的优化。
结构,结构,刚才我提到了标签有块级元素和行内元素之分的,要是不理解好这两个概念,我们也不会很好的了解怎么合理的结构化页面,现在就来介绍它们:
块级元素
块级元素生成一个元素框,(默认地)它会填充其父级元素的内容,旁边不能有其他元素。换句话说,他在元素框之前和之后生成了“分隔”符。我们最熟悉的HTML元素是p和div.
行内元素
行内元素在一个文本行内生成元素框,而不会打断这行文本。行内元素最好的例子就是XHTML中的a元素。strong和em也属于行内元素。这些元素不会在它本身之前或之后生成“分隔符”,所以可以出现在另一个元素的内容中,而不会破坏其显示。
注意,尽管“块”和“行内”这两个词与HTML和XHTML中的块级和行内元素有很多共同点,但也存在一个重要的差别。在HTML和XHTML中,块级元素不能继承自行内元素(即不能嵌套在行内元素中)。但是CSS中,对于显示角色如何嵌套不存在任何限制。
恩,还是不能把CSS跟XHTML完全的分开谈,没办法。现在大家了解了什么是块级元素和什么是行内元素了吧。还是回过头来接着说我们的结构化吧,以上讲了这么多,我都是在讲XHTML标签要结构化,所以基本跟CSS关系不大,那么我们下面看看我写的这个页面的代码在没有CSS的情况想,是否结构清晰吧:
看看,页面是不是依旧很清晰,很容易看出,哪里是标题,哪里是导航,哪里是搜索表单?呵呵,其实我这么给大家看的页面,跟我们的搜索引擎查看页面的结构很接近了,到这里大家可能体会到一些搜索引擎友好的意思了吧?
其实让我们看看这个页面的DOM数状图,可能我们对一个简洁合理的结构化布局的页面有更直观的了解。
看看,是不是做到了我说的:XHTML标签充分的语意化、尽量少的嵌套、页面里基本只显示用户需要看的数据、对搜索引擎友好(没有CSS支持时,浏览页面时同样清晰,层次分明,十分容易查看的数据。),已做了基本的SEO优化
呵呵,说到这里我才回答了“怎样的一个页面才算是合理的布局的呢?”,做到上面4点,我个人觉得这个页面就已经做到了合理布局。而至于说要通过W3C的XHTML语法验证,只是很基础的开始。一个通过验证的页面和一个合理布局的页面是两码事的。
这里我再罗嗦一下,我一直都在说的是个人认为如何才算是一个合理布局的页面?要达到什么要求才能算合理布局?因为只有理解了这个,之后的CSS的技巧(浏览器的兼容处理等)都是围绕着让XHTML页面布局更合理来进行的。而不是单纯做到各个浏览器下都显示正常(一致)。所以请记住在开发时做到:
XHTML标签充分的语意化
尽量少的嵌套
页面里基本只显示用户需要看的数据(我们用CSS直接控制用户需要看的数据的显示的样式)
页面对搜索引擎友好(没有CSS支持时,浏览页面时同样清晰,层次分明,十分容易查看数据。),已做了基本的SEO优化
今天就谈到这里吧,下面我们会接着讨论CSS的处理技巧。
转自:http://www.cnblogs.com/junzhongxu/archive/2008/07/08/1237993.html
IE6实现min-width
今天整理文件的时候,发现自己以前的一些布局的解决方法躺在文件夹里很长时间了,翻翻老底吧
需要说明的是有幸也见到过CSSPLAY的老工程师站长对这个效果的实现,而且肯定是很早就给出来了。
闲话少说,上源码:
触发并利用IE6-layout的怪异特性,css实现:
css实现演示:
运行代码框
< html xmlns="http://www.w3.org/1999/xhtml">< head>< meta http-equiv="Content-Type" content="text/html; charset=gb2312" />< title>CSS实现最小宽度< / title>< / head>< body>
代码"> [Ctrl+A 全部选择 提示:你可先修改部分代码,再按运行]
扩展演示:
运行代码框
< html xmlns="http://www.w3.org/1999/xhtml">< head>< meta http-equiv="Content-Type" content="text/html; charset=gb2312" />< title>CSS实现最小宽度< / title>< / head>< body>
代码"> [Ctrl+A 全部选择 提示:你可先修改部分代码,再按运行]
CSS Expression
——相信许多人在用这个方法实现容器最小宽度时都时常会被莫名其妙的死机所困扰,最后往往无果而终。
这里特别需要指出的是两点:
1. IE6-的标准模式下和quirk模式下代表视口的元素是不一样的,前者为< html>,后者则为< body>;
2. IE6-在以上两种不同的模式下,其对包含内容溢出时的不同表现形式,从而导致了赋值判断上的死循环。解释起来有些啰嗦,自己实践一下吧。
CSS Expression实现最小宽度源码:
演示:
运行代码框
< html xmlns="http://www.w3.org/1999/xhtml">< head>< meta http-equiv="Content-Type" content="text/html; charset=gb2312" />< title>CSS Expression实现最小宽度< /title>< /head>< body>
代码"> [Ctrl+A 全部选择 提示:你可先修改部分代码,再按运行]
以上两种解决方法在IE6-的标准模式下和quirk模式下都可实现,IE Expression在这个应用中也未发现CPU效率问题。
2008年11月26日星期三
利用SqlDataAdapter进行分页
说到分页,很多地方都会用到,不管是windows程序还是web程序,为什么要进行分页?很简单,如果BlueIdea BBS帖子列表不分页的话,几十万条记录,可想而知.......
分页有几种方法,可以用存储过程进行分页,将要显示的记录写入一个临时表中,再从临时表中取出这些记录,取出的记录呢,也就是当前页的记录了。
我这里谈到的,是利用SqlDataAdapter Fill方法的重载,进行分页
public int Fill (
DataSet dataSet,
int startRecord,
int maxRecords,
string srcTable
)
参数:
dataSet
要用记录和架构(如果必要)填充的 DataSet。
startRecord
从其开始的从零开始的记录号。
maxRecords
要检索的最大记录数。
srcTable
用于表映射的源表的名称。
返回值
已在 DataSet 中成功添加或刷新的行数。这不包括受不返回行的语句影响的行。
示例方法:
DataTable tbl = SqlDataAdapter.Fill(objDst,1,10,"temp")
言归正传,在这个设计过程中,我们需要哪些参数呢?
1、_pagesize----int型,每页要显示的记录数
2、_pagecount----总记录数,这是需要算出来的
_pagesize可以在web.config中设定,至于_pagecount嘛,得首先知道总的记录数,所以,我们得先弄出记录数出来,大家可以用一条sql语句查询而得到,这里就不多说了。假定已得知记录数,现在计算总页数
_pagecount = (_recordCount % _pagesize==0) ? (_recordCount / _pagesize) : (_recordCount / _pagesize + 1);
这里用总记录数去除以每页显示的记录数,如果能整除,说明直接用记录数除以页记录数就能得到总页数,否则,就用记录数除页记录数再加上1就可得到总页数了
是不是具备这些就够了呢?
当然不够
想想,Fill方法时的第二个参数startRecord怎么赋值呢?如果是第1页的话很简单,直接传1,因为我们从第1条开始取嘛,如果是第二页呢?怎么办?
假定,我们每页显示10条,当前是第2页,那么应该从多少条开始取??大家应该想到了,应该从11条开始,也就是取11-20条,那这里的11怎么得到?
算法:
(_page-1) * _pagesize + 1
(2-1) * 10 + 1 = 11
其中_page是当前页,但这个数必须大于1
OK,有了这些基础,再来分页就简单了
.....
int _page;
int _pagesize = 10;
int _pagecount;
int _recordcount;
System.Text.StringBuilder sbPager = new System.Text.StringBuilder(); //用来显示分页
//在.aspx页中调用,用以显示分页代码
public string getPager()
{
return sbPager.ToString();
}
//计算出记录数
_recordcount = 134;
//判断当前页数
private void Page_Load(object sender, System.EventArgs e)
{
if(Request.QueryString["page"]==null)
{
_page = 1;
}
else
{
_page = Int16.Parse(Request.QueryString["page"]);
}
...............
}
//开始填充数据
private void FillList()
{
_recordcount= BLL.MessageBLL.getListCount(""); //取得总记录数
_pagecount = (_recordcount % _pagesize==0)?(_recordcount/_pagesize):(_recordcount/_pagesize+1); //得到总页数
int _minPage=1;
if(_page!=1)
{
_minPage = (_page-1)*_pagesize + 1;
}
this.repMsgList.DataSource = BLL.MessageBLL.GetList("",_minPage,_pagesize);
this.repMsgList.DataBind();
#region 打印页数
for (int i = 1; i <= _pagecount; i++)
{
if (i == _page)
//如果是当前页,不显示链接
sbPager.Append("" + i.ToString() + " ");
else
{
sbPager.Append("" + i + " ");
}
}
sbPager.Append(_recordcount + " Records");
#endregion
}
在前台中
分页:<%=getPager()%>调用就OK了
这样,初级的分页就完成了
但这样写代码很烦琐,看以后能不能写成一个自定义控件.
转自:http://www.cnblogs.com/junzhongxu/
如何快速生成100万不重复的8位编号
1. 如何生成8位随机数,生成的数越随机,重复的可能性当然越小
2. 控制不重复
3. 考虑性能
针对这个问题,我写了如下的示例来解决,希望能为有这类需求的人提供指导
生成100万条8位不重复数据的示例
USE tempdb
GO
-- 创建测试表
CREATE TABLE tb(id char(8))
-- 创建用于自动过滤重复值的唯一索引
CREATE UNIQUE INDEX IX_tb ON tb(id)
WITH IGNORE_DUP_KEY
GO
-- 测试数据插入的处理时间, 记录开始处理的时间点
DECLARE @dt datetime
SET @dt = GETDATE()
-- 插入随机数据
SET NOCOUNT ON
DECLARE @row int
SET @row = 1000000 -- 设置总记录数
WHILE @row >0
BEGIN
-- 显示提示信息, 表示还需要插入多行数据
RAISERROR('need %d rows', 10, 1, @row) WITH NOWAIT
-- 插入随机的位编码数据
SET ROWCOUNT @row
INSERT tb SELECT
id = RIGHT(100000000 + CONVERT(bigint, ABS(CHECKSUM(NEWID()))), 8)
FROM syscolumns c1, syscolumns c2
SET @row = @row - @@ROWCOUNT
END
-- 显示插入数据使用的时间
SELECT BeginDate = @dt, EndDate = GETDATE(),
Second = DATEDIFF(Second, @dt, GETDATE()),
GO
-- 显示最终的结果记录是否正确
SELECT COUNT(*) FROM tb
GO
-- 删除测试
DROP TABLE tb
解决中用到的技巧:
1. 控制产生的数据不重复,直接使用唯一索引中的 IGNORE_DUP_KEY 选项,使插入数据中的重复值自动过滤,避免手工处理重复
2. 使用 CHECKSUM 配合 NEWID() 函数,使生成的数据尽量随机,一般生成随机数会考虑使用 RAND() 函数,但这个函数是产生伪随机值,用下面的语句测试一下,会发现产生的数据全部是一样的,这不适用于想批量生成多个随机数,而NEWID() 函数生成的是GUID,基本上不会有重复的,再通过CHECKSUM将其转化成数字,这样产生重复的可能性会比较小
SELECT TOP 10
RAND()
FROM sysobjects
3. 在效率控制,使用循环+批量生成的方式,而不是传统的逐个生成。在SQL Server中,每个插入语句都会有一个内部的事务处理,如果逐条插入,则事务的开销太大,效率势必非常低;不考虑一次性生成100万数据,一则因为生成的数据可能有重复的,去掉重复就没有100万了,二则一次性生成100万数据,消耗的内存和CPU资源也很高,一般的电脑可能承受不住.
转自:http://www.cnblogs.com/junzhongxu/
无法在 SQL Server 2005 Manger Studio 中录入中文的
在 SQL Server 2005 Manger Studio中打开表,无法输入中文,只能输入英文
问题重现:
1. 我们建立如下三个测试表:
CREATE TABLE ta(
id int IDENTITY,
col varchar(50)
)
CREATE TABLE tb(
col varchar(50),
id int IDENTITY
)
CREATE TABLE tc(
col1 varchar(50),
col2 as col1
)
2. 在SQL Server 2005 Manger Studio 中打开上述三个表进行数据录入,你会发现是能录入中文的
3. 那么错误故障是如何再现的呢?继续测试,在上面建立的测试表中,每个表都有一个只读的列,如果把你把光标移动到该列,再移回到可写列,你就会发现,无法开启中文输入法了。
结论:
在 SQL Server 2005 Manger Studio 中录入数据的时候,不要把光标移动到只读的列中,否则你的中文就不要想录入了
另外,设计表的时候可能也得注意,如果你想在SQL Server 2005 Manger Studio 中录入中文,则不要把只读列设置为第1列,比如上面的测试表 ta, 如果你已经录入了数据,再用SQL Server 2005 Manger Studio 打开的时候,你会发现光标是定位在第1条记录的第1列(标识列,也是只读列)的,这样会导致你根本无法录入中文
这应该算是SQL Server 2005 Manger Studio 的一个BU吧
小提示:
在 SQL Server 2005 Manger Studio 中,只读列显示的数据是灰色的(比正常列的颜色稍淡一些,仔细看一下就能区分出来
转自:http://www.cnblogs.com/junzhongxu/
恢复系统数据库
关于系统数据库的恢复总结如下:
在SQL Server数据库中,系统信息存储在系统数据库中,主要的系统数据库包括:
master-从整体上控制用户数据库和SQL Server操作,在创建了任何用户定义的对象后,都要备份它。
model-为新数据库提供模版和原型
msdb-包含了有关作业、报警及操作员等信息如果包含系统数据库的介质变了,那么必须重建系统数据库,如果你仍然可以启动SQL Server服务,则可以通过RESTORE语句从系统数据库的备份中恢复数据库。
如果master坏了,不能启动系统,可以按照下面步骤进行恢复
1、重建系统数据库 运行c:\mssql7\binn\rebuildm.exe,按照提示进行即可,过程中需要系统数据库样本的路径,可在安装光盘中找到;
2、重建系统数据库后,启动SQL Server服务,用系统数据库的备份恢复数据库就行了通常恢复顺序为master->msdb->model 在恢复master的备份时要注意:必须在single user模式下进行,有以下几种方法:
进入单用户模式:
1、可以在命令行模式下输入sqlservr -c -f -m或者输入sqlservr -m
其中:-c 可以缩短启动时间,SQL Server 不作为Windows NT的服务启动
-f 用最小配置启动SQL Server
-m 单用户模式启动SQL Server
2、可以在控制面板-服务-MSSQLServer的启动参数中输入-c -f -m或者输入-m,点击开始
3、还有一种更灵活的启动方法:用存在注册表里的启动参数启动,在MSSQLServer项下添加项SingleUser,具体内容如下所示:
HKEY_LOCAL_MACHINE
\Software
\Microsoft
\MSSQLServer
\SingleUser
\Parameters
SQLArg0 : REG_SZ : -dC:\MSSQL7\DATA\MASTER.DAT
SQLArg1 : REG_SZ : -eC:\MSSQL7\LOG\ERRORLOG
SQLArg2 : REG_SZ : -lC:\MSSQL7\DATA\MASTLOG.DAT
SQLArg3 : REG_SZ : -m
在命令行下输入SQLServr -c -sSingleUser,注意:必须是在命令行下进入单用户模式后启动 Query Analyzer执行语句:
RESTORE DATABASE master form disk='c:\(具体的备份文件名)
转自:http://www.cnblogs.com/junzhongxu/
实现删除主表数据时, 判断与之关联的外键表是否有数据
某个基础信息表,与系统中30多个表存在外键关系,当删除基础数据时,需要判断是否已经被用过,如果用过则更改标志位,如果没有用过则直接删除,如何能很好实现这个处理?最好能够自动适应表的变化
问题解决(SQL Server 2005)
-- SQL Server 2005的错误处理容易控制, 因此, SQL Server 2005中可以直接删除, 通过错误处理来确定是否需要更新.
-- 示例如下.
USE tempdb
GO
CREATE TABLE m(
id int PRIMARY KEY,
bz bit)
INSERT m SELECT 1, 0
UNION ALL SELECT 2, 0
CREATE TABLE c(
id int primary key,
a_id int references m(id)
ON DELETE NO ACTION)
INSERT c SELECT 1, 1
GO
-- 删除处理存储过程
CREATE PROC dbo.p_delete
@id int
AS
SET NOCOUNT ON
BEGIN TRY
BEGIN TRAN
DELETE FROM m WHERE id = @id
COMMIT TRAN
END TRY
BEGIN CATCH
ROLLBACK TRAN
IF ERROR_NUMBER() = 547 -- 如果是外键约束错误
BEGIN
BEGIN TRY
BEGIN TRAN -- 更新标志
UPDATE m SET bz = 1
WHERE id = @id
COMMIT TRAN
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER(), ERROR_MESSAGE()
END CATCH
END
ELSE
SELECT ERROR_NUMBER(), ERROR_MESSAGE()
END CATCH
GO
-- 调用
EXEC dbo.p_delete 1
EXEC dbo.p_delete 2
SELECT * FROM m
SELECT * FROM c
GO
DROP TABLE c, m
DROP PROC dbo.p_delete
问题解决(SQL Server 2000)
-- SQL Server 2000 对错误处理不好控制, 一般还是建议做判断
-- 通过系统表查询系统表,可以获取某个表关联的所有外键表
-- 示例存储过程
CREATE PROC dbo.p_Delete
@tbname sysname, -- 基础数据表名
@PkFieldName sysname, -- 基础数据表关键字段名
@PkValue int -- 要删除的基础数据表关键字值
AS
SET NOCOUNT ON
DECLARE @bz bit, @s nvarchar(4000)
DECLARE tb CURSOR LOCAL
FOR
SELECT N’
SET @bz = CASE WHEN EXISTS(
SELECT * FROM ’ + QUOTENAME(@tbname)
+ N’ A, ’ + QUOTENAME(OBJECT_NAME(B.fkeyid))
+ N’ B
WHERE A.’ + QUOTENAME((SELECT name FROM syscolumns WHERE colid = B.rkey AND id = B.rkeyid))
+ N’ = B.’ + QUOTENAME((SELECT name FROM syscolumns WHERE colid = B.fkey AND id = B.fkeyid))
+ N’ AND A.’ + QUOTENAME((SELECT name FROM syscolumns WHERE colid = B.rkey AND id = B.rkeyid))
+ N’ = @id) THEN 1 ELSE 0 END’
FROM sysobjects A
JOIN sysforeignkeys B
ON A.id= B.constid
JOIN sysobjects C
ON A.parent_obj = C.id
WHERE A.xtype = ’f’
AND C.xtype = ’U’
AND OBJECT_NAME(B.rkeyid) = @tbname
OPEN tb
FETCH tb INTO @s
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC sp_executesql @s, N’@tbname sysname, @id int, @bz bit OUT’, @tbname, @PkValue, @bz OUT
IF @bz = 1
BEGIN
SET @s = N’UPDATE ’ + QUOTENAME(@tbname)
+ N’ SET bz = 1 WHERE ’ + QUOTENAME(@PkFieldName)
+ N’ = @id’
EXEC sp_executesql @s, N’@id int’, @PkValue
RETURN
END
FETCH tb INTO @s
END
CLOSE tb
DEALLOCATE tb
SET @s = N’DELETE FROM ’ + QUOTENAME(@tbname)
+ N’ WHERE ’ + QUOTENAME(@PkFieldName)
+ N’ = @id’
EXEC sp_executesql @s, N’@id int’, @PkValue
GO
注意事项
设置表的主/外键关系的时候,不要设置级联删除(ON DELETE CASCADE)
转自:http://www.cnblogs.com/junzhongxu/
当SQL Server数据库崩溃时如何恢复
但是呢,一般数据库崩溃的时候系统是未必能有时间把未完成的事务和脏页等写入磁盘的,这样的情况sp_attach_db就会失败。那么,寄期望于DBA制定了一个良好的灾难恢复计划吧。按照你的恢复计划,还原最新的完全备份,增量备份或者事务日志备份,然后如果你的活动事务日志还能读得出来的话,恭喜你!你可以还原到崩溃前的状态。
一般的单位都是没有专职的DBA的,如果没有可用的备份,更可能是最近一次备份的时间过于久远而导致不可接受的数据损失,而且你的活动事务日志也处于不可用的状态,那就是最麻烦的情况了。
不幸的很的是,一般数据库崩溃都是由于存储子系统引起的,而这样的情况是几乎不可能有可用的日志用于恢复的。
那么就只好试一下这些方案了。当然,是要求至少你的数据文件是存在的,要是数据文件、日志文件和备份都没有了的话,别找我,你可以到楼顶上去唱“神啊,救救我吧”。
首先,你可以试一下sp_attach_single_file_db,试着恢复一下你的数据文件,虽然能恢复的可能性不大,不过假如这个数据库刚好执行了一个checkpoint的话,还是有可能成功的。
如果你没有好到有摸彩票的手气,最重要的数据库没有像你期盼的那样attach上去,不要气馁,还是有别的方案的。
我们可以试着重新建立一个log,先把数据库设置为emergency mode,sysdatabases的status为32768 就表示数据库处于此状态。
不过系统表是不能随便改的,设置一下先
Use Master
Go
sp_configure 'allow updates', 1
reconfigure with override
Go
然后
update sysdatabases set status = 32768 where name = '<db_name>'
现在,祈求满天神佛的保佑吧,重新建立一个log文件。成功的机会还是相当大的,系统一般都会认可你新建立的日志。如果没有报告什么错误,现在就可以松一口气了。
虽然数据是恢复了,可是别以为事情就算完成了,正在进行的事务肯定是丢失了,原来的数据也可能受到一些损坏。
先把SQL Server 重新启动一下,然后检查你的数据库吧。
先设置成单用户模式,然后做dbcc
sp_dboption '<db_name>', 'single user', 'true'
DBCC CHECKDB('<db_name>')
如果没有什么大问题就可以把数据库状态改回去了,记得别忘了把系统表的修改选项关掉。
update sysdatabases set status = 28 where name = '<db_name>' --当然你的数据库状态可能不是这个,自己改为合适的值吧。也可以用sp_resetstatus
go
sp_configure 'allow updates', 0
reconfigure with override
Go
checkdb的时候可能报告有一些错误,这些错误的数据你可能就只好丢弃了。
checkdb有几种修复选项,自己看着用吧,不过最后你可能还是得用REPAIR_ALLOW_DATA_LOSS,完成所有修复。
chekcdb并不能完成所有的修复,我们需要更进一步的修复,用DBCC CHECKTABLE对每一个表做检查吧。
表的列表可以用sysobjects里面得到,把OBJECTPROPERTY是IsTable的全部找出来检查一下吧,这样能够基本上解决问题了,如果还报告错误,试着把数据select into到另一张表检查一下。
这些都做完了之后,把所有索引、视图、存储过程、触发器等重新建立一下。DBCC DBREINDEX也许可以帮你一些忙。
转自:http://www.cnblogs.com/junzhongxu/
Sql语句密码验证的安全漏洞
Sql语句作为国际标准的数据库查询语句,在各种编程环境中得到了广泛的应用。作为一个成熟、稳定的系统,用户登陆和密码验证是必不可少的。笔者在平时的编程工作中发现,许多程序员在用sql语句进行用户
密码验证时是通过一个类似这样的语句来实现的:
Sql="Select * from 用户表 where 姓名='"+name+"' and 密码='"+password+"'"
其中name和password是存放用户输入的用户名和口令,通过执行上述语句来验证用户和密码是否合法有效。但是通过分析可以发现,上述语句却存在着致命的漏洞。当我们在用户名称中输入下面的字符串时:111'or'1=1,然后口令随便输入,我们设为aaaa。变量代换后,sql语句就变成了下面的字符串:
Sql="Select * from 用户表 where 姓名='111'or'1=1' and 密码='aaaa'
我们都知道select语句在判断查询条件时,遇到或(or)操作就会忽略下面的与(and)操作,而在上面的语句中1=1的值永远为true,这意味着无论在密码中输入什么值,均能通过上述的密码验证!这个问题的解决很简单,方法也很多,最常用的是在执行验证之前,对用户输入的用户和密码进行合法性判断,不允许输入单引号、等号等特殊字符。
上述问题虽然看起来简单,但确实是存在的。例如在互联网上很有名气的网络游戏"笑傲江湖 "的早期版本就存在着这样的问题,笔者也是在看了有关此游戏的漏洞报告后才仔细分析了自己以前编写的一些程序,竟然有不少也存在着这样的漏洞。这确实应该引起我们的注意。这也暴露出包括笔者在内的年轻程序员在编程经验和安全意识上的不足。同时也提醒我们编程工作者在程序设计时应当充分考虑程序的安全性,不可有半点马虎,一个看似很小的疏漏可能就会造成很严重的后果。
转自:http://www.cnblogs.com/junzhongxu/
怎样用SQL 2000 生成XML
但是在使用ADO(Required ADO 2.6)访问返回的XML的方式和原来的Recordset是有所不同的。如果你还是使用Recordset访问的话,只能得到一个Unicode格式的XML Schema,而无法得到XML的内容。
其实这个问题也是很容易就能解决的,只是我自以为很熟悉ADO,没有仔细看Help,所以没有发现ADO是采用Stream的方式来得到和返回XML的。
Command 对象有两个属性,叫Input Stream和Output Stream,属性的值是一个IUnknown接口。可以把一个XML Parser的接口赋给它,或者是直接用Request、Response等。这样的好处是不需要再去生成一个Recordset,不需要去保存这些数据,从而节省了系统开销。
下面给大家一个简单的把XML用Response返回的Example:
<%@ Language=VBScript %>
<%
Dim objConn, objCmd, i
Set objConn = Server.createobject("ADODB.CONNECTION")
objConn.Open "Provider=SQLOLEDB.1;Password=;Persist Security Info=True;User ID=sa;Initial Catalog=PBA;Data Source=(local)"
Set objCmd = Server.CreateObject("ADODB.Command")
objCmd.ActiveConnection = objConn
objCmd.Properties("Output Stream") = Response
objCmd.Properties("XML Root") = "root"
objCmd.CommandText = "Select * from UserStatus for XML Auto"
Response.ContentType = "text/xml"
objCmd.Execute i, , adExecuteStream
Set objCmd = Nothing
objConn.Close
Set objConn = Nothing
%>
转自:http://www.cnblogs.com/junzhongxu/
2008年11月24日星期一
SQL Server导出导入数据方法
1.在SQL Server企业管理器里选中要转移的数据库,按鼠标右键,选所有任务->备份数据库。
2.备份 选数据库-完全,
目的 备份到 按添加按钮
文件名 在SQL Server服务器硬盘下输入一个自定义的备份数据库文件名(后缀一般是bak)
重写 选重写现有媒体
最后按确定按钮。
如果生成的备份数据库文件大于1M,要用压缩工具压缩后再到Internet上传输。
3.通过FTP或者remote desktop或者pcanywhere等方法
把第二步生成的备份数据库文件或者其压缩后的文件传到目的SQL Server数据库,如果有压缩要解压。
4.目的SQL Server数据库如果还没有此数据库,先创建一个新的数据库;
然后选中这个新创建的数据库,按鼠标右键,选所有任务->还原数据库
还原->从设备->选择设备->磁盘->添加(找到要导入的备份数据库文件名)->确定
还原备份集->数据库-完全
最后按确定按钮。完全的数据库导入成功了。
(如果在已经存在的SQL Server数据库上还原数据库可能遇到有还有其它人正在使用它而恢复操做失败,
可以去看 ->管理->当前活动->锁/对象->找到数据库下锁的进程号->到查询分析器里用kill 进程号杀掉这些锁,然后再做还原)
注意:如果在原有的目的SQL Server数据库上从备份文件(*.bak)还原数据库会把已经存在的表、存储过程等数据库对象全部替换成最近这次导入的备份数据库里的内容。
如果一定要还原备份文件(*.bak)里部分数据,需要另外建一个新数据库,其逻辑名称和数量同备份文件(*.bak)里数据库的逻辑名称和数量一致;
新数据库的物理文件名称取得一定要和备份文件(*.bak)里数据库的物理文件不一样才行。
二、导出导入SQL Server里某个表
1.没有防火墙,同一个局域网里或不在同一个局域网里,但通过Internet可以互相访问
在SQL Server企业管理器里选中目的数据库 ,按鼠标右键,选所有任务->导入数据-> 弹出数据转换服务导入/导出向导窗口->下一步->选数据源-> 数据源(用于SQL Server的Microfost OLE DB提供程序)->服务器(可选择局域网内能访问到的所有SQL Server服务器,或者直接输入IP地址)->选择使用windows身份验证还是使用SQL Serve身份验证(输入数据库的用户名和密码)->数据库(可选择上面选中SQL Server服务器上所有权限范围内的数据库)->下一步->选择目的->目的(用于SQL Server的Microfost OLE DB提供程序)->服务器(默认为上一步里选中的导出服务器,也可以选其它局域网内能访问到的所有SQL Server服务器,或者直接输入IP地址)->目的数据库(可选择上面选中SQL Server服务器上所有权限范围内的数据库)->下一步->制定表复制或查询->选从源数据库复制表和视图(也可以选择用一条查询指定要传输的数据)->下一步->选择源表和视图->在要导入的表和视图前面选中源->目的出现同样的表名(可以手工修改成别的表名)->转换->列映射和转换里面可以修改源表和目的表之间字段的对应关系,修改目的表字段的类型和长度等,并可以选择创建目的表,在目的表中增加行,除去并重新创建目的表,启用标志插入等选项->确定->下一步->保存、调度和复制包->时间->立即运行(如果要实现隔一段时间自动导出导入数据,选调度DTS包以便以后执行)->保存(可以不选)->[ 保存DTS包(如果以后还要转移这批相同的数据,可以把本次导出导入的内容和步骤保存起来,存到SQL Server即可,保存的时候要输入DTS的包名及详细描述)->下一步-> ]->完成
正在执行包->图形界面显示创建表及插入记录的步骤和状态->完成
2.经过防火墙,不在同一个局域网里
①、导出表里的数据到文本文件:
在SQL Server企业管理器里选中目的数据库,按鼠标右键,选所有任务-> 导入数据->弹出数据转换服务导入/导出向导窗口->下一步->选数据源-> 数据源(用于SQL Server的Microfost OLE DB提供程序)->服务器(可选择局域网内能访问到的所有SQL Server服务器)->选择使用windows身份验证还是使用SQL Serve身份验证(输入数据库的用户名和密码)->数据库(可选择上面选中SQL Server服务器上所有权限范围内的数据库)->下一步->选择目的->目的(文本文件)-> 文件名(在自己的电脑硬盘中生成一个自定义的文本文件) ->下一步->制定表复制或查询->选从源数据库复制表和视图(也可以选择用一条查询指定要传输的数据)->下一步->选择目的文件格式->源(选择要导出的表)->用默认的带分隔符->选第一行包含有列名称选项->下一步->保存、调度和复制包->时间->立即运行(如果要实现隔一段时间自动导出到文本文件,选调度DTS包以便以后执行)->保存(可以不选)-> [保存DTS包(保存的时候要输入DTS的包名及详细描述)->下一步->]->完成
正在执行包->图形界面显示表到文本文件的步骤和状态->完成
如果生成的文本文件大于1M,要用压缩工具压缩后再到Internet上传输。
②、通过FTP或者remote desktop或者pcanywhere等方法把
第①步生成的文本文件或者其压缩后的文件传到目的SQL Server数据库,如果有压缩要解压。
③、把文本文件导入目的SQL Server数据库
直接把文本文件导入目的SQL Server数据库里跟文本文件同名的新表名时,默认的会把所有字段类型都变成字符串。
所以我们要这样做:
在源SQL Server数据库上先生成创建表的sql语句
在SQL Server查询分析器里->选中源数据库里表名->按右键->在新窗口中编写对象脚本->创建->复制下新窗口内创建表名的sql语句到目标SQL Server数据库上查询分析器里执行创建表名的sql语句,生成空表结构。(如果已经存在这样的表名,修改建表的sql语句,在表名后面加上导入时间的年月信息,例如table_0113)
调用导入/导出工具->弹出数据转换服务导入/导出向导窗口->下一步->选数据源-> 数据源(文本文件)->文件名(已传到目的SQL Server数据库下要导入的文本文件,后缀可以不是*.txt,但是常规文本编辑器能打开的文件,文件类型选全部)->下一步->选择文件格式->用默认的带分隔符->选第一行包含有列名称选项->下一步->制定列分割符->逗号->下一步->选择目的->目的(用于SQL Server的Microfost OLE DB提供程序)->服务器(可选择目标局域网内能访问到的所有SQL Server服务器)-> 选择使用windows身份验证还是使用SQL Serve身份验证(输入数据库的用户名和密码)->数据库(可选择上面选中SQL Server服务器上所有权限范围内的数据库)->下一步->选择源表和视图->修改目的表名为刚才创建的表名->转换(在目的表中追加行) ->下一步->保存、调度和复制包->时间->立即运行(如果要实现隔一段时间自动把文本文件导入,选调度DTS包以便以后执行)->保存(可以不选)-> [保存DTS包(保存的时候要输入DTS的包名及详细描述)->下一步->]->完成
正在执行包->图形界面显示文本文件到表的步骤和状态->完成
如果要更改导入时间的年月信息的表名,例如table_0113到原来的表名,在企业管理器里把原来的表名改成table_old_0113,table_0113改名成table。这会对应用程序里频繁访问的表照成一定的中断。
注意:源表上的建的索引和主键约束不能用上面介绍的1和2方法转移过来,还需要手工来建索引和主键。
标志种子和not null的约束可以继承过来。
导入视图时会把源视图里所有的真实数据导入成一个新表,而不是视图。
三、SQL Server存储过程或用户定义的函数导出导入
1、导出存储过程或用户定义的函数成*.sql文件
在SQL Server企业管理器里选中源数据库,存储过程->单选或者多选中要转移的存储过程->用户定义的函数->单选或者多选中要转移的函数->按鼠标右键,选所有任务->生成SQL脚本->确定->在自己的电脑硬盘中生成一个自定义的*.sql文件->保存->正在生成SQL脚本->成功
2、如果目的数据库经过防火墙,不在同一个局域网里,要通过FTP或者remote desktop或者pcanywhere等方法把第1步生成的*.sql文件传到目的SQL Server数据库服务器上。
3、用查询分析器进入SQL Server目的数据库,从菜单里选文件->打开->打开查询文件->选中第1步生成的*.sql文件->点执行查询的绿色倒三角型快捷键->查询窗口里会出现执行后的消息(有时候可能因为存储过程和用户定义的函数之间有一定的依赖关系,会报一些错。
最好先执行用户定义的函数的*.sql文件,再执行存储过程的*.sql文件)
四、ORACLE数据库里表导入SQL Server数据库
1、在目的SQL Server数据库服务器上安装ORACLE Client软件或者ORACLE ODBC Driver. 在\network\admin\tnsnames.ora里配置ORACLE数据库的别名(service name)。
具体配置方法可以参考本站文章:客户端连服务器的注意事项
2、在WIN2000或者win2003服务器->管理工具->数据源(ODBC)->系统DSN(本机器上NT域用户都可以用)->添加->ORACLE ODBC Driver->完成->data source name 可以自定义,我一般填ORACLE数据库的sid标志,description里可以填ORACLE数据库详细描述,也可以不填->data source service name 填第1步定义的ORACLE数据库别名->OK。
(用户DSN和文件DSN也可以类似配置,但使用的时候有一些限制)
3、SQL Server的导入和导出数据工具里->选数据源-> 数据源(其它(ODBC数据源))->选第2步在ODBC里定义的系统DSN source name,用户名密码处填写ORACLE系统的用户名和密码->下一步->选择目的,选SQL Server数据库(跟上面第二点讲的一致,就不重复了)。
注意:在ORACLE表和SQL Server表之间'转换'那步很重要,可以改变默认的字段数据类型,如image->text,decimal->int
五、SQL Server数据库里表导入ORACLE数据库
方法一.导出目的选通过ODBC数据源里定义的ORACLE数据库, 注意ORACLE里表名都是大写的.我一般在ORACLE这边先生成好表结构,再选择SQL SERVER源表往ORACLE目的表里追加数据.数据传输速度比方法二慢.
方法二.从SQL Server数据库导入数据到ORACLE数据库可以选择用Windows下ORACLE9i企业或者个人版数据库做中转。
转自:http://www.cnblogs.com/junzhongxu/
个人总结:精妙SQL语句
SQL: select * into b from a where 1<>1
说明:拷贝表(拷贝数据,源表名:a 目标表名:b)
SQL: insert into b(a, b, c) select d,e,f from b;
说明:显示文章、提交人和最后回复时间
SQL: select a.title,a.username,b.adddate from table a,(select max(adddate) adddate from table where table.title=a.title) b
说明:外连接查询(表名1:a 表名2:b)
SQL: select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c
说明:日程安排提前五分钟提醒
SQL: select * from 日程安排 where datediff('minute',f开始时间,getdate())>5
说明:两张关联表,删除主表中已经在副表中没有的信息
SQL:
delete from info where not exists ( select * from infobz where info.infid=infobz.infid )
说明:--
SQL:
SELECT A.NUM, A.NAME, B.UPD_DATE, B.PREV_UPD_DATE FROM TABLE1,(SELECT X.NUM, X.UPD_DATE, Y.UPD_DATE PREV_UPD_DATE FROM (SELECT NUM, UPD_DATE, INBOUND_QTY, STOCK_ONHAND FROM TABLE2 WHERE TO_CHAR(UPD_DATE,'YYYY/MM') = TO_CHAR(SYSDATE, 'YYYY/MM')) X, (SELECT NUM, UPD_DATE, STOCK_ONHAND FROM TABLE2 WHERE TO_CHAR(UPD_DATE,'YYYY/MM') = TO_CHAR(TO_DATE(TO_CHAR(SYSDATE, 'YYYY/MM') ¦¦ '/01','YYYY/MM/DD') - 1, 'YYYY/MM') ) Y, WHERE X.NUM = Y.NUM (+)AND X.INBOUND_QTY + NVL(Y.STOCK_ONHAND,0) <> X.STOCK_ONHAND ) B WHERE A.NUM = B.NUM
说明:--
SQL:
select * from studentinfo where not exists(select * from student where studentinfo.id=student.id) and 系名称='"&strdepartmentname&"' and 专业名称='"&strprofessionname&"' order by 性别,生源地,高考总成绩
说明:
从数据库中去一年的各单位电话费统计(电话费定额贺电化肥清单两个表来源)
SQL:
SELECT a.userper, a.tel, a.standfee, TO_CHAR(a.telfeedate, 'yyyy') AS telyear, SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '01', a.factration)) AS JAN, SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '02', a.factration)) AS FRI, SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '03', a.factration)) AS MAR, SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '04', a.factration)) AS APR, SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '05', a.factration)) AS MAY, SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '06', a.factration)) AS JUE,SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '07', a.factration)) AS JUL, SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '08', a.factration)) AS AGU, SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '09', a.factration)) AS SEP, SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '10', a.factration)) AS OCT, SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '11', a.factration)) AS NOV,SUM(decode(TO_CHAR(a.telfeedate, 'mm'), '12', a.factration)) AS DEC FROM (SELECT a.userper, a.tel, a.standfee, b.telfeedate, b.factration FROM TELFEESTAND a, TELFEE b WHERE a.tel = b.telfax) a GROUP BY a.userper, a.tel, a.standfee, TO_CHAR(a.telfeedate, 'yyyy')
说明:四表联查问题:
SQL: select * from a left inner join b on a.a=b.b right inner join c on a.a=c.c inner join d on a.a=d.d where .....
说明:得到表中最小的未使用的ID号
SQL:
SELECT (CASE WHEN EXISTS(SELECT * FROM Handle b WHERE b.HandleID = 1) THEN MIN(HandleID) + 1 ELSE 1 END) as HandleID FROM Handle WHERE NOT HandleID IN (SELECT a.HandleID - 1 FROM Handle a)
转自:http://www.cnblogs.com/junzhongxu/
黑客经验谈 MSSQL SA权限入侵的感悟
1.存在SQL注入,并且数据库类型是MSSQL。
2.连接数据库的权限必须是SA。
3.后台必须有文件上传的程序。
好了,我们找到一个网址hxxp://www.6x36x.com/fangchan/listpro.asp?id=53,用NBSI一会就一目了然了。
很好,数据库类型是MSSQL,权限是SA,再看看第三个条件满足不满足。找到页面中的文章(新闻),看看里面的图片的地址是什么。好!一看就明白了hxxp://www.6x36x.com/admin/uploadpic/2xx5042823082994329.gif,你明白了吗?特别是2xx5042823082994329.gif 这下我们敢肯定后台有上传文件的功能了。下面做什么呢?晕,找出该网站所在的路径呀。这个嘛就得全靠NBSI的NB Commander(NB Tree_List)功能了(在这里我推荐大家用NB Commander,为什么呢?看完文章就知道了),不过找出网站所在的真实路径需要花一定的时间,那就看你有没有耐心了。我敢说只要有耐心,肯定能找出网站所在的真实路径。这里我找到了这个站点所在的路径D:\9x3x9,接着就是后台了,很快就得到Admin/login.asp,接下来就是账号和密码的猜解了。不过我这次猜解出现了问题。说什么也弄不出他的账号和密码,难道都是空的?我不相信,就试着登录了一下,结果失败了。于是从这开始,NB Commander功能就显得非常重要了(因为大家都知道,列目录NB Command和NB Tree_List都能实现),我找到文件conn.asp,用type D:\9x3x9\admin\logining.asp命令看了看源代码。
够狠吧!读了读代码没有问题呀!就是用的admin表字段也一样,不多说了,谁能知道其中的原因?请告诉俺一下,也让俺这只菜鸟走出困惑。进不了后台怎么上传图片呢?这里我用NBSI的上传功能,我试过了,没有成功。因为我传上去后,看到代码每行都重复三次,也不知是为什么,就是用臭要饭的Getwebshell也是同样的结果。
我想有了,看看它的Session是怎么验证的,又是一个type D:\9x3x9\admin\quanxian.asp。通过分析很快就明白了,它给Session(“wsl”)赋了一个值为1,哈哈!我写了一个非常简单的程序。用NBSI的上传功能传了上去,我想不管重复几次都是正确的(这里你又会想到什么呢?如果密码是MD5的,我们没有必要去爆破了,弄个session就ok了),传上去保存为1.asp,然后我访问hxxp://www.6x36x.com/admin/1.asp,接着访问hxxp://www.6x36x.com/admin/admin_index.asp,就这样进入了后台,本地测试。
小提示:Session变量和cookies是同一类型的。如果某用户将浏览器设置为不兼容任何cookie,那么该用户就无法使用这个Session变量! 当一个用户访问某页面时,每个Session变量的运行环境便自动生成,这些Session变量可在用户离开该页面后仍保留20分钟!(事实上,这些变量一直可保留至“timeout”。“timeout”的时间长短由Web服务器管理员设定。一些站点上的变量仅维持了3分钟,一些则为10分钟,还有一些则保留至默认值20分钟。)所以,如果在Session中置入了较大的对象(如ADO recordsets,connections, 等等),那就有麻烦了!随着站点访问量的增大,服务器将会因此而无法正常运行!
因为创建Session变量有很大的随意性,可随时调用,不需要开发者做精确地处理。所以,过度使用session变量将会导致代码不可读而且不好维护。
这样我找到上传图片的地方,把asp木马改成.gif传了上去,记住了上传后的名字,这里是uploadpic\2xx56171430123.gif,那么你会想到什么呢?哈哈我想起来了,把图片copy成.asp的,或者重命名成.asp的。
好了,到这里我们的马就算是上去了,至于以后的事情就不提了。
总结:SA的确给我们带来了很大危害,所以程序员在连接MSSQL数据库的时候千万不能用它,否则服务器成为肉鸡的可能性非常非常的大。还有,MSSQL 的扩展存储功能,用不到它就删除,留着就成了黑客的利器。
转自:http://www.cnblogs.com/junzhongxu/
数据备份中可能出错的情况及解决办法
如果你做DBA时间不长,对数据库的备份有些担心,希望能找到一种让你放心的备份方案,那么本文绝对适合你。
关于数据库的备份恢复原理,大家多少都比较熟悉了。但是,你目前做的数据库备份有多可靠?你可以安心睡觉了吗?如果答案是肯定的,那就不用多花时间看下文了,如果觉得还不够安心,总担心数据库哪一天坏了修不好,那么请接着看:
1、我有RAID,还需要做数据库备份吗?需要。有了RAID,万一部份磁盘损坏, 可以修复数据库,有的情况下数据库甚至可以继续使用。但是,如果哪一天,你的同事不小心删除了一条重要的记录,怎么办?RAID是无能为力的。你需要合适 的备份策略,把那条被误删的数据恢复出来。所以有了RAID,仍需要做备份集群,磁盘镜像同理。
2、如果你只做全备份,那么受限于全备份的大小和备份时间,不可能常做。而且只有 全备份,不能将数据库恢复至某个时间点。所以,我们需要全备份+日志备份。比如每天一个全备份,每隔1小时或若干分钟一个日志备份。说到差异备份,因为微 软的差异备份记录的是上一次全备份以来发生的变化,所以,如果数据库的改动很频繁的话,没过多久,差异备份就会和全备份的大小接近,因此这种情况下就不合 适了。因此,全备份+日志备份的方案适合绝大多数的用户。
3、如果你仅在数据库本地做备份,万一磁盘损坏,或者整个服务器硬件损坏,备份也 就没了,就没法恢复数据库。因此,你需要把备份文件传送至另一个物理硬件上。大多数用户不用磁带机,因此不考虑。一般,我们需要另一台廉价的服务器或者 PC来存放数据库的备份,来防止硬件损坏造成的备份丢失。
4、你可以在数据库服务器本地做完备份,然后使用某些方式将备份文件传送至备机。你是在备份完成后就马上穿送的吗?其实可以考虑将传送备份的脚本用T-SQL语句来写。
5、备份文件传送至备机后,就可以高枕无忧了吗?不。作为DBA的你还需要检查备机上的备份文件是否能将数据库恢复至最新,如果采用日志备份,会不会因为丢失某一个日志备份文件而导致数据库不能恢复至最新?如何检查日志备份文件之间存在断档?
6、为了将数据库尽可能的恢复到最新,你可能会每隔10分钟(甚至1分钟)执行一次日志备份,那么万一数据库坏了,在恢复的时候,手动恢复成百上千个日志文件,是不是不太现实?
7、如果你所在公司有很多的数据库服务器(就像我所在的公司),而且磁盘空间有限,那么你不得不经常登录服务器来删除旧的备份文件,如果哪天忘了,或者五一十一长假,磁盘空间用完了,就麻烦了。
8、数据库在备份的时候,并不会检查数据页面的完整性,如果数据页坏了,备份作业仍 会执行,而且不会报错,等到你发现数据页有错误的时候,你也很可能已经因为磁盘空间不足,而删除了早期的备份,而此时剩下的那些备份可能都是包含损坏的数 据页,如果损坏的数据页是某个表的表头的话,那这个表你就再也没办法恢复了。
9、所以你需要定期执行DBCC检查,来尽早发现数据库页面的完整性。在未作完DBCC检查之前,你不能删除旧的备份,以防止新的备份存在问题。所以,删除备份文件的工作变的有些麻烦。
10、你可能知道SQL Server提供了数据库维护计划。没错,使用它可以定期做备份,执行DBCC检查,但这一切仅限于本机操作。为了使数据库可靠,你还是需要自己把本地备份传送至备机。
综上,你的备份做好了吗?检查了吗?删除旧的备份是不是花去你很多时间,特别是在网络条件不好的时候?如果数据库备份文件的传送在某一时刻停止了,你多久才能发现?公司值晚班的同事有权限检查数据库的备份情况吗?
转自:http://www.cnblogs.com/junzhongxu/
SQL Server各种日期计算方法之二
这是一个计算上个月最后一天的例子。它通过从一个月的最后一天这个例子上减去3毫秒来获得。有一点要记住,在Sql Server中时间是精确到3毫秒。这就是为什么我需要减去3毫秒来获得我要的日期和时间。
SELECT dateadd(ms,-3,DATEADD(mm, DATEDIFF(mm,0,getdate()), 0))
计算出来的日期的时间部分包含了一个Sql Server可以记录的一天的最后时刻(“23:59:59:997”)的时间。
去年的最后一天
连接上面的例子,为了要得到去年的最后一天,你需要在今年的第一天上减去3毫秒。
SELECT dateadd(ms,-3,DATEADD(yy, DATEDIFF(yy,0,getdate()), 0))
本月的最后一天
现在,为了获得本月的最后一天,我需要稍微修改一下获得上个月的最后一天的语句。修改需要给用DATEDIFF比较当前日期和“1900-01-01”返回的时间间隔上加1。通过加1个月,我计算出下个月的第一天,然后减去3毫秒,这样就计算出了这个月的最后一天。这是计算本月最后一天的SQL脚本。
SELECT dateadd(ms,-3,DATEADD(mm, DATEDIFF(m,0,getdate())+1, 0))
本年的最后一天
你现在应该掌握这个的做法,这是计算本年最后一天脚本
SELECT dateadd(ms,-3,DATEADD(yy, DATEDIFF(yy,0,getdate())+1, 0))。
本月的第一个星期一
好了,现在是最后一个例子。这里我要计算这个月的第一个星期一。这是计算的脚本。
select DATEADD(wk, DATEDIFF(wk,0,
dateadd(dd,6-datepart(day,getdate()),getdate())
), 0)
在这个例子里,我使用了“本周的星期一”的脚本,并作了一点点修改。修改的部分是把原来脚本中“getdate()”部分替换成计算本月的第6天,在计算中用本月的第6天来替换当前日期使得计算可以获得这个月的第一个星期一。
总结
我希望这些例子可以在你用DATEADD和DATEDIFF函数计算日期时给你一点启发。通过使用这个计算日期的时间间隔的数学方法,我发现为了显示两个日期之间间隔的有用历法是有价值的。注意,这只是计算出这些日期的一种方法。要牢记,还有很多方法可以得到相同的计算结果。假如你有其他的方法,那很不错,要是你没有,我希望这些例子可以给你一些启发,当你要用DATEADD和DATEDIFF函数计算你程序可能要用到的日期时。
附录:其他日期处理方法
1)去掉时分秒
declare @ datetime
set @ = getdate() --’2003-7-1 10:00:00’
SELECT @,DATEADD(day, DATEDIFF(day,0,@), 0)
2)显示星期几
select datename(weekday,getdate())
3)如何取得某个月的天数
declare @m int
set @m=2 --月份
select datediff(day,’2003-’+cast(@m as varchar)+’-15’ ,’2003-’+cast(@m+1 as varchar)+’-15’)
另外,取得本月天数
select datediff(day,cast(month(GetDate()) as varchar)+’-’+cast(month(GetDate()) as varchar)+’-15’ ,cast(month(GetDate()) as varchar)+’-’+cast(month(GetDate())+1 as varchar)+’-15’)
或者使用计算本月的最后一天的脚本,然后用DAY函数区最后一天
SELECT Day(dateadd(ms,-3,DATEADD(mm, DATEDIFF(m,0,getdate())+1, 0)))
4)判断是否闰年:
SELECT case day(dateadd(mm, 2, dateadd(ms,-3,DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)))) when 28 then ’平年’ else ’闰年’ end
或者
select case datediff(day,datename(year,getdate())+’-02-01’,dateadd(mm,1,datename(year,getdate())+’-02-01’))
when 28 then ’平年’ else ’闰年’ end
5)一个季度多少天
declare @m tinyint,@time smalldatetime
select @m=month(getdate())
select @m=case when @m between 1 and 3 then 1
when @m between 4 and 6 then 4
when @m between 7 and 9 then 7
else 10 end
select @time=datename(year,getdate())+’-’+convert(varchar(10),@m)+’-01’
select datediff(day,@time,dateadd(mm,3,@time))
转自:http://www.cnblogs.com/junzhongxu/
防范SQL注入攻击的新办法
以下是引用片段:
Function SafeRequest(ParaName)
Dim ParaValue
ParaValue=Request(ParaName)
if IsNumeric(ParaValue) = True then
SafeRequest=ParaValue
exit Function
elseIf Instr(LCase(ParaValue),"select ") > 0 or Instr(LCase(ParaValue),"insert ") > 0 or Instr(LCase(ParaValue),"delete from") > 0 or Instr(LCase(ParaValue),"count(") > 0 or Instr(LCase(ParaValue),"drop table") > 0 or Instr(LCase(ParaValue),"update ") > 0 or Instr(LCase(ParaValue),"truncate ") > 0 or Instr(LCase(ParaValue),"asc(") > 0 or Instr(LCase(ParaValue),"mid(") > 0 or Instr(LCase(ParaValue),"char(") > 0 or Instr(LCase(ParaValue),"xp_cmdshell") > 0 or Instr(LCase(ParaValue),"exec master") > 0 or Instr(LCase(ParaValue),"net localgroup administrators") > 0 or Instr(LCase(ParaValue)," and ") > 0 or Instr(LCase(ParaValue),"net user") > 0 or Instr(LCase(ParaValue)," or ") > 0 then
Response.Write "< script language='javascript' >"
Response.Write "alert('非法的请求!');" '发现SQL注入攻击提示信息
Response.Write "location.href='http://blog.knowsky.com/';" '发现SQL注入攻击转跳网址
Response.Write "< script >"
Response.end
else
SafeRequest=ParaValue
End If
End function
使用SafeRequest函数替换你的Request
转自:http://www.cnblogs.com/junzhongxu/
SQL Server 用户自定义的数据库修复 new
一、自动应用重做日志
1、 利用SET AUTORECOVERY命令自动应用重做日志,完成对数据文件的修复操作。
SQL>STARTUP MOUNT:启动实例并加载数据库。
SQL>SET AUTORECOVERY ON:启用重做日志自动应用功能。
SQL>RECOVER DATABASE:恢复指定表空间、数据文件或整个数据库。
SQL>ALTER DATABASE OPEN:完成恢复后打开数据库。
2、 利用RECOVERY AUTOMATIC命令自动应用重做日志,完成对数据文件的修复操作。
SQL>STARTUP MOUNT:启动实例并加载数据库。
SQL>RECOVER AUTOMATIC DATABASE。
SQL>ALTER DATABASE OPEN:完成恢复后打开数据库。
二、 不归档模式下的数据库介质恢复
1、 将数据库恢复到原来的位置上。
SQL>SHUTDOWN IMMEDIATE 如果数据库仍然处于打开状态,关闭数据库;将数据库文件恢复到原来的位置上,利用最近一次建立的一致性完全备份对整个数据库进行恢复,必须对所有的数据文件与控制文件进行修复。
SQL>RECOVER DATABASE UNTIL CANCEL
SQL>CANCEL
SQL>ALTER DATABASE OPEN RESETLOGS;
将当前重做日志顺序号设置为1。
2、 将数据库恢复到新的位置上。
SQL>SHUTDOWN IMMEDIATE:如果数据库仍然处于打开状态,关闭数据库;将数据库文件恢复到新的位置上,利用最近一次建立的一致性完全备份对整个数据库进行恢复,必须对所有的数据文件与控制文件进行修复;对初始化参数文件中的CONTROL_FILES参数进行编辑,使它执行保存在新位置中修复后的控制文件。
SQL>STARTUP MOUNT:如果修复后的数据库文件处于新的位置,必须利用ALTER DATABASE RENAME FILE语句对控制文件进行修改,使它指向新位置中修复后的数据文件。如:
SQL>ALTER DATABASE RENAME FILE
‘I: ora9ioradatasystem01.dbf’ TO ‘K: oracleoradatasystem01.dbf’;
SQL>RECOVER DATABASE UNTIL CANCEL
SQL>CANCEL
SQL>ALTER DATABASE OPEN RESETLOGS;
将当前重做日志顺序号设置为1。
三、 归档模式下的完全介质恢复
1、 关闭状态下的完全恢复。
SQL>SHUTDOWN ABORT(如果数据库处于打开状态,将它强行关闭):将数据文件恢复到原来的位置上,如果介质故障无法排除,需要将数据文件恢复到其它位置上;利用备份修复丢失或损坏的数据文件,也可利用ALTER DATABASE CREATE DATAFILE 语句重建一个空白的数据文件替换对视或损坏的数据文件。
SQL>STARTUP MOUNT:如果修复后的数据文件不在原来的位置上,需要使用ALTER DATABASE RENAME FILE …TO …语句在控制文件中更新它们的信息。
SQL>SELECT name,status FROM V$DATAFILE;
查询数据文件的名称和状态。
SQL>ALTER DATABASE DATAFILE … ONLINE;
将脱机数据文件改未联机。
SQL>RECOVER DATABASE
或 SQL>RECOVER TABLESPACE users
或 SQL>RECOVER DATAFILE ‘I: ora9ioradatausers0.dbf;
SQL>ALTER DATABASE OPEN;
2、 打开状态下的完全介质恢复。
SQL>SELECT d.file# f#,d.name,d.status,
h.status from v$datafile d,v$datafile_header h
WHERE d.file#=h.file#;
查询哪些数据文件被自动设置为脱机状态;
SQL>ALTER TABLESPACE users OFFLINE TEMPORARY;
将包含损坏数据文件的表空间设置为脱机状态;将数据文件恢复到原来的位置上,如果介质故障无法排除,需要将数据文件恢复到其它位置上;利用备份修复丢失或损坏的数据文件;如果修复后的数据文件不在原来的位置上,需要使用ALTER DATABASE RENAME FILE …TO …语句在控制文件中更新它们的信息。
SQL>RECOVER TABLESPACE users AUTOMATIC
对包含损坏数据文件的脱机表空间进行恢复;
SQL>ALTER TABLESPACE users ONLINE;
四、 归档模式下的不完全介质恢复
1、 不完全恢复的操作准则:
在恢复前后都对数据库进行完全备份。
完成不完全介质恢复后,检查数据库是否已经恢复到了目标时刻下的状态。
完成不完全介质恢复后,将归档重做日志文件移动到其它位置保存。
2、 基于时间的不完全恢复:
对数据库进行一次完全备份,包括控制文件和所有的联机重做日志文件。
SQL>SHUTDOWN ABORT:确定不完全介质恢复的目标时间,即你需要将数据库恢复到哪个时刻下的状态,然后确定需要使用哪些备份来对数据进行修复,数据库修复所使用的控制文件备份应当能够正确反映出目标时刻下数据库的物理结构,所使用的数据文件备份应当是在目标时刻之前创建的,而且必须修复所有的数据文件,如果没有在目标时刻之前建立的数据文件备份,需要重新创建空白的数据文件。
如果在数据库中包含在目标时刻之后建立的数据文件,不要对这个数据文件进行修复,因为在完成不完全恢复后的数据库中根本不应当存在这个数据文件;将数据文件恢复到原来的位置上,如果介质故障无法排除,则恢复到其它位置上;利用选定的备份文件修复所有的控制文件和数据文件。
SQL>STARTUP MOUNT 启动实例并加载数据库;如果修复后的数据文件不在它们原来的位置上,需使用ALTER DATABASE RENAME FILE … TO …语句在控制文件中更新它们的信息。
SQL>SELECT name,status FROM V$DATAFILE;
确定所有数据文件都处于联机
SQL>ALTER DATABASE DATAFILE … ONLINE;
将数据文件恢复为联机;
SQL>RECOVER DATABASE UNTIL TIME ‘2004-02-01:12:30:30’
如果控制文件是利用备份修复的,
必须在RECOVER名利中指定USING BACKUP CONTROLFILE子句;
SQL>ALTER DATABASE OPEN RESETLOGS;
立即对数据库进行一次完全备份。
3、 基于撤销的不完全恢复。
SQL>RECOVER DATABASE UNTIL CANCEL:其它步骤同基于时间的不完全恢复。
4、 基于SCN的不完全恢复。
在进行基于SCN的不完全恢复时,oracle会在应用了所有具有小于等于指定SCN的事务的重做记录之后终止恢复过程。
RESETLOGS选项在如下三种情况下,必须使用RESETLOGS选项打开数据库:
1、 在执行任何类型的不完全介质恢复之后;
2、 在使用备份修复控制文件后(在RECOVER命令中使用USING BACKUP CONTROLFILE子句);
在没有联机重做日志文件备份的情况下对不归档数据库进行完全恢复之后。
转自:http://www.cnblogs.com/junzhongxu/
SQL Server各种日期计算方法之一
在使用本文中的例子之前,你必须注意以下的问题。大部分可能不是所有例子在不同的机器上执行的结果可能不一样,这完全由哪一天是一个星期的第一天这个设置决定。第一天(DATEFIRST)设定决定了你的系统使用哪一天作为一周的第一天。所有以下的例子都是以星期天作为一周的第一天来建立,也就是第一天设置为7。假如你的第一天设置不一样,你可能需要调整这些例子,使它和不同的第一天设置相符合。你可以通过@@DATEFIRST函数来检查第一天设置。
为了理解这些例子,我们先复习一下DATEDIFF和DATEADD函数。DATEDIFF函数计算两个日期之间的小时、天、周、月、年等时间间隔总数。DATEADD函数计算一个日期通过给时间间隔加减来获得一个新的日期。要了解更多的DATEDIFF和DATEADD函数以及时间间隔可以阅读微软联机帮助。
使用DATEDIFF和DATEADD函数来计算日期,和本来从当前日期转换到你需要的日期的考虑方法有点不同。你必须从时间间隔这个方面来考虑。比如,从当前日期到你要得到的日期之间有多少时间间隔,或者,从今天到某一天(比如1900-1-1)之间有多少时间间隔,等等。理解怎样着眼于时间间隔有助于你轻松的理解我的不同的日期计算例子。
一个月的第一天
第一个例子,我将告诉你如何从当前日期去这个月的最后一天。请注意:这个例子以及这篇文章中的其他例子都将只使用DATEDIFF和DATEADD函数来计算我们想要的日期。每一个例子都将通过计算但前的时间间隔,然后进行加减来得到想要计算的日期。
这是计算一个月第一天的SQL 脚本:
SELECT DATEADD(mm, DATEDIFF(mm,0,getdate()), 0)
我们把这个语句分开来看看它是如何工作的。最核心的函数是getdate(),大部分人都知道这个是返回当前的日期和时间的函数。下一个执行的函数DATEDIFF(mm,0,getdate())是计算当前日期和“1900-01-01 00:00:00.000”这个日期之间的月数。记住:时期和时间变量和毫秒一样是从“1900-01-01 00:00:00.000”开始计算的。这就是为什么你可以在DATEDIFF函数中指定第一个时间表达式为“0”。下一个函数是DATEADD,增加当前日期到“1900-01-01”的月数。通过增加预定义的日期“1900-01-01”和当前日期的月数,我们可以获得这个月的第一天。另外,计算出来的日期的时间部分将会是“00:00:00.000”。
这个计算的技巧是先计算当前日期到“1900-01-01”的时间间隔数,然后把它加到“1900-01-01”上来获得特殊的日期,这个技巧可以用来计算很多不同的日期。下一个例子也是用这个技巧从当前日期来产生不同的日期。
本周的星期一
这里我是用周(wk)的时间间隔来计算哪一天是本周的星期一。
SELECT DATEADD(wk, DATEDIFF(wk,0,getdate()), 0)
一年的第一天
现在用年(yy)的时间间隔来显示这一年的第一天。
SELECT DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)
季度的第一天
假如你要计算这个季度的第一天,这个例子告诉你该如何做。
SELECT DATEADD(qq, DATEDIFF(qq,0,getdate()), 0)
当天的半夜
曾经需要通过getdate()函数为了返回时间值截掉时间部分,就会考虑到当前日期是不是在半夜。假如这样,这个例子使用DATEDIFF和DATEADD函数来获得半夜的时间点。
SELECT DATEADD(dd, DATEDIFF(dd,0,getdate()), 0)
深入DATEDIFF和DATEADD函数计算
你可以明白,通过使用简单的DATEDIFF和DATEADD函数计算,你可以发现很多不同的可能有意义的日期。
目前为止的所有例子只是仅仅计算当前的时间和“1900-01-01”之间的时间间隔数量,然后把它加到“1900-01-01”的时间间隔上来计算出日期。假定你修改时间间隔的数量,或者使用不同的时间间隔来调用DATEADD函数,或者减去时间间隔而不是增加,那么通过这些小的调整你可以发现和多不同的日期。
这里有四个例子使用另外一个DATEADD函数来计算最后一天来分别替换DATEADD函数前后两个时间间隔。
转自:http://www.cnblogs.com/junzhongxu/
运行SQL Server的计算机之间移动数据库
注意:支持将数据从SQL Server 2000迁移到Microsoft SQL Server 2000(64位)。您可以将一个32位数据库附加到一个64位数据库上,方法是:使用sp_attach_db系统存储过程或sp_attach_single_file_db系统存储过程,或者使用32位企业管理器中的备份和还原功能。您可以在SQL Server的32位和64位两种版本之间来回移动数据库。您还可以使用同样的方法从SQL Server 7.0迁移数据。但是,不支持将数据从SQL Server 2000(64位)降级到SQL Server 7.0。下面分别介绍这几种方法。
如果您使用的是SQL Server 2005
您可以使用相同的方法从SQL Server 7.0或SQL Server 2000迁移数据。但是,Microsoft SQL Server 2005中的管理工具与SQL Server 7.0或SQL Server 2000中的管理工具有所不同。您应该使用SQL Server Management Studio(而不是SQL Server企业管理器)以及SQL Server导入和导出向导(DTSWizard.exe)(而不是数据转换服务导入和导出数据向导)。
备份和还原
在源服务器上备份用户数据库,然后将用户数据库还原到目标服务器上。在备份过程中时可能有人使用数据库。如果用户在备份完成后对数据库执行INSERT、UPDATE或DELETE语句,则备份中不会包含这些更改。如果您必须传输所有更改,那么,假如您既执行事务日志备份又执行完整数据库备份,您可以以尽可能短的停止时间来传输这些更改。
1.在目标服务器上还原完整数据库备份,并指定WITH NORECOVERY选项。
注意:为防止对数据库做进一步的修改,请指导用户在源服务器上退出数据库活动。
2.执行事务日志备份,然后使用WITH RECOVERY选项将事务日志备份还原到目标服务器上。停止时间仅限于事务日志备份和恢复的时间。
◆目标服务器上的数据库将与源服务器上的数据库大小相同。要减小数据库的大小,您必须在执行备份前压缩源数据库的大小,或者在完成还原后压缩目标数据库的大小。
◆如果您将数据库还原到的文件位置不同于源数据库的文件位置,则必须指定WITH MOVE选项。例如,在源服务器上,数据库位于D:MssqlData文件夹中。目标服务器没有D驱动器,因而您需要将数据库还原到C:MssqlData文件夹。有关如何将数据库还原到其他位置的更多信息,请查看相关资料。
◆如果您想覆盖目标服务器上的一个现有数据库,则必须指定WITH REPLACE选项。
◆源服务器和目标服务器上的字符集、排序顺序和Unicode整序可能必须相同,具体取决于您要还原到SQL Server的哪种版本。有关更多信息,请参阅本文中的“关于排序规则的说明”一节。
Sp_detach_db和Sp_attach_db存储过程
要使用sp_detach_db和sp_attach_db这两个存储过程,请按下列步骤操作:
1.使用sp_detach_db存储过程分离源服务器上的数据库。您必须将与数据库关联的.mdf、.ndf和.ldf这三个文件复制到目标服务器上。参见下表中对文件类型的描述:
2.使用sp_attach_db存储过程将数据库附加到目标服务器上,并指向您在上一步骤中复制到目标服务器的文件。
◆分离数据库后将无法访问该数据库,并且复制文件时也无法使用该数据库。在进行分离的那一时刻数据库中包含的所有数据都被移动。
◆在您使用附加或分离方法时,两个服务器上的字符集、排序顺序和Unicode整序都必须相同。有关更多信息,请参阅本文中的“关于排序规则的说明”一节。
关于排序规则的说明
如果您使用备份和还原或附加和分离方法在两个SQL Server 7.0服务器之间移动数据库,则两个服务器上的字符集、排序顺序和Unicode整序都必须相同。如果您将数据库从SQL Server 7.0移到SQL Server 2000,或者在不同的SQL Server 2000服务器之间移动数据库,则数据库将保留源数据库的整序。这意味着,如果运行SQL Server 2000的目标服务器的整序与源数据库的整序不同,则目标数据库的整序也将与目标服务器的master、model、tempdb和msdb数据库的整序不同。
第1步:导入和导出数据:(在SQL Server数据库之间复制对象和数据)
您可以使用数据转换服务导入和导出数据向导来复制整个数据库或有选择地将源数据库中的对象和数据复制到目标数据库。在传输过程中,可能有人在使用源数据库。如果在传输过程中有人在使用源数据库,您可能会看到传输过程中出现一些阻滞现象。
◆在您使用导入和导出数据向导时,源服务器与目标服务器的字符集、排序顺序和整序不必相同。
◆因为源数据库中未使用的空间不会移动,所以目标数据库不必与源数据库一样大。同样,如果您只移动某些对象,则目标数据库也不必与源数据库一样大。
◆SQL Server 7.0数据转换服务可能无法正确地传输大于64KB的文本和图像数据。但SQL Server 2000版本的数据转换服务不存在此问题。
第2步:如何传输登录和密码:
如果您不将源服务器中的登录传输到目标服务器,当前的SQL Server用户就无法登录到目标服务器。目标服务器上的登录的默认数据库可能与源服务器上的登录的默认数据库不同。您可以使用sp_defaultdb存储过程来更改登录的默认数据库。
第3步:如何解决孤立用户:
在您向目标服务器传输登录和密码后,用户可能还无法访问数据库。登录与用户是靠安全识别符(SID)关联在一起的;在您移动数据库后,如果SID不一致,SQL Server可能会拒绝用户访问数据库。此问题称为孤立用户。如果您使用SQL Server 2000 DTS传输登录功能来传输登录和密码,就可能会产生孤立用户。此外,被允许访问与源服务器处于不同域中的目标服务器的集成登录帐户,也会导致出现孤立用户。
1.查找孤立用户。在目标服务器上打开查询分析器,然后在您移动的用户数据库中运行以下代码:exec sp_change_users_login 'Report'
此过程将列出任何未链接到一个登录帐户的孤立用户。如果没有列出用户,请跳过第2步和第3步,直接进行第4步。
2.解决孤立用户问题。如果一个用户是孤立用户,数据库用户可以成功登录到服务器,但却无权访问数据库。如果您尝试向数据库授予登录访问权,则会因该用户已经存在而出现下列错误消息:
Microsoft SQL-DMO (ODBC SQLState:42000)
错误15023:当前数据库中已存在用户或角色'%s'。上面介绍了如何使用sp_change_users_login存储过程来逐个纠正孤立用户。sp_change_users_login存储过程仅能解决标准的SQL Server登录帐户的孤立用户问题。
3.如果数据库所有者(dbo)被当作孤立用户列出,请在用户数据库中运行下面的代码:exec sp_changedbowner 'sa'此存储过程会将数据库所有者更改为dbo并解决这个问题。要将数据库所有者更改为另一用户,请使用您想使用的用户再次运行sp_changedbowner。
4.如果您的目标服务器运行的是SQL Server 2000 Service Pack 1,则在您执行附加操作或还原操作(或两种操作都执行)后,企业管理器的用户文件夹中的列表中可能没有数据库所有者用户。
5.如果目标服务器上不存在映射到源服务器上的dbo的登录,您在尝试通过企业管理器更改系统管理员(sa)密码时,可能会收到以下错误消息:
错误21776:[SQL-DMO]名称'dbo'在Users集合中没有找到。如果该名称是合法名称,则使用[]来分隔名称的不同部分,然后重试。
警告:如果您再次还原或附加数据库,则数据库用户可能会再次被孤立,这样您就必须重复第3步操作。
第4步:如何移动作业、警报和运算符:
第4步是可选操作。您可以为源服务器上的所有作业、警报和运算符生成脚本,然后在目标服务器上运行脚本。要移动作业、警报和运算符,请按照下列步骤操作:
1.打开SQL Server企业管理器,然后展开管理文件夹。
2.展开SQL Server代理,然后右键单击警报、作业或运算符。
3.单击所有任务,然后单击生成SQL脚本。对于SQL Server 7.0,请单击为所有作业生成脚本、警报或运算符。
您可以用右键单击选择为所有警报、所有作业或所有运算符生成脚本。
◆您可以将作业、警报和运算符从SQL Server 7.0移到SQL Server 2000,也可以在运行SQL Server 7.0和运行SQL Server 2000计算机之间移动。
◆如果在源服务器上为运算符设置了SQLMail通知,则目标服务器上也必须设置SQLMail,才能具有相同的功能。
第5步:如何移动DTS包:
第5步是可选操作。如果DTS包在源服务器上存储在SQL Server中或存储库中,您可以在需要时移动这些包。要在服务器之间移动DTS包,请使用下列方法之一。
方法1
1.在源服务器上将DTS包保存到一个文件中,然后在目标服务器上打开DTS包文件。
2.将目标服务器上的包保存到SQL Server或存储库中。
注意:您必须用单独的文件逐个地移动这些包。
方法2
1.在DTS设计器中打开每个DTS包。
2.在包菜单上,单击另存为。
3.指定目标SQL Server。
注意:在新服务器上,包可能无法正常运行。您可能必须对包进行更改,更改包中任何对旧的源服务器上的连接、文件、数据源、配置文件和其他信息的引用,以便引用新的目标服务器。您必须根据每个包的设计逐个包进行这些更改。
本文中介绍的步骤不移动数据库关系图以及备份与还原历史记录。如果您必须移动这些信息,请移动msdb系统数据库。如果您移动msdb数据库,则不必执行“第4步:如何移动作业、警报和运算符”或“第5步:如何移动DTS包”。
转自:http://www.cnblogs.com/junzhongxu/
测试用例设计—等价类划分法
1.1 定义
等价类划分法是把所有可能的输入数据,即程序的输入域划分成若干部分(子集),然后从每一个子集中选取少数具有代表性的数据作为测试用例。该方法是一种重要的,常用的黑盒测试用例设计方法。
1.2 等价类
等价类是某个输入域的集合,在这个集合中每个输入条件都是等效的。如果其中一个的输入不能导致问题发生,那么集合中其它输入条件进行测试也不可能发现错误。
等价类分为有效等价类和无效等价类。
有效等价类就是由那些对程序的规格说明有意义的、合理的输入数据所构成的集合,利用有效等价类可检验程序是否实现了规格说明中所规定的功能和性能。
无效等价类就是那些对程序的规格说明不合理的或无意义的输入数据所构成的集合。
2.划分等价类的方法
划分等价类重要的是:集合的划分,划分为互不相交的一组子集,而子集的并是整个集合。
下面给出六条确定等价类的原则。
在输入条件规定了取值范围或值的个数的情况下,则可以确立一个有效等价类和两个无效等价类。例如:成年人每分钟的心跳60-100之间为正常。
有效等价类:60-100 无效等价类:<60 和 >100
2、在输入条件规定了输入值的集合或者规定了“必须如何”的条件的情况下,可确立一个有效等价类和一个无效等价类。 例如:用户连续输入错误密码的次数最多为3次。
有效等价类:<=3次 无效等价类:>3次
3、在输入条件是一个布尔量的情况下,可确定一个有效等价类。
例如:单选的选中与不选中。
4、在规定了输入数据的一组值(假定n个),并且程序要对每一个输入值分别处理的情况下,可确立n个有效等价类和一个无效等价类。
mDjr(eNl0 例如:输入数据为省份的选择。
5、在规定了输入数据必须遵守的规则的情况下,可确立一个有效等价类(符合规则)和若干个无效等价类(从不同角度违反规则) 。
例如:规定必须输入非0的正整数。
这种例子应充分考虑规则是否可以拆分为具有单一的子规则,然后得到从不同角度违反规则的无效等价类。
该例子起码可拆分为非0、数字、正数、整数4个子规则,至少每个规则对应一个无效等价类,即0、字符串、负数、小数,甚至可挖掘出输入为空的隐含等价类。
6、在确知已划分的等价类中各元素在程序处理中的方式不同的情况下,则应再将该等价类进一步的划分为更小的等价类。 例如:核对日期的有效性,初步有效等价类是1<=Month<=12,1<=Day<=31
可是考虑到2月以及闰年、闰月、长月、短月等,需要进一步细分,当然其中还涉及到了年月日组合的问题。
根据等价类划分原则,将等价类填入下表。
等价类表
输入条件 有效等价类 无效等价类
3.测试用例设计原则
根据等价类表,然后从划分出的等价类中按以下三个原则设计测试用例:
1)、为每一个等价类规定一个唯一的编号。
2)、设计一个新的测试用例,使其尽可能多地覆盖尚未被覆盖地有效等价类,重复这一步,直到所有的有效等价类都被覆盖为止。
3)、设计一个新的测试用例,使其仅覆盖一个尚未被覆盖的无效等价类,重复这一步,直到所有的无效等价类都被覆盖为止。
4.等价类划分法优缺点`
等价类划分法的优点是考虑了单个输入域的各类情况,避免了盲目或随机选取输入数据的布完整性和覆盖的不稳定性。
等价类划分法虽然简单易用,但是没有对组合情况进行充分的考虑。需要结合其他测试用例设计的方法进行补充。
5.实例
竞猜系统中:投注的金额要求是大于10的正整数。
根据分析等到以下等价类表。 输入条件
有效等价类
无效等价类
大于10的正整数 大于10正整数 空
负数
小数
小于10的数
字符串
备注:再加上全角状态下的数
转自:http://www.cnblogs.com/junzhongxu/
2008年11月23日星期日
Web性能测试案例分析
案例重点关注如下三个方面:
如何根据项目需要来设计与开发测试程序。
测试工具在性能测试中的作用与地位。
针对数据库本神进行性能测试的地位。
二、项目背景
三、性能测试策略、目标、范围、环境
四、性能测试分析与设计
(1)系统架构组对数据库提测试出了如下要求:
测试数据库对复杂SQL语句的支持,尤其是一些长SQL语句的支持。
测试数据库对二进制等格式类型存储的支持。
测试系统在数据量较大时的多用户并发查询的响应时间--查询响应时间为Web服务器对数据库响应时间,不包含应用系统响应时间。
(2)根据上面的测试要求的分析,设计出如下性能测试方案:
测试体系采用B/S架构方式,对数据库的查询操作封存在Servlet中,Servlet运行于tomcat5.0上,前端采用JSP页面调用Servlet并输出执行结果(数据库执行查询的时间)和查询结果(数据库返回的查询结果)。
利用原型系统后台数据库的数据作为测试数据,保证测试结果更加真实。
测试采用的查询语句用原型系统的业务语句,保证测试兼顾实际应用。
通过测试工具LoadRunner来创建虚拟用户,实现用户并发查询。.
通过测试结果来综合分析各家数据库的性能。
五、性能测试实施
测试脚本的开发与修改
数据库系统调优前的测试
数据库系统调优后的测试
六、测试结果分析TestAge
各家国产数据库调优前的测试结果及分析
数据库调优策略TestAge
各家国产数据库调优后的测试结果及分析
稳定性测试场景分析
测试结果TestAge
七、案例点评
体现了模型剪裁与具体化原则
节约了一定的开发成本TestAge
仅仅有测试工具是不够的:很多人认为有了性能测试工具,就可以完成性能测试任务,根本不需要自己去开发脚本。实际上,性能测试最重要的是设计与规划,如何去执行测试用例,则是相对容易的事情。执行测试用例主要通过测试工具,但有时还要靠自己开发的脚本来辅助测试,案例中的测试项目就是很好的证明。
调优不能解决一切问题:性能测试的基本思路“测试--发现问题--调优--再测试”的反复过程。因此,解决性能问题应该从设计阶段着手。
转自:http://www.cnblogs.com/junzhongxu/
白盒测试工具大全
这是一组白盒测试工具,主要是用于代码开发阶段,检查应用的可靠性和稳定性。它提供了先进的错误检查和调试解决方案,充分地改善生产力和开发团队的软件开发质量。NuMega产品线是一个全面的SmartDebugging工具包,自动地检查企业级或Internet级用多语言创建的组件和应用中出现的软件错误和性能问题,并能很快地给予解决。
NuMega DecPartner Studio满足在软件开发过程中每一个开发人员的需求,无论我们是使用一种或多种语言,NuMega产品都能够帮助我们提高生产力。它的产品主要有自动地错误检测、性能分析、代码覆盖分析等功能,分别用于捕获、定位错误,抽取代码执行频度,以及抽取代码覆盖率等数据,产品包括:
程序员在开发过程中可能会经常遇到这样的问题:调试时语法没有问题,代码也没有错误,但应用程序运行就是不正常甚至死机,其实这有可能是由于逻辑错误引起的内存溢出或资源泄露等问题,这些错误一般是不容易被检测出来的。而这类错误就是BoundsChecker错误检测范围之一。
通过对被测应用程序的操作,BoundsChecker提供清晰的、详细的程序错误分析,自动查明静态的堆栈错误及内存/资源泄露,并能够迅速的定位出错的源代码,即使在没有源代码的情况下也可检查第三方组件的错误。
BoundsChecker错误检测范围主要包括:
1).指针和泄露错误
接口泄露
内存泄露
资源泄露
未分配的指针错误
2).内存错误
动态存储溢出
无效的句柄被锁定
句柄没有被锁定
内存分配冲突
栈空间溢出
静态存储溢出
API和OLE错误
API函数返回失败
API函数未执行
无效的变量(包括指针变量、字符串变量等)
OLE接口方法的变量无效
OLE接口方法失败
线程调用库函数错误
BoundsChecker支持的语言和主机平台:
C++, Delphi
Windows NT, Windows95/98
在开发过程中,对一个应用程序通过手工测试,总会有一部分代码功能没有被检测到,或者说逐个检测每一个函数的调用是相当费时间的;未被检测的代码我们不能保证它的可靠性,以后程序的失败可能往往就是由这部分未检测的代码造成的。现在我们可以用TrueCoverage来帮助我们解决这些问题,我们在测试程序时,每完成一次应用话路,TrueCoverage就能够列出在这次对话中所有函数被调用次数、所占比率等,并可以直接定位到源代码,当然我们也可以合并多个应用话路来进行检测。所以说TrueCoverage能通过衡量和跟踪代码执行及代码稳定性,帮助开发团队节省时间和改善代码可靠性。
TrueCoverage支持的语言和主机平台
C++, JAVA, Visual Basic
Windows NT, Windows95/98
3. TrueTime
代码运行缓慢是开发过程中一个重要问题。一个应用程序运行速度较慢,程序员不容易找到到底是在哪里出现了问题,如果不能解决应用程序的性能将降低并极大的影响应用程序的质量,于是查找和修改性能瓶颈是调整整个代码性能的关键。如何快速的查找性能瓶颈呢?TrueTime的出现就使这个问题变得很容易了。当我们在测试程序时,每完成一次应用话路,TrueTime都能提供这次对话中函数的调用时间,提供详细的应用程序和组件性能的分析,并自动定位到运行缓慢的代码。这样就能帮助程序员尽快地调整应用程序的性能。
TrueTime支持的语言和主机平台
C++, JAVA, Visual Basic
Windows NT, Windows95/98
SmartCheck
作为一名Visual Basic的开发人员,在开发的过程中经常会遇到许多问题难以解决,包括象隐藏的run-time错误、Windows API函数在Visual Basic中正确使用的问题、一些组件的错误等等,它们很难被定位到具体的代码中,令开发人员花费大量时间去寻找并解决。SmartCheck就是能很快地查找到这些问题的一个自动化的工具,它是对于Visual Basic来说最好的run-time调试工具,它检测所有的Windows API函数调用、内存分配以及其它一些重要的程序错误。SmartCheck检错的种类包括泄露、接口方法失败、存储错误、程序和函数失败和Visual Basic的Runtime错误等,它能够将检测到的错误快速地定位到源代码。使用SmartCheck将会极大地提高VB开发人员的工作效率。
SmartCheck 支持的语言和主机平台:TestAge
Visual Basic
Windows NT, Windows95/98
FailSafe是用于Visual Basic开发的一个自动错误处理和恢复系统。VB开发人员经常能够遇到程序执行时意外地终止,但是对于为什麽出现错误只提供了简短的、模糊的出错信息,使开发人员不能方便地发现错误的根源。如果使用了FailSafe,它将插入额外的代码对你的程序进行插装,当程序执行时,FailSafe通过这些插装的代码捕获、记录执行时程序和系统的重要信息,直接指出错误发生时程序和系统的状态,这些丰富的信息使开发人员能够快速且正确的解决问题。
FailSafe 支持的语言和主机平台:
Visual Basic
Windows NT, Windows95/
对于Visual Basic开发人员来说,CodeReview是最好的自动源代码分析工具,它对应用程序的组件、逻辑、Windows和Vb自身潜在的数百个问题进行严格地源代码检查。CodeReview分析的类型包括Y2K问题,逻辑错误,应用程序性能和可用性问题,Windows API调用和标准一致性问题等。CodeReview可以检测整个的VB工程或指定的模块,并能定制检错的种类;对检测的结果有详细的说明,提供帮助和推荐解决方案,而且能够直接的链接到源代码。
CodeReview系统还提供了两个子模块,一个是Metrics:通过对VB工程(vbp)的执行,计算出代码的长度、复杂度、理解度、语言的使用等级、出错的可能性等数据;另一个是Namer:它调用一个VB工程,自动并规则地对其中的对象重新命名,并备份原来没有规则命名的工程文件,使开发人员对程序能够有条理地管理。
可以这麽说:CodeReview是Visual Basic开发人员必不可少的顾问。
CodeReview 支持的语言和主机平台:
Visual Basic
Windows NT, Windows95/98
JCheck对于Java开发人员来说是一个功能强大的图形化的线程和事件分析工具,它提供了一个生动的图形化的方法来表现程序的线程的状态信息以及和Windows线程、同步对象、线程组等的交互作用信息,使开发人员能够直观地分析Java Applet或Application:通过这些形象化的图形显示,可以确定runtime错误,对执行和逻辑错误进行分析,立刻发现线程问题如死锁、活锁、资源缺乏和系统失败,诊断线程同步和时间选择问题,分析程序执行流程;而后JCheck对于那些错误可以定位和显示详细的信息并能定位到源代码。Jcheck极大地减少了程序的调试时间,改善了软件开发生产力。TestAge 中国软件测试时代WB"j#Hd
JCheck 支持的语言和主机平台:
Microsoft Visual J++
Windows NT, Windows95/98
转自:http://www.cnblogs.com/junzhongxu/
谈:面向对象的软件测试与传统测试的比较[3]
5、 面向对象的单元测试
传统的单元测试的对象是软件设计的最小单位——模块。单元测试的依据是详细设描述,单元测试应对模块内所有重要的控制路径设计测试用例,以便发现模块内部的错误。单元测试多采用白盒测试技术,系统内多个模块可以并行地进行测试。
当考虑面向对象软件时,单元的概念发生了变化。封装驱动了类和对象的定义,这意味着每个类和类的实例(对象)包装了属性(数据)和操纵这些数据的操作。而不是个体的模块。最小的可测试单位是封装的类或对象,类包含一组不同的操作,并且某特殊操作可能作为一组不同类的一部分存在,因此,单元测试的意义发生了较大变化。我们不再孤立地测试单个操作,而是将操作作为类的一部分。
6、 面向对象的集成测试
传统的集成测试,有两种方式通过集成完成的功能模块进行测。(一)自顶向下集成:自顶向下集成是构造程序结构的一种增量式方式,它从主控模块开始,按照软件的控制层次结构,以深度优先或广度优先的策略,逐步把各个模块集成在一起。(二)自底向上集成:自底向上测试是从“原子”模块(即软件结构最低层的模块)开始组装测试。
因为面向对象软件没有层次的控制结构,传统的自顶向下和自底向上集成策略就没有意义,此外,一次集成一个操作到类中(传统的增量集成方法)经常是不可能的,这是由于“构成类的成分的直接和间接的交互”。对OO软件的集成测试有两种不同策略,第一种称为基于线程的测试,集成对回应系统的一个输入或事件所需的一组类,每个线程被集成并分别测试,应用回归测试以保证没有产生副作用。第二种称为基于使用的测试,通过测试那些几乎不使用服务器类的类(称为独立类)而开始构造系统,在独立类测试完成后,下一层的使用独立类的类,称为依赖类,被测试。这个依赖类层次的测试序列一直持续到构造完整个系统。
7、 面向对象的系统测试
通过单元测试和集成测试,仅能保证软件开发的功能得以实现。但不能确认在实际运行时,它是否满足用户的需要。为此,对完成开发的软件必须经过规范的系统测试。系统测试应该尽量搭建与用户实际使用环境相同的测试平台,应该保证被测系统的完整性,对临时没有的系统设备部件,也应有相应的模拟手段。系统测试时,应该参考OOA分析的结果,对应描述的对象、属性和各种服务,检测软件是否能够完全"再现"问题空间。系统测试不仅是检测软件的整体行为表现,从另一个侧面看,也是对软件开发设计的再确认。
面向对象测试的整体目标——以最小的工作量发现最多的错误——和传统软件测试的目标是一致的,但是OO测试的策略和战术有很大不同。测试的视角扩大到包括复审分析和设计模型,此外,测试的焦点从过程构件(模块)移向了类。
软件测试与传统测试不论是传统的测试方法还是面向对象的测试方法,我们都应该遵循下列的原则:
1.应当把“尽早和不断地测试”作为开发者的座右铭。
2.程序员应该避免检查自己的程序,测试工作应该由独立的专业的软件测试机构来完成。
3.设计测试用例时,应该考虑到合法的输入和不合法的输入,以及各种边界条件,特殊情况下要制造极端状态和意外状态,比如网络异常中断、电源断电等情况。
4.一定要注意测试中的错误集中发生现象,这和程序员的编程水平和习惯有很大的关系。
5.对测试错误结果一定要有一个确认的过程。一般有A测试出来的错误,一定要有一个B来确认,严重的错误可以召开评审会进行讨论和分析。
6.制定严格的测试计划,并把测试时间安排得尽量宽松,不要希望在极短的时间内完成一个高水平的测试。
7.回归测试的关联性一定要引起充分的注意,修改一个错误而引起更多错误出现的现象并不少见。
8.妥善保存一切测试过程文档,意义是不言而喻的,测试的重现性往往要靠测试文档。
转自:http://www.cnblogs.com/junzhongxu/
谈:面向对象的软件测试与传统测试的比较[2]
对认定的结构的测试
对认定的主题的测试
对定义的属性和实例关联的测试
对定义的服务和消息关联的测试
3、 面向对象设计的测试
通常的结构化的设计方法,用的"是面向作业的设计方法,它把系统分解以后,提出一组作业,这些作业是以过程实现系统的基础构造,把问题域的分析转化为求解域的设计,分析的结果是设计阶段的输入"。而面向对象设计(OOD)采用"造型的观点",以OOA为基础归纳出类,并建立类结构或进一步构造成类库,实现分析结果对问题空间的抽象。由此可见,OOD不是在OOA上的另一思维方式的大动干戈,而是OOA的进一步细化和更高层的抽象。所以,OOD与OOA 的界限通常是难以严格区分的。OOD确定类和类结构不仅是满足当前需求分析的要求,更重要的是通过重新组合或加以适当的补充,能方便实现功能的重用和扩增,以不断适应用户的要求。因此,对OOD的测试,应从如下三方面考虑:
对认定的类的测试
对构造的类层次结构的测试
对类库的支持的测试
4、 面向对象编程的测试
典型的面向对象程序具有继承、封装和多态的新特性,这使得传统的测试策略必须有所改变。封装是对数据的隐藏,外界只能通过被提供的操作来访问或修改数据,这样降低了数据被任意修改和读写的可能性,降低了传统程序中对数据非法操作的测试。继承是面向对象程序的重要特点,继承使得代码的重用率提高,同时也使错误传播的概率提高。多态使得面向对象程序对外呈现出强大的处理能力,但同时却使得程序内"同一"函数的行为复杂化,测试时不得不考虑不同类型具体执行的代码和产生的行为。
面向对象程序是把功能的实现分布在类中。能正确实现功能的类,通过消息传递来协同实现设计要求的功能。因此,在面向对象编程(OOP)阶段,忽略类功能实现的细则,将测试的目光集中在类功能的实现和相应的面向对象程序风格,主要体现为以下两个方面。
数据成员是否满足数据封装的要求
转自:http://www.cnblogs.com/junzhongxu/
谈:面向对象的软件测试与传统测试的比较[1]
软件的质量不仅是体现在程序的正确性上,它和编码以前所做的需求分析,软件设计也密切相关。这时,对错误的纠正往往不能通过可能会诱发更多错误的简单的修修补补,而必须追溯到软件开发的最初阶段。因此,为了保证软件的质量,我们应该着眼于整个软件生存期,特别是着眼于编码以前的各开发阶段的工作。于是,软件测试的概念和实施范围必须扩充,应该包括在整个开发各阶段的复查、评估和检测。由此,广义的软件测试实际是由确认、验证、测试三个方面组成。
在整个软件生存期,确认、验证、测试分别有其侧重的阶段。确认主要体现在计划阶段、需求分析阶段、也会出现在测试阶段;验证主要体现在设计阶段和编码阶段;测试主要体现在编码阶段和测试阶段。事实上,确认、验证、测试是相辅相成的。确认无疑会产生验证和测试的标准,而验证和测试通常又会帮助完成一些确认,特别是在系统测试阶段。
传统的测试计算机软件的策略是从“小型测试”开始,逐步走向“大型测试”。即我们从单元测试开始,然后逐步进入集成测试,最后是有效性和系统测试。在传统应用中,单元测试集中在最小的可编译程序单位——子程序(如,模块、子例程、进程),一旦这些单元均被独立测试后,它被集成在程序结构中,这时要进行一系列的回归测试以发现由于模块的接口所带来的错误和新单元加入所导致的副作用,最后,系统被作为一个整体测试以保证发现在需求中的错误。
面向对象程序的结构不再是传统的功能模块结构,作为一个整体,原有集成测试所要求的逐步将开发的模块搭建在一起进行测试的方法已成为不可能。而且,面向对象软件抛弃了传统的开发模式,对每个开发阶段都有不同以往的要求和结果,已经不可能用功能细化的观点来检测面向对象分析和设计的结果。因此,传统的测试模型对面向对象软件已经不再适用。
1、 面向对象测试模型
面向对象的开发模型突破了传统的瀑布模型,将开发分为面向对象分析(OOA),面向对象设计(OOD),和面向对象编程(OOP)三个阶段。针对这种开发模型,结合传统的测试步骤的划分,我们把面向对象的软件测试分为:面向对象分析的测试,面向对象设计的测试,面向对象编程的测试,面向对象单元测试,面向对象集成测试,面向对象系统测试。
2、 面向对象分析的测试
传统的面向过程分析是一个功能分解的过程,是把一个系统看成可以分解的功能的集合。这种传统的功能分解分析法的着眼点在于一个系统需要什么样的信息处理方法和过程,以过程的抽象来对待系统的需要。而面向对象分析(OOA)是"把E-R图和语义网络模型,即信息造型中的概念,与面向对象程序设计语言中的重要概念结合在一起而形成的分析方法",最后通常是得到问题空间的图表的形式描述。OOA直接映射问题空间,全面的将问题空间中实现功能的现实抽象化。将问题空间中的实例抽象为对象,用对象的结构反映问题空间的复杂实例和复杂关系,用属性和操作表示实例的特性和行为。对一个系统而言,与传统分析方法产生的结果相反,行为是相对稳定的,结构是相对不稳定的,这更充分反映了现实的特性。OOA的结果是为后面阶段类的选定和实现,类层次结构的组织和实现提供平台。因此,对OOA的测试,应从以下方面考虑:
转自:http://www.cnblogs.com/junzhongxu/