亮's profileRyan's SpacePhotosBlogLists Tools Help

Blog


    April 25

    钧霆的出处


    (鈞)
    jūn
    古代重量单位,合三十斤:千钧一发(一根头发挂着三万斤重的东西,喻极其危急)。雷霆万钧之势。
    制陶器所用的转轮:陶钧(亦喻造就人材)。
    乐(yu?)调(di刼 ):钧弦。
    敬辞,用于对尊长或上级:钧安。钧鉴。钧座。
    笔画数:9;
    部首:钅;
    笔顺编号:311153541

    详细解释:
    --------------------------------------------------------------------------------


    jūn
    【名】
    (形声。从金,匀声。本义:古代重量单位。三十斤为一钧)
    同本义〖anancientunitofweight(equalto30catties)〗
    钧,三十斤也。——《说文》
    入钧金。——《周礼·大司寇》
    钧衡石。——《礼记·月令》
    颜高之弓六钧。——《左传·定公八年》
    吾力足以举百钧。——《孟子·梁惠王上》
    又如:钧衡(钧和衡都是量物的工具,因借为评量人才之意);千钧一发;雷霆万钧;钧石(钧和石。四钧为石);钧金(三十斤铜);钧分(一钧半,合四十五斤)
    制陶器所用的转轮〖potter’swheel〗
    独化于陶钧之上。——《史记·邹阳传》。索隐:“范也。”
    陶家名模下圆转者为钧。——《汉书》注
    大钧播物。——《汉书·贾谊传》
    钧旋毂转。——《淮南子·原道》
    又如:钧甄(钧和甄。都是制陶用的转轮)
    调节乐音的标准〖tuningstandard〗。如:钧容直(钧容班。宋代宫廷仪仗中的乐队);钧天广乐(钧天。神话传说中指天上的音乐)
    喻国政〖power〗。如:钧枢(执掌国政的人)
    乐调〖tune〗。如:钧球(乐调和谐的玉磬)


    jūn
    【形】
    你的,你们的,或与你、你们有关的〖your〗——旧时的敬词,对尊长或上级用。如:钧座(书函公文中对行政尊长的敬称。也称“钧席”);钧眷(尊称别人的眷属);钧旨(尊称上司的命令);钧鉴(敬称。明察;详察)
    通“均”。相同;相等;均衡;均匀〖same;equal;balanced;even;uniform〗
    其罪惟钧。——《书·吕刑》
    敦弓既坚,四鍭既钧。——《诗·大雅·行苇》
    钧是人也。——《孟子·告子上》
    是以圣人和之以是非,而休乎天钧。——《庄子·齐物论》
    善均从众。——《左传·成公六年》
    钧则曰左右均。——《礼记·投壶》注:“等也。”
    夫名异则实殊,质同则称钧。——《论衡·实知》
    又如:钧平(均等,相等);钧等(均等);钧德(功德相同);钧谐(均衡和谐)
    〖马色〗纯一〖pure〗。如:钧驷(毛色纯一的驷马)
     

    tíng
    劈雷,霹雳:雷霆万钧。
    震动:“天冬雷,地冬霆,草木夏落而秋荣”。
    笔画数:14;
    部首:雨;
    笔顺编号:14524444312154

    详细解释:
    --------------------------------------------------------------------------------

    tíng
    【名】
    (形声。从雨,廷声。“霆、電”实同一词,后来岐为二义,其声曰霆,其光曰電。轻雷或无电,疾雷必有电,故霆的本义:为疾雷,为霹雳)
    同本义〖violentthunderclaps〗
    霆,雷余声也——《说文》
    疾雷为霆。——《尔雅·释天》
    如雷如霆。——《诗·大雅·常武》
    若疾霆转雷而激迅风也。——《文选·张衡·东京赋》
    畏之如雷霆。——《左传·襄公十四年》
    千雷万霆。——唐·李朝威《柳毅传》
    又如:霆激(雷霆激发。形容来势凶猛;比喻迅速快捷);霆击(快速攻击,如雷霆一般迅疾)
    闪电〖lightning〗
    疾雷不及塞耳,疾霆不暇掩目。——《淮南子》

    tíng
    【动】
    震动〖shake〗
    天冬雷,地冬霆,草木夏落而秋荣。——《管子》。尹知章注:“霆,震。”

    June 15

    SQL Server 2008 RC0中的Report Designer呢?

    微软在在6月5日发布了SQL Server 2008 RC0,首先在MSDN和Technet订阅区放开下载,不过最近在Downloads站点也看到了下载。

    这个周末得空装了一个RC0的环境,不过安装完了之后却发现原来在以前各个版本的CTP中的Report Designer不见了。百般搜索无果后突然在微软的下载站点找到了答案:原来CTP中的Report Designer已经被更名为Report Builder 2.0,作为SQL Server 2008 RC0 Feature Pack中的一部分,这个Report Builder 2.0必须从网上单独下载。

    不过有人可能会有疑问,Report Builder不是利用Report Model设计报表的么,而且是在Report Manager中下载的么?

    是的,不过那是Report Builder 1.0,Report Builder 1.0仍然可以在SQL Server 2008 Reporting Service内置的Report Manager站点上下载。SQL Server 2008提供单独下载的是Report Builder 2.0。

    现在看界面可以初步确定的是Report Builder 1.0仍然用于设计基于Report Model的报表,而Report Builder 2.0应该是设计基于常规数据源的报表。

    Report Builder 2.0

    June 11

    万恶的缩写

    今天在看技术资料的时候发现了一个缩写——UBO,思前想后才明白UBO就是Usage Based Optimizer的缩写,因此突然感慨用缩写来发明新单词的办法不知道是谁起的头。顺便总结几个SQL Server 2005时代里面的缩写:

    SQL Server 工具类所写:

    • SSMS:SQL Server Management Studio
    • BIDS:Business Intelligence Development Studio
    • DTA:Database Engine Tuning Advisor
    • SSAC:SQL Server Surface Area Configuration

    SQL Server服务和产品的缩写:

    • SSAS:SQL Server Analysis Service
    • SSRS:SQL Server Reporting Service
    • SSIS:SQL Server Integration Service
    • FTS:FullText Search
    • SSCE:SQL Server Compact Edition
    • SSEE:SQL Server Express Edition
    • SSME:SQL Server Mobile Edition

    大家要是想到什么缩写就跟上来补一下吧。

    June 08

    SQL Server 2008 RC0新的Logo

    SQL Server 2008 RC0已经在6月5日发布啦,最先可以下载的是MSDN和Technet订阅用户,不过现在Download站点里面也可以下载到了。

    感谢我的老婆第一时间帮我PS了SQL Server 2008的桌面,这个桌面在SQL Server开发组成员的Blog上面看到过,不过像素太小。

    March 27

    SQL Server 2008中的TDE

    在SQL Server 2005中,我们得到了一个新的功能:数据加密,在SQL Server 2008中,这一功能得到了进一步的增强。除了SQL Server 2005中支持的Cell级加密外,现在SQL Server 2008加入了整库加密,微软称其为数据库透明加密Transparent Data Encryption(我们后面简称TDE)。相比SQL Server 2005的加密,TDE工作在SQL Server的IO层,因此开销更小,同时对应用程序的影响也降低到零。

    因此在Windows Server 2008平台上,我们现在对数据库的数据加密保护手段已经有了4种,分别是:

    • Windows 2000开始加入的EFS功能
    • Windows Vista和Windows Server 2008开始的BitLockers
    • SQL Server 2005开始引入的Cell级加密
    • SQL Server 2008加入的TDE

    关于这4项技术的对比会在稍后的帖子中讨论.

    SQL Server的数据加密框架

    先看一下SQL Server 2005的加密体系:

    image

    然后再看一下SQL Server 2008中的数据加密体系:

     

    相比之下,SQL Server 2008的加密体系多出了TDE这条路径,就是上图中虚线部分,SQL Server 2008会通过DMK(Database Master Key数据库主密钥)直接加密数据文件,也可以通过EKM(Extensible Key Management外部密钥管理)直接加密数据文件。

    SQL Server 2005与SQL Server 2008相同之处在于两者都是用Windows操作系统提供的DPAPI(Data Protection API数据保护应用开发接口)来保护SMK(Service Master Key服务主密钥),每个SQL Server的实例都拥有自己的SMK

    EFS对SQL Server的性能影响主要原因是SQL Server在EFS加密文件上执行IO的时候IO操作是同步操作,也就是说如果SQL Server的工作线程需要一次IO就必须等到当前的文件IO结束,因此SQL Server中的Read Ahead机制和CheckPoint机制都会受到影响。关于这个问题的具体讨论可以参考微软支持网站的KB922121

    SQL Server 2008基于策略的管理

    SQL Server 2008通过引入可扩展的基于策略的管理功能降低了数据平台的管理工作量。其中最引人注意的莫过于提供策略化管理的Declarative Management Framework (简称DMF),不过这个名称已经被启用了,现在正是名字是基于策略的管理(Policy Based Management),我们姑且简称为PBM。PBM使DBA们可以制定管理策略,并将这些策略应用到服务器、数据库以及数据环境中的其他对象上去。经过精心设计的管理策略可以帮助DBA们对数据环境进行前摄性的管理。

    PBM的优势体现在:

    • 按需管理:PBM提供了系统配置的逻辑视图,因此DBA们可以预先定义各自所需要的数据服务配置,而不用等到这些需要实际发生的时候再去配置。
    • 智能监控:PBM可以持续监控系统的配置变化,并阻止那些违反了策略的配置变化操作。
    • 虚拟管理:通过PBM,DBA们可以对多台服务器进行规模化管理,在企业内部统一实施某些强制性配置会变得更加方便。

    基于策略管理的框架

    PBM的框架有三部分组成:

    • 策略管理:管理员制定各种策略。
    • 显式管理:管理员通过对指定的目标或目标群应用策略来检查目标对策略的依从性,或者更严格的是禁止这些目标上违反策略的行为发生。
    • 执行模式:SQL Server 2008的PBM支持4种执行模式,这4种模式决定了策略对目标的影响程度。这四种模式分别是:
      • 按需(On Demand):这种模式下的策略可以有管理员自由的选择是否应用,例如管理员可以手动调用这些策略来检查目标的依从性,或者通过DDL Trigger来订阅这些策略。
      • 更新时阻止(On Change - Prevent):这是最严格的一种,SQL Server 2008通过DDL Trigger的方式在订阅该策略的目标上发生操作时实施检查操作对策略的符合性,如果违反策略则回滚该操作,以达到强制策略的效果。
      • 更新时记录(On Change - Log Only):SQL Server 2008通过Event Notification的机制在在订阅该策略的目标上发生操作时实施检查操作对策略的符合性,如果违反策略则发送消息,就将该违反操作通过Service Broker的队列发送进行记录。
      • 按计划(On Schedule):通过SQL Agent的作业来调用策略对目标对象进行检查。

    虽然PBM有以上四种执行模式,但是归总起来其实是两大种,一种是基于SQL Agent作业方式的On Schedule模式,而另外一种是基于Event机制的On Change模式。因此并非所有Facet都支持On Change模式,要支持On Change模式,那么Facet的状态改变必须可以通过事件捕获或者事务性的DDL操作,当然On Schedule和On Demand就没有这些机制,因为这两种模式无需参与到Facet状态更新的事务中去。

    基于策略管理的架构图

    image

    基于策略管理的术语

    • 对象(Facets):包括策略管理中某个方面的相关配置属性。例如在Surface Area中包括了像Database Mail Enabled以及CLR Integration Enabled之类的SQL Server功能的属性。
    • 条件(Conditions):表示一个方面的状态。条件是基于单个方面的,并且可以被一个或多个策略使用。例如,DBA可以建立一个名为Minimal Surface Area的条件,在这个条件中将Surface Area Facet中的所有属性都设置为False。
    • 策略(Policies):包括了用于约束单个或多个目标的条件。例如DBA可以创建一个名为Locked Down Server的策略,在这一策略中将Minimal Surface Area条件指派给某台服务器。
    • 类别(Categories):包含一条或多条策略。数据库拥有者可以将一个或多个分类绑定到数据库上。例如,DBA可以创建一个名为Corporate DB Policies的分类,其中包含一条强制数据库对象命名规则的策略和一条强制数据库兼容级别的策略,并将该分类绑定到业务数据库上。通常所有数据库都绑定到默认分类,但是可以在服务器或数据库级别上将分类设置为激活(Active)或暂停(Inactive),这样管理员就可以灵活控制策略的强制性。
    • 目标(Targets):目标代表像服务器、数据库、登录、表以及其他数据库对象各种被指派策略的实体。在一个SQL Server实例中的所有目标组成了一个目标层级。对于某个策略,DBA可以通过对目标层级进行筛选来定义一个目标集合。例如,DBA可以定义一个包含Production架构拥有的所有索引的目标集合。

    为策略检查配置警报

    如果某项策略被违反,SQL Server 2008会生成相应的警报,因此可以通过在SQL Agent中配置警报来监控这些事件,

    执行模式 事件号

    On Change - Prevent
    (if automatic)

    34050

    On Change - Prevent
    (if On Demand)

    34051
    On schedule 34052
    On change

    34053

    策略管理的安全性

    属于PolicyAdministratorRole的成员才可以制定和修改策略定义,这个角色的成员是必须要小心控制的,因为恶意用户可以通过制定苛刻的策略来达到类似于拒绝服务攻击的效果。

    基于策略管理的配置

    基于策略管理的常规配置步骤为:

    1. 根据Facet创建Condition,Condition可以作为Policy的检查条件,也可以是用于确定策略应用范围的筛选条件。
    2. 引用已经创建好的策略来定义策略,同时可以在策略检查条件可以应用的对象集上附加筛选条件,例如一个检查Multiple Part Name对象状态的策略就可以有表、存储过程、视图、同义词等一系列的对象可以进行选择并附加筛选条件,默认的筛选条件是Every,也就是这个对象集中所有的对象。当然不是所有条件都可以充当筛选条件的,在二月CTP的测试中就发现包含LIKE运算符的表达式的条件就不能充当筛选条件。
    3. 如果需要可以在Server Restriction中定义需要应用策略的SQL实例条件。
    4. 如果需要还可以在Policy Management节点上定义策略组,并在策略的定义中将策略归入某个类别,然后由服务器管理员或数据库拥有者订阅某个策略组。不过要注意看看我的前一个帖子,默认策略组都是强制订阅的,要启用自选订阅要在策略组管理中设置。
    5. 然后就等着策略帮你自动管理SQL Server了,这就看策略定义时选择的执行模式了。
    March 25

    关于SQL Server 2008是否是SQL Server 2005的一个Service Pack

    随着SQL Server 2008越来越多地进入到大家的视线中,也有越来越多的声音说SQL Server 2008 (Kaitmai)仅仅是SQL Server 2005的一个补丁包。当初我也是这个想法,因为我们把太多的目光放到了例如Geo-Spatial支持、Hierarchical ID、File Stream上,确实这些功能都是建立在SQL Server 2005搭建的CLR -Integrated平台上的,但是我们也需要看到SQL Server 2008中的其他功能,例如Resource Governor、Declarative Management Framework、Change Data Capture、Reporting Service IIS agnostic report deployment等等。SteveB在美国Launch中更将Policy-base Management列在SQL Server 2008 Top 10新功能的首位,还有美国Launch上关于Activity Monitor的改进、Object Search以及T-SQL Debugger被用户爱称为三项Surprise Features。当然还是会有人说这些都是在DMV和DDL Triggers的技术基础或技术路线上发展起来的,不过哪个产品不是从一条技术路线或某项基础性改革上发展起来的呢?

    因此微软把SQL Server 6.0/6.5称为第一代数据库产品,SQL Server 7.0/2000称为第二代数据库,而SQL Server 2005/2008称为第三代数据库产品。以此类推,那么SQL Server vNext是不是会变成第四代数据库产品呢,那又会有什么革命性的改变呢?Grid?...哦,跑题了

    因此我们还是应该宽容地看待SQL Server 2008,还是应该升级到SQL Server 2008的,升级有问题可以咨询EMC MSP么,哈哈哈~~~~!

    附上SQL Server 2008现在的新特性以及改进列表:

  • Policy-Based Management
  • Management/performance data collection
  • extended events
  • database mirroring enhancements
  • Query plan freezing
  • resource governor
  • transparent data encryption
  • external key management
  • Hot add CPU
  • partition aligned index views
  • backup compression
  • Table valued parameters
  • DDL trigger enhacements
  • T-SQL programming enhancements
  • T-SQL Intellisense
  • Entity data model
  • LINQ
  • Visual entity designer
  • Service Broker enhacements
  • SQLCLR enhacements
  • Multi-server query
  • Configuration servers
  • Hierarchy ID
  • Large UDTs
  • Date/Time data type
  • Improved XML support
  • Geography data type
  • Geometry data type
  • Filestream
  • Analysis Services scalable backup
  • Analysis Services dimension design
  • Star join query optimization
  • MERGE SQL statement
  • Change data capture
  • Analysis Services MDX enhacements
  • Analysis Services Personalization extensions
  • Analysis Services aggregation designer
  • Analysis Services Data Mining enhanced validation
  • Analysis Services Data Mining and enhanced structures
  • Analysis Services MOLAP writeback
  • Analysis Services dynamic management views
  • Integration Services C# scripting
  • Integration Services persistent look-ups
  • Integration Services data profiling
  • Integration Services import/export wizard enhancements
  • Integration Services Improved connectivity
  • Reporting Services IIS agnostic report deployments
  • Reporting Services report builder enhancements
  • Reporting Services core engine enhacements
  • Improved installation
  • March 24

    如何在SQL Server 2008中订阅策略组

    SQL Server 2008中的DMF可以极大程度地降低管理成本,不过今天在学习DMF的配置时偶几乎吐血加厥倒。

    原因是DMF的策略可以提供策略组的功能,用户可以选择订阅某些组的策略,一般策略都是属于Default组,而策略管理员可以创建一些策略组,并将策略放置到组中,以便于DBA选择策略进行应用。不过我屡次尝试都发现创建的策略组是无法订阅的,因为订阅的Checkbox始终是灰色的,在经历了疑惑、焦躁、愤怒和无奈后,最终发现了这个问题的症结。

    为了避免大家也经历这种和MM分手时相类似的心理历程,贴图如下

    1、打开策略组的定义对话框

    Open Policy Category Management Dialog

    2、定义策略组

    Policy Category Origin

    3、千万记得横向滑动列表框到底,天晓得这个后面还有一列,只有取消了Checkbox,这个策略组才是可以手动订阅的,默认有点类似于组策略中的强制策略一样。诶,微软的Dev为什么不把这列放到前面呢?!看来易用性的细节是无处不在阿。

    Policy Category Rolled toEnd

    为纪念这一经历,我特将MSN签名改为“SQL Server 2008 Feb CTP最新心得:拖动条很重要!!!”三天,以此警醒。

    March 18

    怎样让Windows Server 2008象Vista

    调出Vista的Aero

    第一步当然是要安装Desktop Experience功能,启用Theme服务,然后在Personalize Appearance and Sounds的Theme中选择Windows Vista Theme,接着在Windows Color and Experience中选择Windows Aero的Color Scheme。

    使用Live

    Live Writer没有什么问题,主要是Live Messenger。

    首先是把Vista下安装好的目录原样拷贝过来,但是要在system32目录下再拷贝一个sirenacm.dll文件。将sirenacm.dll拷贝到Windows Server 2008的system32目录即可。不过这个时候Live Messenger的窗口中可能找不到启用Live Today、Space、Sharing Folder和Email的四个按钮,需要到注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Live\Messenger键中添加一条值:

    Name Type Value
    Primary Service REGSZ .Net Messenger Service

    使用Sidebar

    Sidebar是最头疼的,费了我大把时间,最后还是王博给了个链接,终于解决。这里是地址http://www.reggieburnett.com/post/Sidebar-working-nicely-on-Windows-Server-2008.aspx

    主要过程就是把Windows Sidebar整个目录都拷贝过来,然后执行下面的命令:

  • regsvr32 sbdrop.dll
  • regsvr32 wlsrvc.dll
  • sidebar /RegServer
  • 前面两条我都执行了,唯独不知道最后一条,因此老是在加载Gadget的时候爆Library not registered的错误。感谢王博,感谢这位reggie。

    使用Notebook Presenter 8000的蓝牙棒连接手机

    Vista下面安装IntelliPoint 6.1后自动会有驱动安装,蓝牙棒就能连手机了,不过在Server 2008下面就不行了,说是有个驱动找不到,这个还在找解决方案。有晓得的兄弟一定要共享。

    March 11

    SQL Server 2008 二月CTP中的CDC小改动

    上一周在微软MSDN上Webcast的时候,突然发现一段在CTP5,也就是去年的十一月CTP中测试过的CDC演示代码不能用了,提示是系统无法找到sp_cdc_enable_table_change_data_capture,sp_cdc_enable_db_change_data_capture等一系列存储过程,原以为SQL Server 2008安装的有问题,是不是那些个系统存储过程没有生成。不过后来一看系统对象列表才晓得在二月CTP中CDC的部分存储过程名字已经被改变了。现在所有存储过程后面都删除了change_data_capture后缀。

    想想也对,这些个存储过程已经有cdc前缀了,何苦再加个功能全名的后缀呢?不过再一看,二月CTP的联机丛书里面可没有提到这个改变,而且联机丛书中的CDC配置说明用的还是老的存储过程名称,也就是带change_data_capture后缀的。

    诶,看来版本管理还是很重要的阿,相信如果有人直接从二月CTP开始评估Kaitmai的话,一定会被这个小改动混淆一下的了。

    March 02

    SQL Server 2008中的数据压缩功能

    SQL Server 2005 SP2为我们带来了vardecimal功能,这项功能使得原来定长的decimal数据在数据文件中以可变长的格式存储,据称这项功能可以为典型的数据仓库节省30%的空间,而SQL Server 2008在这一基础上又进一步增强了数据压缩功能。SQL Server 2008现在支持行压缩和页面压缩两种选项,数据压缩选项可以在以下对象上启用:

    • 未创建聚簇索引的表
    • 创建聚簇索引的表
    • 非聚簇索引(对表设置压缩选项不会影响到该表上的非聚簇索引,因此聚簇索引的压缩需要单独设置)
    • 索引视图
    • 分区表和分区索引中的单个分区

    为什么需要数据压缩

    首先可能需要讨论的问题就是为什么在存储成本不断降低的今天,微软还要煞费苦心地在SQL Server中实现并且不断改进数据压缩技术呢?

    尽管存储成本已经不再是传统意义上的首要考虑因素,但是这并不代表数据库尺寸不是一个问题,因为数据库尺寸除了会影响到存储成本之外,还极大地关联到管理成本和性能问题。

    首先我们来讨论为什么会有管理成本的问题?因为数据库需要备份,数据库的尺寸越大,那么备份时间就会越长,当然另外一点就是消耗的备份硬件成本也会随之提高(包括需要的备份介质成本和为了满足备份窗口而需要更高级的备份设备带来的采购成本),还有一种管理成本就是数据库的维护成本,例如我们经常需要完成的DBCC任务,数据库尺寸越大,我们就需要更多的时间来完成这些任务。

    接着我们再看看性能问题。SQL Server在扫描磁盘读取数据的时候都是按照数据页为单位进行读取的,因此如果一张数据页中包含的数据行数越多,SQL Server在一次数据页IO中获得的数据就会越多,这样也就带来了性能的提升。

    最后考虑存储的成本,按照原先SQL Server 2005 SP2中vardecimal的压缩数据为例,30%的空间节省也就意味着30%的存储成本,而按照SQL Server 2008当前放出的测试数据,采用新的数据压缩技术可以达到2X-7X的存储率,再加上如果企业要考虑容灾而增加的存储空间,这样节省的存储硬件成本也将是想当可观的。

    如何使用数据压缩

    SQL Server 2008中的压缩选项可以在创建表或索引时通过Option进行设置,例如:
    CREATE TABLE TestTable (col1 int, col2 varchar(200)) WITH (DATA_COMPRESSION = ROW);

    如果需要改变一个分区的压缩选项,则可以用以下语句:

    ALTER TABLE TestTable REBUILD PARTITION = 1 WITH (DATA COMPRESSION = PAGE);

    如果需要为分区表的各个分区设置不同的压缩选项,可以使用以下的语句:(SQL Server 2008可以对不同的分区使用不同的压缩选项,这一点对于数据仓库应用是非常重要的,因为数据仓库的事实表通常都会有一个或数个热分区,这些分区中的数据经常需要更新,为了避免数据压缩给这些分区上的数据更新带来额外的处理载荷,可以对这些分区关闭压缩选项)

    CREATE TABLE PartitionedTable (col1 int, col2 varchar(200))
    ON PS1 (col1)
    WITH (
    DATA_COMPRESSION = ROW ON PARTITIONS(1),
    DATA_COMPRESSION = PAGE ON PARTITION(2 TO 4));

    如果是为某个索引设置压缩选项的话,可以使用:

    CREATE INDEX IX_TestTable_Col1 ON TestTable (Col1) WITH (DATA_COMPRESSION = ROW);

    如果是修改某个索引的压缩选项,可以使用:

    ALTER INDEX IX_TestTable_Col1 ON TestTable REBUILD WITH (DATA_COMPRESSION = ROW);

    SQL Server 2008同时还提供了一个名为sp_estimate_data_compression_savings存储过程帮助DBA估计激活压缩选项后对象尺寸。

    数据压缩是怎样工作的

    对于行压缩,SQL Server 2008采用以下三种方法来节省存储空间:

  • 减少了与记录相关联的元数据开销。此元数据为有关列、列长度和偏移量的信息。在某些情况下,元数据开销可能大于旧的存储格式。
  • 它对于数值类型(例如,integerdecimalfloat)和基于数值的类型(例如,datetimemoney)使用可变长度存储格式。
  • 它通过使用不存储空字符的可变长度格式来存储定长字符串。
  • 对于页面压缩,SQL Server 2008则是在一张数据页面上依次采用:

    1. 行压缩
    2. 前缀压缩
    3. 字典压缩

    配置数据压缩功能需要注意的

    尽管SQL Server 2008的数据压缩功能非常有价值,但是仍然需要注意一些问题:

    • 数据压缩功能仅在企业版和开发版中可用
    • 数据压缩可以让一张数据页存储更多的数据行,但是并不能改变单行数据最长8060字节这一限制
    • 在一张已经设置了数据压缩的表上创建聚簇索引时,聚簇索引默认继承原表上的压缩选项
    • 在未设置聚簇索引的表上设置页面压缩时,只有以下情况才会获得页面压缩的实际效果:
      • 数据使用BULK INSERT语法添加到表中
      • 数据使用INSERT INTO ... WITH (TABLOCK)语法添加到表中
      • 执行带有页面压缩选项的ALTER TABLE ... REBUILD命令
    • 在未设置聚簇索引的表上更改压缩选项,会导致该表上所有非聚簇索引都需要重建,因为这些非聚簇索引指向的数据行地址已经都发生了改变。
    • 在改变压缩选项时所需要的临时空间大小与创建索引是所需要的空间是一样的,因此对于分区表,我们可以逐个分区设置压缩选项来减少临时空间的需求压力。
    • 由于SQL Server 2008中数据压缩技术其实是SQL Server 2005 SP2中vardecimal技术的一个超集,因此设置了数据压缩后就没有必要保留vardecimal了。当然SQL Server 2008为了保持向后兼容性,在当前版本中仍然保留了vardecimal,但是SQL Server 2008的下一个版本及可能就会弃用vardecimal选项,因此做了这些设置的数据库应该尽早改变到数据压缩设置下。
    • SQL Server 2008的压缩选项是工作在存储引擎层的,对于SQL Server的其他部件来说这一特性是透明的,因此当我们用BULK LOAD的方式将外面的数据导入SQL Server时,会显著的增加CPU的工作载荷,同时将以压缩的数据表导出到外部文件时,可能会消耗比原来多很多的空间。
    February 01

    初探SQL Server 2008 Change Tracking

    前段时间评估了一下SQL Server 2008的CDC功能,总体发现CDC的开销还是不小的,特别是对日志文件的读。对于那些字段数量较多而且需要跟踪每个字段值更新的用户表,CDC的带来的额外开销则尤其明显。因此继续评估SQL Server 2008对数据更新跟踪的技术方案。

    在SQL Server 2008的联机丛书中提到了,Change Tracking适用于单向或双向的数据同步应用场景。在接下来的一些列试用中,我们就能深刻体会到这一点。

    激活Change Tracking

    要使用Change Tracking,首先需要在数据库级别启用Change Tracking功能,我们可以通过两种途径启用Change Tracking。

    1. 使用T-SQL语句,例如要启用TestCT数据库的Change Tracking功能,可以通过提交如下语句
      ALTER DATABASE TestCT
      SET CHANGE_TRACKING = ON
      (CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON)
    2. 在SQL Server Management Studio中通过设置数据库的属性,Change Tracking功能在数据库属性对话框中拥有一个独立的选项页。

    其中我们会注意到CHANGE_RETENTION参数和AUTO_CLEANUP参数。因为Change Tracking所有的信息都会被添加到一张内部表中,所以这张内部表将会无限制地增长,如果我们不希望这张表的数据量一直增长,Change Tracking功能提供了一个后台的进程自动对内部表进行清理,清理的依据就是CHANGE_RETENTION,每次清理进程运行的时候都会将超过CHANGE_RETENTION参数中设定时间的记录删除掉,而清理进程是否会运行则依赖于AUTO_CLEANUP参数。CHANGE_RETENTION参数的时间单位可以是分钟、小时或天。

    激活数据库级别的Change Tracking功能后,DBA就可以选择哪些需要更新跟踪的表了。我们同样也有两种方法在更新跟踪的表上启用Change Tracking功能:

    1. 使用T-SQL语句,例如要启用TestCT表上的更新跟踪,我们可以提交如下语句
      ALTER TABLE TestCT
      ENABLE CHANGE_TRACKING
      WITH (TRACK_COLUMNS_UPDATED = ON)
    2. 在SQL Server Management Studio中通过设置表的属性,Change Tracking功能在表属性对话框中拥有一个独立的选项页。

    其中我们注意到有个参数是TRACK_COLUMN_UPDATED,当这个参数被设置为ON的时候,UPDATE语句提交后在内部表中将会记录UPDATE语句影响了哪些列,对于INSERT和DELETE语句,则所有列都是被影响到的。

    使用Change Tracking

    Change Tracking提供的信息可不像Change Data Capture那么详细,Change Data Capture可以提供每个事务影响到的数据的前像和后像。Change Tracking功能通过CHANGETABLE系统表来获得更新的版本信息。

    CHANGETABLE有两种用法,一种是CHANGETABLE(CHANGES),一种是CHANGETABLE(VERSION)。前者用于返回某个sync_version后的数据变化情况,后者用于返回某行数据的最新的更改版本号。

    CHANGETABLE(CHANGES)

    这是一个表函数,语法结构为CHANGETABLE(CHANGES table, last_sync_version),table参数为激活Change Tracking功能的表名,last_sync_version参数是希望获取更新的最小版本号。

    table参数的值非常明白,我们希望获得哪张表的更新信息,就是这张表的名字了,而last_sync_version是影响返回结果集的主要因素。
    举个例子,我们用下面这段脚本来详细说明Change Tracking记录下的信息:

    CREATE TABLE TestCT
    (
        ID int PRIMARY KEY,
        Name varchar(50),
        Description varchar(200)
    )
    GO
    ALTER TABLE TestCT
    ENABLE CHANGE_TRACKING
    WITH (TRACK_COLUMNS_UPDATED = ON)
    GO

    这个时候TestCT表的Change Tracking功能就被激活了,这张表上所有的数据更新都会被记录到一张内部表中,接着我们就可以在TestCT表上提交各种DML语句了。
    INSERT INTO TestCT VALUES (1, 'ABC', NULL)

    这个时候我们可以通过CHANGETABLE函数来查看TestCT的更新历史
    SELECT * FROM CHANGETABLE(CHANGES TestCT, 0) CT
    返回结果为

    SYS_CHANGE
    _VERSION
    SYS_CHANGE
    _CREATION_VERSION
    SYS_CHANGE
    _OPERATION
    SYS_CHANGE
    _COLUMNS
    SYS_CHANGE
    _CONTEXT
    ID
    1 1 I NULL NULL 1

    返回结果集中:

    • SYS_CHANGE_VERSION是一个bigint的字段,表示的当前这行数据最新的更新版本号。
    • SYS_CHANGE_CREATION_VERSION代表的是当前数据行被插入数据表的更新版本号。
    • SYS_CHANGE_OPERATION是一个nchar(1)的字段,I代表Insert,U代表Update,D代表Delete。
    • SYS_CHANGE_COLUMNS代表更新操作影响到了哪些数据列,这个字段的结果是个varbinary(4100)。这个字段只有在表上激活Change Tracking时将TRACK_COLUMN_UPDATED选项设置为ON时才会返回有效值,并且对于INSERT和DELETE操作返回的都是NULL,因为DELETE和INSERT其实影响到了所有数据列,只有UPDATE操作才会返回值,这个字段的值可以通过CHANGE_TRACKING_IS_COLUMN_IN_MASK()函数来解析。
    • SYS_CHANGE_CONTEXT是一个varbinary(128)的字段,这个字段可以记录数据更新的上下文环境信息,不过上下文环境信息需要在提交DML语句时显式地通过WITH CHANGE_TRACK_CONTEXT语句提供。
    • ID是TestCT表的主键字段,因此如果TestCT的主键是内容为(ID, Name)的组合主键,则除了ID外,CHANGETABLE返回结果集中还会多一个Name字段。

    我们接着测试,
    UPDATE TestCT SET Name = 'abc' WHERE ID = 1

    提交完这句语句后,其实TestCT表已经有了两次更新,一次是INSERT操作,一次是UPDATE操作,但是这两次操作都是针对ID为1的这一行数据,这个时候我们在CHANGETABLE函数中通过不同的last_sync_version参数会得到不同的返回结果。

    SELECT * FROM CHANGETABLE(CHANGES TestCT, 0) CT
    返回结果为

    SYS_CHANGE
    _VERSION
    SYS_CHANGE
    _CREATION_VERSION
    SYS_CHANGE
    _OPERATION
    SYS_CHANGE
    _COLUMNS
    SYS_CHANGE
    _CONTEXT
    ID
    2 1 I NULL NULL 1

    从返回结果看,主键值为1的这行数据最近的更新版本为2,而插入表时的更新版本为1,代表这行数据在插入数据表后已经有过更新,但是SYS_CHANGE_OPERATION仍然是I。原因是我们选取的last_sync_version参数值是0,代表我们上次同步时数据更新版本是0,而这行数据是在版本0后被插入表的,因此这行数据还没有出现在同步的目的表中,因此在我们进行同步时首先要处理INSERT操作,而被INSERT到源表中的原始记录已经被更新了(在版本2时),因此我们只能将INSERT操作和UPDATE操作进行合并,直接取得UPDATE后的数据行写入目的表。

    但是如果我们提交的语句是
    SELECT * FROM CHANGETABLE(CHANGES TestCT, 1) CT
    返回结果则会变为

    SYS_CHANGE
    _VERSION
    SYS_CHANGE
    _CREATION_VERSION
    SYS_CHANGE
    _OPERATION
    SYS_CHANGE
    _COLUMNS
    SYS_CHANGE
    _CONTEXT
    ID
    2 1 U 0x0000000002000000 NULL 1

    这次SYS_CHANGE_OPERATION字段变成了U。原因是我们选取的last_sync_version参数值是1,代表我们上次同步时数据更新版本是1,也就意味着这行数据已经被同步到目的表中了,因此在我们进行同步时只需要处理UPDATE操作。

    接着我们尝试一下
    UPDATE TestCT SET ID = 5 WHERE ID = 1

    然后我们看看CHANGETABLE会返回什么
    SELECT * FROM CHANGETABLE(CHANGES TestCT, 2) CT
    返回结果则会变为

    SYS_CHANGE
    _VERSION
    SYS_CHANGE
    _CREATION_VERSION
    SYS_CHANGE
    _OPERATION
    SYS_CHANGE
    _COLUMNS
    SYS_CHANGE
    _CONTEXT
    ID
    3 NULL D NULL NULL 1
    3 3 I NULL NULL 5

    返回结果会变成两条,这是因为我们这次UPDATE修改的是主键,所以在表中其实是将原始记录删除,然后增加一行新的数据。在同步的时候我们就知道需要删除先前同步到目的表中的ID为1的记录,然后复制源表中ID为5的记录到目的表中。

    接下来还可以进行一系列的测试,比如说:

    • 在一个事务中修改多条记录,则会注意到这些记录在CHANGETABLE表函数返回的结果中拥有的SYS_CHANGE_VERSION是相同的。
    • 提交DDL语句,表架构的改变不会在CHANGETABLE表函数的结果中体现,也就是说表结构的改动不被Change Tracking记录,甚至新增加一个设置了默认值的字段。不过增加后对新的字段进行修改就可以被Change Tracking记录了。这说明了Change Tracking只跟踪DML,不跟踪DDL。不过这一点应该不是问题,因为SQL Server 2005开始就有DDL Trigger了,相对DDL语句的提交应该不会过于频繁,因此通过触发器的方案来实现对架构的跟踪应该从成本上是可以接受的。
    • 利用WITH CHANGE_TRACKING_CONTEXT (@context)语句在跟踪记录中留下DML语句执行上下文的信息,这一手段通常可用于跟踪最后一次更新是由哪个应用程序做出的、或由哪个个用户作出的,通过判断执行上下文可以在双向复制中解决冲突的问题。
    • 如果执行了TRUNCATE TABLE或者Cleanup进程清理了Change Tracking记录,那么可以通过CHANGE_TRACKING_MIN_VALID_VERSION函数了解可获取的最小更新版本号。如果这个最小版本号比复制目的端记录的最近一次复制成功的最大版本号都高,则意味着源数据库已经丢失了一部份尚未复制的记录,也就代表目的系统需要重新初始化。
    • 对于UPDATE语句,如果在启用表的Change Tracking功能是设置了Track_Column_Updated选项为ON,Change Tracking会记录下UPDATE语句影响到字段信息,这个信息可以通过使用CHANGE_TRACKING_IS_COLUMN_IN_MASK函数解析,比如说要知道TestCT表的Name字段是否在版本2的UPDATE操作中被影响到,可以使用CHANGE_TRACKING_IS_COLUMN_IN_MASK(COLUMNPROPERTY(OBJECT_ID('TestCT'), 'Name', 'ColumnId'), SYS_CHANGE_COLUMNS)函数。
    CHANEGTABLE(VERSION)

    这个表函数的语法结构为CHANGETABLE(VERSION table , { ( column_name [ , ...n ] ) , ( value [ , ...n ] ) },table参数是激活Change Tracking功能的表名,column_name是主键的字段名,如果主键是复合键,则需要列举主键中所有的字段,value是对应前面每个字段的值。例如上面代码,我们可以通过下面语句来得到ID为5的记录最近的更新版本号是多少:

    SELECT * FROM CHANGETABLE(VERSION TestCT, (ID), (5)) CT
    返回结果则会变为

    SYS_CHANGE
    _VERSION
    SYS_CHANGE
    _CONTEXT
    ID
    3 NULL 5

    由返回结果可以了解到ID为5的数据在当前系统中最后一次更新的更新版本号是3,如果在另外一个系统中ID为5的数据版本号不是3的话,那么就意味着需要进行复制了。

    CHANGETABLE(VERSION)函数与CHANGETABLE(CHANGES)函数最大的区别就在于它可以传入表的主键值,因此可以根据用户的需求了解每一行数据的最新版本号,而CHANGETABLE(CHANGES)则是通过某个版本号来获得自从这个版本号之后的更新信息。因此两个函数可以用于两种不同的复制拓扑,前者更加适用于双向复制,而后者则适用于单向复制。

    测试脚本

    下面给出完整的测试脚本:

    /*************************************************************
    **************************************************************
    Section 1: How to enable the change tracking
    **************************************************************
    **************************************************************/

    --Create test database and enable the change tracking feature of the database
    USE master
    GO
    CREATE DATABASE TestCT
    GO
    ALTER DATABASE TestCT
    SET Change_Tracking = ON
    (CHANGE_RETENTION = 2 MINUTES, AUTO_CLEANUP = ON)
    GO

    --Create test table and enable the change tracking feature on the table
    USE TestCT
    GO
    CREATE TABLE TestCT
    (
        ID int PRIMARY KEY,
        Name varchar(50),
        Description varchar(200)
    )
    GO
    ALTER TABLE TestCT
    ENABLE CHANGE_TRACKING
    WITH (TRACK_COLUMNS_UPDATED = ON)
    GO
    --Use sys.change_tracking_tables to get the table list which enabled change tracking
    SELECT OBJECT_NAME(object_id) AS table_name, sys.change_tracking_tables.* FROM sys.change_tracking_tables
    GO

    /*************************************************************
    **************************************************************
    Section 2: How to get change tracking information
    **************************************************************
    **************************************************************/

    --The current version is 0 now.
    SELECT CHANGE_TRACKING_CURRENT_VERSION() AS current_version
    --Insert two rows in two transactions
    INSERT INTO TestCT VALUES (1, 'ABC', NULL)
    INSERT INTO TestCT VALUES (2, 'XYZ', NULL)
    --The current version is 2 now.
    SELECT CHANGE_TRACKING_CURRENT_VERSION() AS current_version
    --Get the changed table version with table function 'CHANGETABLE'.
    --You will see two rows with different SYS_CHANGE_VERSION and SYS_CHANGE_CREATION_VERSION.
    SELECT * FROM CHANGETABLE(CHANGES TestCT, 0) CT  

    --INSERT two rows in one transaction
    BEGIN TRAN
        INSERT INTO TestCT VALUES (3, 'CBA', NULL)
        INSERT INTO TestCT VALUES (4, 'ZYX', NULL)
    COMMIT
    --The current version is 3 now.
    SELECT CHANGE_TRACKING_CURRENT_VERSION() AS current_version
    --Get the changed table version with table function 'CHANGETABLE'.
    --You will see two rows with same SYS_CHANGE_VERSION and SYS_CHANGE_CREATION_VERSION.
    --We use 2 as the last_sync_version value here to see net changes of after the last time insertion.
    SELECT * FROM CHANGETABLE(CHANGES TestCT, 2) CT

    --DML update which not affected the primary key
    UPDATE TestCT SET Name = 'abc' WHERE ID = 1
    --The current version is 4 now.
    SELECT CHANGE_TRACKING_CURRENT_VERSION() AS current_version
    --Get the changed table version with table function 'CHANGETABLE'.
    --With different last_sync_version, you will see the different result
    SELECT * FROM CHANGETABLE(CHANGES TestCT, 0) CT  --the SYS_CHANGE_OPERATION is I now
    SELECT * FROM CHANGETABLE(CHANGES TestCT, 3) CT  --the SYS_CHANGE_OPERATION is U now
    --With the CHANGE_TRACKING_IS_COLUMN_IN_MASK function, you will see which row changed.
    SELECT CHANGE_TRACKING_IS_COLUMN_IN_MASK
        (COLUMNPROPERTY(OBJECT_ID('TestCT'), 'Name', 'ColumnId'), SYS_CHANGE_COLUMNS)
        AS is_column_Name_changed,
        CHANGE_TRACKING_IS_COLUMN_IN_MASK
        (COLUMNPROPERTY(OBJECT_ID('TestCT'), 'Description', 'ColumnId'), SYS_CHANGE_COLUMNS)
        AS is_column_Description_changed
        FROM CHANGETABLE(CHANGES TestCT, 3) CT

    --DML update which affected the primary key
    UPDATE TestCT SET ID = 5 WHERE ID = 1
    --The current version is 5 now.
    SELECT CHANGE_TRACKING_CURRENT_VERSION() AS current_version
    --Get the changed table version with table function 'CHANGETABLE'
    --you will see one record indicate the deletion of origional record
    --and one record indicate the insertion of new record
    SELECT * FROM CHANGETABLE(CHANGES TestCT, 4) CT

    --DML delete
    DELETE TestCT WHERE ID = 5
    --The current version is 6 now.
    SELECT CHANGE_TRACKING_CURRENT_VERSION() AS current_version
    --Get the changed table version with table function 'CHANGETABLE'
    --you will see one record indicate the deletion of origional record
    SELECT * FROM CHANGETABLE(CHANGES TestCT, 5) CT

    --Another usefull usage of CHANGETABLE, which can help you
    --to determine the current version of a specific row
    SELECT * FROM CHANGETABLE(VERSION TestCT, (ID), (4)) CT

    --Another usefull statement which can help you distinguish the
    --change context
    DECLARE @context AS varbinary(128);
    SET @context = CAST('Test_Change_Tracking' AS varbinary(128));
    WITH CHANGE_TRACKING_CONTEXT (@context)
        UPDATE TestCT SET Description = 'NA';
    -- The change now has an associated change context
    SELECT CT.SYS_CHANGE_VERSION, CT.SYS_CHANGE_OPERATION,
        CAST(SYS_CHANGE_CONTEXT AS varchar(20)) AS change_context
        FROM CHANGETABLE(CHANGES TestCT, 6) AS CT

    --Another usefull function which can help you determine the requirement
    --of re-initialization.
    --Before the truncation or periodical cleanup, the min_valid_version is 0 now.
    SELECT CHANGE_TRACKING_MIN_VALID_VERSION(OBJECT_ID('TestCT')) AS min_valid_version;
    --After the truncation or periodical cleanup, the min_valid_version is 7 now.
    TRUNCATE TABLE TestCT;
    SELECT CHANGE_TRACKING_MIN_VALID_VERSION(OBJECT_ID('TestCT')) AS min_valid_version;
    --So the insert statement will use 8 as the SYS_CHANGE_VERSION
    INSERT INTO TestCT VALUES (1, 'ABC', NULL)
    --So the last_sync_version parameter will not affect the result now
    SELECT * FROM CHANGETABLE(CHANGES TestCT, 0) CT
    SELECT * FROM CHANGETABLE(CHANGES TestCT, 7) CT

    /*************************************************************
    **************************************************************
    Section 3: DDL affection to the change tracking
    **************************************************************
    **************************************************************/

    --After the tables schema changed, the new column will also be monitored
    ALTER TABLE TestCT ADD Age int;
    UPDATE TestCT SET Age = 10;
    SELECT CHANGE_TRACKING_IS_COLUMN_IN_MASK
        (COLUMNPROPERTY(OBJECT_ID('TestCT'), 'Name', 'ColumnId'), SYS_CHANGE_COLUMNS)
        AS is_column_Name_changed,
        CHANGE_TRACKING_IS_COLUMN_IN_MASK
        (COLUMNPROPERTY(OBJECT_ID('TestCT'), 'Description', 'ColumnId'), SYS_CHANGE_COLUMNS)
        AS is_column_Description_changed,
        CHANGE_TRACKING_IS_COLUMN_IN_MASK
        (COLUMNPROPERTY(OBJECT_ID('TestCT'), 'Age', 'ColumnId'), SYS_CHANGE_COLUMNS)
        AS is_column_Age_changed
        FROM CHANGETABLE(CHANGES TestCT, 8) CT;

    --You will find the schema modification will not be tracked.
    SELECT CHANGE_TRACKING_CURRENT_VERSION() AS current_version
    ALTER TABLE TestCT DROP COLUMN Age
    SELECT CHANGE_TRACKING_CURRENT_VERSION() AS current_version

    January 26

    SQL Server 2008对日期时间类型的改进

    微软在备受多年的争议后,终于对日期时间数据类型开刀了,在新版的SQL Server 2008中一口气增加了4种新的日期时间数据类型,包括:

    • Date:一个纯的日期数据类型。
    • Time:一个纯的时间数据类型。
    • DateTime2:新的日期时间类型,将精度提到到了100纳秒。
    • DateTimeOffset:新的日期时间类型,在DateTime2的基础上增加了时区部分。

    下面是在SQL Server 2008中日期时间数据类型的一个简单汇总表:

    数据类型 格式 取值范围 精度 存储尺寸
    date yyyy-mm-dd 0001-1-1
    9999-12-31
    1天 3字节
    time hh:mm:ss.nnnnnn 0:0:0.000000
    23:59:59.999999
    100纳秒 3-5字节
    smalldatetime yyyy-mm-dd
    hh:mm:ss
    1900-1-1
    2079-6-6
    1分钟 4字节
    datetime yyyy-mm-dd
    hh:mm:ss:nnn
    1753-1-1
    9999-12-31
    0.00333秒 8字节
    datetime2 yyyy-mm-dd
    hh:mm:ss:nnnnnn
    0001-1-1
    9999-12-31
    100纳秒 6-8字节
    datetimeoffset yyyy-mm-dd
    hh:mm:ss:nnnnnn
    +|- hh:mm
    0001-1-1
    9999-12-31
    (全球标准时间)
    100纳秒 8-10字节

    为了使用这些数据类型,SQL Server 2008同时还引入了一系列的T-SQL函数。

    三个用于获得高精度系统时间的函数(因为是这三个函数都是取的操作系统时间,所以精度仅能达到10毫秒):

    • SYSDATETIME:返回运行SQL Server实例的服务器的本地时间,数据类型是datetime2(7),不包含时区信息。
    • SYSDATETIMEOFFSET:返回运行SQL Server实例的服务器的本地时间及时区信息,数据类型是datetimeoffset(7)。
    • SYSUTCDATETIME:返回运行SQL Server实例的服务器的标准世界时间,数据类型是datetime2(7)。

    用于时区转换的函数:

    • SWITCHOFFSET(datetimeoffset, time_zone):根据输入的世界时间以及时区信息返回某个特定时区的数据,例如SWITCHOFFSET('2008-1-1 0:0:0 + 8:00', '-07:00')返回值将是'2007-12-31 9:00 -07:00',这样我们就晓得我们元旦的时候老美的时间只是早上9:00。(有个有趣的情况是SWITCHOFFSET函数time_zone参数小时的前导0时不能省略的,就我们刚才用的那个例子如果time_zone参数写成'7:00'就会报错,必须写成'07:00',不过datetimeoffset数据里那个时区部分小时的前导0时可以省略的,也就是说'2008-1-1 0:0:0 + 8:00'和'2008-1-1 0:0:0 + 08:00'都是可以接受的,对于时区中分钟部分也是如此。不过建议大家养成良好的编码习惯,所有前导0都不要省略。)
    • TODATETIMEOFFSET(datetime, offset):根据输入的日期时间参数值和时区参数值返回一个世界时间值。例如TODATETIMEOFFSET('2008-1-1 0:0:0', '+08:00')返回值是'2008-1-1 0:0:0 + 08:00'。

    顺便列举一下SQL Server 2005中已经提供的日期时间函数,不过就不做介绍了:

    • 用于获取系统时间的函数:CURRENT_TIMESTAMP,GETDATE,GETUTCDATE
    • 返回日期时间的指定部分:DATENAME,DATEPART,DAY,MONTH,YEAR
    • 计算日期时间差异的函数:DATEDIFF, DATEADD (必须注意的是datetime、smalldatetime数据类型支持+和-运算符,但是对于date、time、datetime2、datetimeoffset则不支持。)
    • 对日期时间进行计算的函数:DATEADD
    • 设置日期时间显示格式的函数:@@DATEFIRST,SET DATEFIRST,SET DATEFORMAT,@@LANGUAGE,SET LANGUAGE,sp_helplanguage
    • 用于确认日期时间数据格式的函数:ISDATE
    January 22

    SQL Server 2008 CDC对存储空间的影响

    前面有一个帖子出了几张图用来说明CDC对数据库IO的影响,接着俺又接着对CDC产生的存储空间的影响进行研究。

    首先CDC功能对数据库存储空间产生显著影响的两张表是cdc.<capture_instance>_CT表和cdc.lsn_time_mapping表,这里简称为表1和表2。下面是对表1和表2作的一些较为深入地剖析:

    • 表1和表2的数据
      • 表1主要由3个binary(10)字段、1个int字段、1个varbinary(128)字段以及所有被选定更新跟踪的原始表字段构成。因此表1每行数据的尺寸大概是在30 + 4 + 5 (因为通常一张表需要监控的字段会在16个以内,所以暂定为2bytes的binary然后加上varbinary数据2个bytes的固定开销),也就是 39 + x(假定原始表需要监控的字段键总尺寸为x个字节)个字节。
      • 表2则有1个binary(10)字段、2个datetime字段和1个varbinary(10)字段构成。因此表2每行数据应该是20 + 16 + 12 = 48个字节。
    • 表1和表2的索引(这个不太好估算,因为不同的表聚簇索引的键值密度是不一样的,一般按照1/4的数据尺寸估算,只有多没有少啦)
      • 表1的3个binary(10)字段构成了聚簇索引,同时3个binary(10)字段加上原始数据表的聚簇索引构成一个非聚簇索引,同上面一样,我们假定原始表聚簇索引键是x个字节,那么表1的非聚簇索引每行是(30 + y(假定原始表聚簇索引键尺寸为y个字节) + 4(指向聚簇索引的内部指针))个字节。
      • 而表2中的binary(10)字段构成了聚簇索引,其中1个datetime字段构成了非聚簇索引。因此表2的非聚簇索引每行是8 + 4 = 12个字节。
    • 对原始数据表的一行数据进行UPDATE操作,会在表1中添加2行数据,而DELETE操作和INSERT操作则会增加1行数据;而对于表2则是每笔事务增加1行数据。

    因此我们作如下假定:

    • 典型的OLTP环境:
      • 原始数据表的聚簇索引为1个整型字段,同时需要监控的字段总尺寸为50字节(约为5个decimal(19)或5个char(10))
      • 对原始表提交100,000个事务
      • 产生1,000,000行次数据操作,其中UPDATE占60%,INSERT和DELETE占40%
      • 那么最终CDC产生的额外数据存储空间应该为(39 + 4 + 50) * (1000000 * 1.2 + 1000000 * 0.4) + 48 * 100000 = 153,600,000个字节,约为164MB(假定数据页填充率为90%)。
      • CDC产生的额外的非聚簇索引存储空间按照数据尺寸的1/4估算,大概是40MB左右。
      • 因此约合200MB左右。
    • 典型的OLAP环境(貌似OLAP环境不应该使用CDC了吧,所以就不算了)

    经过这样的对比我们可以知道,CDC在生产环境特别是OLTP环境对存储空间的影响不算太明显的,当然这个还要取决于DBA在原始数据表上选取多少字段进行监控,以及这些字段的数据尺寸,同时还有原始数据表的聚簇索引键值密度。

    最后说明这里只是我长夜漫漫无心睡眠随便算算的,生产环境还是要仔细套用上面的公式计算一下的。另外需要说明的是表1和表2都是由一个异步的进程通过读取日志来完成的,因此表1和表2的数据刷新和原始数据表的刷新会有一定的延时。

    SQL Server 2008 CDC IO测试

    前两天测试了一下SQL Server 2008的CDC功能,今天又花了一些时间对CDC功能对IO的影响进行了一些评估。

    整个评估的思路是这样的:

    1. 创建两个数据库
    2. 在两个数据库中分别创建一张结构完全相同的表,一个数据库启用CDC功能,而另外一个禁用CDC功能
    3. 向两张表中写入相同行数的数据
    4. 利用动态管理视图sys.dm_io_virtual_file_stats来获得两个数据库文件上的IO
    5. 利用sysindexes来获得两个数据库中数据表的存储消耗情况

    因为是在虚拟机中进行的测试,所以选取了比较小的数据表(AdventureWorks数据库中的SalesOrderDetails),大约有12万行数据。

    评估的结果如下:

    Number of Bytes Read

    Number of Read

    从上面两张表中可以看到,CDC激活后日志文件的读会显著增加。原因是CDC在写更新跟踪表时,需要读取日志。

    Number of Bytes Written

    Number of Write

    从上面两张表中可以看到激活CDC后数据文件的写入和日志文件写入都会显著增加,不过需要考虑到CDC激活后会需要多写一张表,在本例中就是dbo_SalesOrderDetails_CT,所以这种增加是可以理解的。当然在生产环境中并不会对数据表的所有列进行CDC监控,所以激活CDC对IO写入的影响还需要针对不同情况进行分析。

     

    从上面这张图可以看出,CDC激活后会生产数据表不会消耗更多的存储空间,但是更新跟踪表会需要俄外的存储空间。另外可以发现的一点是,在本例中dbo_SalesOrderDetail_CT表消耗的空间比SalesOrderDetail表多,这是因为在dbo_SalesOrderDetail_CT表中加入了一些额外的字段,例如_$start_lsn和_$end_lsn,同时注意观察dbo_SalesOrderDetail_CT表会发现,SQL Server在这张表上使用_$start_lsn、_$end_lsn和_$seqval三个字段作为聚簇索引,而SalesOrderDetail表上原来的聚簇索引(SalesOrderID,SalesOrderDetailID)再加上_$start_lsn、_$end_lsn和_$seqval三个字段则被创建为一个非聚簇索引,所以这就导致了dbo_SalesOrderDetail_CT表需要消耗比原始表更多的空间,不过原始数据表上的非聚簇索引不会在CDC跟踪表上被创建,这也就说明了原始数据表聚簇索引的大小也会对CDC引发的IO产生影响。

    经过以上测试,我们可以发现以下情况:

    • CDC激活会显著增加日志文件的读操作
    • CDC激活后更新跟踪表会产生额外的写入,并消耗存储空间
    • CDC激活后,原数据表的聚簇索引尺寸会影响到CDC产生的IO数据量,而原始数据表上的非聚簇索引则不会

    因此建议CDC激活的环境下,应该将更新跟踪表写入与原始表不同的文件组并存放在不同的存储设备上,同时应该注意为日志文件选取可提高读取性能的存储硬件上,比如RAID10。

    最后,附上是用于测试的脚本:

    --Step 1: Test IO under CDC enabled enviroment
    USE master
    PRINT getdate()
    PRINT 'Create test database'
    GO
    CREATE DATABASE TestIO_CDC
    GO

    PRINT getdate()
    PRINT 'Enable CDC feature on the test database'
    GO
    USE TestIO_CDC
    EXEC sp_cdc_enable_db_change_data_capture;
    GO
    PRINT getdate()
    PRINT 'Create test table'
    GO
    CREATE TABLE dbo.SalesOrderDetail(
        SalesOrderID int NOT NULL,
        SalesOrderDetailID int NOT NULL,
        CarrierTrackingNumber nvarchar(25) NULL,
        OrderQty smallint NOT NULL,
        ProductID int NOT NULL,
        SpecialOfferID int NOT NULL,
        UnitPrice money NOT NULL,
        UnitPriceDiscount money NOT NULL,
        rowguid uniqueidentifier ROWGUIDCOL  NOT NULL,
        ModifiedDate datetime NOT NULL,
    CONSTRAINT PK_SalesOrderDetail PRIMARY KEY CLUSTERED
        (SalesOrderID ASC, SalesOrderDetailID ASC)
    )
    GO
    PRINT getdate()
    PRINT 'Enable CDC feature on the test table'
    GO
    EXEC sp_cdc_enable_table_change_data_capture 'dbo', 'SalesOrderDetail', @role_name= NULL,
        @supports_net_changes = 1;
    GO
    PRINT getdate()
    PRINT 'Start inserting data'
    GO
    INSERT INTO dbo.SalesOrderDetail
        (SalesOrderID, SalesOrderDetailID, CarrierTrackingNumber,
        OrderQty, ProductID, SpecialOfferID, UnitPrice, UnitPriceDiscount,
        rowguid, ModifiedDate)
        SELECT SalesOrderID, SalesOrderDetailID, CarrierTrackingNumber,
        OrderQty, ProductID, SpecialOfferID, UnitPrice, UnitPriceDiscount,
        rowguid, ModifiedDate FROM AdventureWorks.Sales.SalesOrderDetail
    GO

    --Step 2: Test IO under CDC disabled enviroment
    PRINT getdate()
    PRINT 'Create test database'
    GO
    USE master
    CREATE DATABASE TestIO_Non_CDC
    GO
    USE TestIO_Non_CDC
    GO
    PRINT getdate()
    PRINT 'Create test table'
    GO
    CREATE TABLE dbo.SalesOrderDetail(
        SalesOrderID int NOT NULL,
        SalesOrderDetailID int NOT NULL,
        CarrierTrackingNumber nvarchar(25) NULL,
        OrderQty smallint NOT NULL,
        ProductID int NOT NULL,
        SpecialOfferID int NOT NULL,
        UnitPrice money NOT NULL,
        UnitPriceDiscount money NOT NULL,
        rowguid uniqueidentifier ROWGUIDCOL  NOT NULL,
        ModifiedDate datetime NOT NULL,
    CONSTRAINT PK_SalesOrderDetail PRIMARY KEY CLUSTERED
        (SalesOrderID ASC, SalesOrderDetailID ASC)
    )
    GO
    PRINT getdate()
    PRINT 'Start inserting table'
    GO
    INSERT INTO dbo.SalesOrderDetail
        (SalesOrderID, SalesOrderDetailID, CarrierTrackingNumber,
        OrderQty, ProductID, SpecialOfferID, UnitPrice, UnitPriceDiscount,
        rowguid, ModifiedDate)
        SELECT SalesOrderID, SalesOrderDetailID, CarrierTrackingNumber,
        OrderQty, ProductID, SpecialOfferID, UnitPrice, UnitPriceDiscount,
        rowguid, ModifiedDate FROM AdventureWorks.Sales.SalesOrderDetail
    GO

    PRINT getdate()
    PRINT 'Wait 30 seconds for IO finished'
    GO
    WAITFOR DELAY '0:0:30'
    GO
    PRINT getdate()
    PRINT 'Start getting the IO statistics data'
    GO
    USE master
    GO
    --IO Statistics
    SELECT DB_NAME(database_id) AS Database_Name,
        FILE_NAME(file_id) AS File_Name, num_of_reads, num_of_bytes_read,
        num_of_writes, num_of_bytes_written
        FROM sys.dm_io_virtual_file_stats(DB_ID(N'TestIO_CDC'), NULL)
    UNION
    SELECT DB_NAME(database_id) AS Database_Name,
        FILE_NAME(file_id) AS File_Name, num_of_reads, num_of_bytes_read,
        num_of_writes, num_of_bytes_written
        FROM sys.dm_io_virtual_file_stats(DB_ID(N'TestIO_Non_CDC'), NULL);

    --Storage Concumed
    USE TestIO_CDC
    GO
    SELECT 'Enabled' AS 'CDC Feature', OBJECT_NAME(id) AS TableName, dpages * 8 AS 'Data (KB)', reserved * 8 AS 'Reserved (KB)'
        FROM TestIO_CDC.sys.sysindexes
        WHERE id IN(OBJECT_ID('dbo.SalesOrderDetail'), OBJECT_ID('cdc.dbo_SalesOrderDetail_CT'))
            AND indid = 1
    GO
    USE TestIO_Non_CDC
    GO
    SELECT 'Disabled' AS 'CDC Feature', OBJECT_NAME(id) AS TableName, dpages * 8 AS 'Data (KB)', reserved * 8 AS 'Reserved (KB)'
        FROM TestIO_Non_CDC.sys.sysindexes
        WHERE id IN(OBJECT_ID('dbo.SalesOrderDetail'))
    GO

    SQL Server 2008数据库引擎中几个重要的Discontinued Feature

    刚才在测试SQL Server 2008的时候突然发现BACKUP LOG WITH TRUNCATE_ONLY命令提交后说语法错误,遂苦阅联机丛书,最后发现原来是被Discontinue了,这才想起貌似在SQL Server 2005中已经被Deprecated,到了2008果然被干掉了。于是索性全览了一遍被丢弃的功能,想想三年前的2005发布,决定总结一下。

    原功能 替代品 上榜理由
    BACKUP LOG WITH TRUNCATE_ONLY None 过去习惯性在收缩数据库脚本前加上这个命令,SQL Server 2008中不在支持这一命令,如果需要截断日志,只能通过将数据库设置为Simple还原模式才可以。当然BACKUP LOG WITH NO_LOG也随之淘汰了。
    sp_helpdevice sys.backup_devices 这个存储过程在自动化备份管理脚本中被使用地有为频繁。现在无奈全面推行内部视图,看来这些通过系统存储过程来获取系统元数据的日子是越来越少了。不过这样确实不错,架构上看起来更加一致么。
    60,65和70的数据库兼容级别 None 在创建数据库的脚本中尤其需要注意这一点
    Surface Area Configuration Tool Declarative Management Framework和SQLCM 这个工具是在SQL Server 2005引入的,曾经在SQL Server 2005发布后被不断提倡,奈何红颜薄命阿......

    不过相比之下,我们回顾一下SQL Server 2005被丢弃的功能,SQL Server 2008的改动相对还是小很多了,毕竟才3年而已么:

    原功能 替代品 上榜理由
    isql sqlcmd 原来还是有很多在Command Line模式下的维护脚本是用isql命令写的。
    sp_config 'allow update' None 直接对系统表的修改被禁止了。这种被等同于修改操作系统注册表的行为被禁止后,系统当然是会安全很多了。
    DBCC PINTABLE None 这是颇具争议的一个丢弃,尽管很多人认为Pin Table到内存里面有助于系统性能的提高,不过微软则坚持认为大家应该把对象的重要性交给内部的缓存管理器去评估。
    Rebuildm.exe 安装程序中的REDUILDDATABASE选项 虽然没有带来什么不便,不过这个命令确实拯救过无数DBA的性命。
    syslocks sys.dm_tran_locks 原来有很多人是依赖这个系统表来了解内部的锁压力的。不过用动态管理视图确实没有什么不方便,而且确实SQL Server的管理需要一套一致的机制。
    *=和=* LEFT JOIN和RIGHT JOIN 好像很多老DBA都喜欢这么写,尽管大家都知道后面的那种才是ANSI SQL的格式。
    通讯协议NWLink IPX/SPX, AppleTalk, Banyan Vines, Multiprotocol 仅能在TCP/IP sockets、命名管道、VIA以及共享内存中选择 料想选择前者通讯协议的人应该也不多了,不过倒也证实了TCP/IP对网络世界的影响,这些曾经还出现在NT年代的MCSE考试中的协议终于退出了SQL Server的世界。

    这里只挑了一些个人认为比较重要的。注意:以下观点仅代表个人观点,欢迎大家推荐你认为同样重要的被丢弃功能。

    January 21

    利用MERGE刷新库存快照

    SQL Server 2008中有一个新的语法叫做MERGE,这种语法可以融合UPDATE、DELETE和INSERT。特别适合于将交易型的记录集合并到快照性的结果集中去。非常具有代表性的应用场景就是库存管理,库存管理应用(俗称进销存)中经常需要获得某个时间点上的库存,也称为库存结余。

    讨论一下一些基础的东西,在数据库建模中有两种基本模型:

    1. 纯交易事务型:这种方法主要是在一张交易表中记录下所有商品的进出仓记录,然后根据所有的进出仓详细记录来计算得出库存结余。这种方法的优势就在于它保存了所有的交易明细记录,所以理论上面可以计算出过往任何一个时间点上的结余库存(只能说是理论上的,因为通常情况结余库存的计算不一定仅仅按照SKU,还可能根据品类、部门等其他因素,而这些信息一般都存放在基础信息表中。因此如果要追溯以往的库存结余,有可能需要这些基础信息表也能够追溯历史,其实这也就是我们经常在数据仓库中提到的维度变化问题。)。这种方法的缺点在于如果要获得库存结余则需要进行大量的聚合工作,所以会在库存结余查询的性能方面面临挑战,特别是需要查询实时的库存结余时(尽管这种需求在库存管理应用中并不多见)。
    2. 纯时点快照型:直接创建一张库存结余表,每次有进出仓操作的时候,直接更新库存结余表,因此这张库存结余表里面的数据实际上就是当前的库存结余。这种方法的优势非常明显,就是查询当前库存的速度会非常快。当然这种方法的缺点也非常明显——由于这种方法丢弃了交易明细记录,因此要想追溯历史库存的话比较麻烦。(当然也是有办法的,数据仓库中有一种方法就是定期快照,也就是每隔一段时间存档这个时间点上的库存,当然这种方法需要平衡两个因素,一是对存储空间的消耗,二是快照的连续性,存档越频繁快照就越连续,存储空间就会消耗的越厉害。通常这种频率是由企业对数据分析的需求决定的,根据库存商品的流动特性一般会在一小时到一周不等。)这种方法最致命的缺定还不在这儿,最为困扰DBA的问题应该是这种模型对应用并发能力的影响。由于所有进出仓操作都需要更新库存结余表,所以库存结余表会成为数据应用的逻辑瓶颈。

    因此一般我们都会用混合模型,为了保证历史的可追溯,进出仓的事务明细是一定要保留的(至少在一段时间内),而为了满足对库存结余查询的及时性和性能则需要维护一张快照表,并且保证定期更新这张快照表。为了保证性能并满足应用的逻辑弹性,这些混和模型的设计是相当重要的,也就是如何更新快照表,不过在这里我们就不多说了。

    在评估SQL Server 2008的过程中,我把SQL Server 2008联机丛书里面的代码给改了一下,一是更加贴和应用的实际情况,二是联机丛书的MERGE范例只合并了UPDATE和DELETE,我多加了一个INSERT的情况。:)

    在这里和大家共享一下修改后的代码:

    --Step 1: Create test table
    USE tempdb
    GO

    CREATE TABLE Inventory_Snapshot
    (
        ProductID    int PRIMARY KEY NOT NULL,
        Quantity    int NOT NULL
    )
    GO

    CREATE TABLE Inventory_Operation
    (
        OperationID        int PRIMARY KEY    IDENTITY(1,1),
        OperationDate    datetime,
        OperationType    int,    --1:Move in; 2:Move out; 3:Adjustment
        ProductID        int,
        Quantity        int
    )
    GO

    --Step 2: Create stored procedure for new inventory operation
    CREATE PROCEDURE usp_Inventory_Operation
        @productID int,
        @operationDate datetime,
        @operationType int, --1:Move in; 2:Move out; 3:Adjustment
        @quantity    int
    AS
    INSERT INTO Inventory_Operation
        (OperationDate, OperationType, ProductID, Quantity)
    VALUES
        (@operationDate, @operationType, @productID, @quantity)
    GO

    --Step 3: Create stored procedure for inventory snapshot calculation
    CREATE PROCEDURE usp_Inventory_Snapshot_Process
        @processDate datetime
    AS
    MERGE Inventory_Snapshot AS invs
    USING (SELECT ProductID, Sum(ABSQuantity) AS SubTotal
            FROM (SELECT ProductID, Quantity  *
                        CASE OperationType --1:Move in; 2:Move out; 3:Adjustment
                            WHEN 1 THEN 1
                            WHEN 2 THEN -1
                            WHEN 3 THEN 1
                            ELSE 0
                        END AS ABSQuantity FROM Inventory_Operation
                    WHERE OperationDate = @processDate) AggInvo       
            GROUP BY AggInvo.ProductID)
        AS invo(ProductID, SubTotal)
    ON (invs.ProductID = invo.ProductID)
    WHEN MATCHED AND invs.Quantity <> invo.SubTotal AND invs.Quantity <> invo.SubTotal * -1
        THEN UPDATE SET invs.Quantity = invs.Quantity + invo.SubTotal
    WHEN MATCHED AND invs.Quantity = invo.SubTotal * -1
        THEN DELETE
    WHEN TARGET NOT MATCHED
        THEN INSERT VALUES (invo.ProductID, invo.SubTotal);
    GO

    DELETE FROM dbo.Inventory_Snapshot
    --Step 4: Test application logic
    --2007-1-1
    EXEC usp_Inventory_Operation 1000, '2007-1-1', 1, 500
    EXEC usp_Inventory_Operation 1001, '2007-1-1', 1, 300
    EXEC usp_Inventory_Operation 1002, '2007-1-1', 1, 250

    EXEC usp_Inventory_Snapshot_Process '2007-1-1'

    SELECT * FROM Inventory_Snapshot
    GO

    EXEC usp_Inventory_Operation 1001, '2007-1-2', 2, 200
    EXEC usp_Inventory_Operation 1003, '2007-1-2', 1, 300
    EXEC usp_Inventory_Operation 1000, '2007-1-2', 2, 200

    EXEC usp_Inventory_Snapshot_Process '2007-1-2'

    SELECT * FROM Inventory_Snapshot
    GO

    EXEC usp_Inventory_Operation 1000, '2007-1-3', 2, 200
    EXEC usp_Inventory_Operation 1002, '2007-1-3', 2, 250
    EXEC usp_Inventory_Operation 1004, '2007-1-3', 2, 300

    EXEC usp_Inventory_Snapshot_Process '2007-1-3'

    SELECT * FROM Inventory_Snapshot
    GO

    最后需要说明的是,通常DBA或者开发员都会认为交易型记录一旦写入数据库后就不会修改,但实际上不是的。我就在一家大型物流公司中见过某些库存操作会找一条记录直接更新(实际上是锁仓操作,这种设计的初衷可能是考虑到锁仓操作非常频繁的缘故吧,不管怎么样,直接更新交易型记录的情况确实存在)。

    就像上面的范例代码一样,如果我们对2007年1月2日的进出仓明细记录执行过了usp_Inventory_Snapshot_Process处理后2007年1月2日的交易记录又被修改了,这个时候怎么办呢?......这或许就是数据库架构设计迷人之处吧......

    January 19

    SQL Server 2008 CDC尝鲜记

    SQL Server 2008有一项新功能叫做“数据更新捕获”(简称CDC)。这项功能主要是为了帮助用户捕获SQL Server中的数据更新操作。CDC功能对于在数据仓库中进行ETL时评估那些数据需要增量抽取这一工作有极大的帮助,我们将可以设计自己的增量ETL方案,而不在需要使用触发器、HASH列或者SSIS中那个慢悠悠的SCD处理模块了。(SCD处理模块实质上应该也采用创建HASH列来计算那些新数据行和更新数据行的。)

    下面是用于评估SQL Server 2008 CDC的一段脚本

    1. 首先创建一个测试数据库
      USE master
      GO
      CREATE DATABASE TestCDC
      GO
      这样我们就得到了一个空白的测试数据库
    2. 然后激活TestCDC数据库上的更新捕获功能
      USE TestCDC
      GO
      EXEC sp_cdc_enable_db_change_data_capture;
      GO
      执行了存储过程sp_cdc_enable_db_change_data_capture后,就会在数据库TestCDC中看到有一些新的表被创建了,分别是ddl_historychange_tablescaptured_columnsindex_columnslsn_time_mapping,并且这5张表都是在cdc架构下。
    3. 然后在TestCDC数据库中创建测试表
      USE TestCDC
      GO
      CREATE TABLE dbo.Product (
                ProductID int PRIMARY KEY NOT NULL,
                ProductName nvarchar(100),
                Category nvarchar(50))
      GO
    4. dbo.Product表上激活更新跟踪
      EXEC sp_cdc_enable_table_change_data_capture 'dbo', 'Product', @role_name= NULL, @supports_net_changes =1;
      成功提交上述命令后,就可以在数据表change_tablescaptured_columnsindex_columns表中看到相应的记录,其中change_table中一条,capture_column中三条,index_columns中一条。同时cdc架构下有增加了一张新表叫做dbo_Product_CT,这张表的结构和Product表的结构有点相似,Product表中的三列在dbo_Product_CT中都有,同时dbo_Product_CT表中还增加了_$start_lsn,_$end_lsn,_$seqval,_$operation和_$update_mask五个新的字段。
      其实在存储过程sp_cdc_enable_table_change_data_capture中有一系列的参数,在这里我们为了简化忽略了一个参数就是@captured_column_list,这个参数可以对表中特定的某些字段启用更新跟踪。
    5. 在Product表上提交INSERT语句
      INSERT INTO dbo.Product VALUES (1, N'ABC', N'A');
      提交完了这条命令后,就会在lsn_time_mappingdbo_Product_CT中分别看到一条新记录。
      其中dbo_Product_CT表中的_$operation字段的值是2,_$update_mask字段的值是0x07。
    6. _$operation字段是代表DML操作类型,1是delete,2是insert,3是update的旧值,4是update的新值。
      _$update_mask字段是表示一个字段列表的掩码,那些在DML操作中被更新了的字段位为1,而没有更新的字段位为0。在本例中Product表一共有三列被跟踪,所以应该是一个三位的二进制数,右边低位第一位是第一列ProductID,低位第二位是第二列ProductName,第三位就是Category了。因为这是一次INSERT,所以更新涉及到了所有的三列,所以_$update_mask字段就应该是0x7了。
    7. 继续在Product表上提交UPDATE语句
      UPDATE dbo.Product SET Category = N'B' WHERE ProductID = 1;
      提交完这条命令后,当然也会在lsn_time_mapping和dbo_Product_CT中看到新记录了。不过这次lsn_time_mapping中是一条,而dbo_Product_CT中则是两条。(为什么会这样呢?建议大家自己试一下咯,一试就明白了。)
      其中dbo_Product_CT表中的_$operation字段的值是第一条是3,第二条是4,_$update_mask字段的值两条都是0x04。
      在这次操作中我们更新的是第三列,所以_$update_mask字段就应该是0x4了。(如果我们更新的是ProductID会发现_$update_mask并非是0x1,而同样是0x7,这估计是因为ProductID是主键,更新主键应该视同一条新的记录。)
    8. 再来一次UPDATE
      UPDATE dbo.Product SET Category = N'A' WHERE ProductID = 1;
      提交完这条命令后,在dbo_Product_CT中又看到两条新记录了。其中dbo_Product_CT表中的_$operation字段的值是第一条是3,第二条是4,_$update_mask字段的值两条都是0x04。(看来CDC确实会记录下数据的每次修改。)
    9. 继续在Product表上提交DML语句
      DELETE dbo.Product WHERE ProductID = 1;
      提交完了这条命令后,就会在lsn_time_mappingdbo_Product_CT中分别看到一条新记录。
      其中dbo_Product_CT表中的_$operation字段的值是1,_$update_mask字段的值是0x07。
    10. 提交一个DDL试试看
      ALTER TABLE dbo.Product ADD Description nvarchar(100);
      提交完这句命令后,只会在ddl_history表中看到一条新的记录。
    11. 然后再试试DML
      UPDATE dbo.Product SET Description = N'NA';
      提交完这句语句后,所有cdc架构下的表中都没有看到新记录。说明新增的列Description不跟踪更新了......估计有人会说(细心的人哦!):“这次当然看不到新记录了,因为在前面第7步我们已经删除了所有的记录,因此这次的UPDATE语句没有影响到任何记录,当然CDC的表中不会有任何记录了。”那么到底对Description更新会不会记录呢,经过测试确实是不记录的。
      那么如果我们想对Description也进行更新跟踪应该怎么办呢?很简单的,由另外一个存储过程叫做sp_cdc_disable_table_change_data_capture可以禁用对某张表的更新跟踪,可以使用这个存储过程先对Product表禁用更新跟踪,然后再重新启用对Product表的更新跟踪就可以了。
    12. 最后试一下DROP命令
      DROP TABLE dbo.Product;
      dbo.Product表消失了,同时cdc.dbo_Product_CT表也消失了。
    13. 尝试结束,哦哦,对了。一定有人问,捕获到的更新怎么用呢,还有一堆系统函数和存储过程可以帮助用户,但是那段测试的过程就不写了。
      其中最重要的应该就是cdc.fn_cdc_get_all_changes_<capture_instance>cdc.fn_cdc_get_net_changes_<capture_instance>两个函数了,这两个函数可以帮助我们获取dbo_Product_CT表中数据,其中cdc.fn_cdc_get_all_changes_<capture_instance>是用于获取所有更新,而cdc.fn_cdc_get_net_changes_<capture_instance>则是用于获取精简后的更新,在精简的更新中有一些重复的更新就会被合并成一条记录,比如说我们把产品类型由A改为B,然后又改回A,在cdc.fn_cdc_get_all_changes_<capture_instance>中应该有3条记录,而在cdc.fn_cdc_get_net_changes_<capture_instance>中则只有1条记录。两个函数的范例如下(你会发现精简结果集的函数运算相当慢,至少在CTP4中是这样的,不知道以后的版本会不回有改进):
      SELECT * FROM cdc.fn_cdc_get_all_changes_dbo_Product(0x00000048000001760004, 0x00000048000001F70004, 'all');
      SELECT * FROM cdc.fn_cdc_get_net_changes_dbo_Product(0x00000048000001760004, 0x00000048000001F70004, 'all');
    January 11

    SQL Server 2008 IntelliSense试用记

    DML语句

    最遗憾的是IntelliSense只支持SELECT,UPDATE、DELETE和INSERT语句都不支持。

    对于SELECT语句,Query Editor会根据当前的上下文确定当前数据库,也就是说不是当前会话连接的数据库,所以用一句简单的USE DATABASE语句就可以改变用于生成IntelliSense列表的当前数据库。IntelliSense初始加载的列表项包括当前数据库中的架构以及用户默认架构下的表、视图和用户定义函数,还有所有系统内置的系统变量、系统函数,最后还有当前连接实例中的所有其他数据库。用户在Query Editior中输入架构并加上"."符号后就可以看到架构下的表、视图和用户函数了,当用户继续输入表名或视图名并加上"."符号后就可以看到表和视图中的字段列表了。

    从安全性方面,用户必须要有View Definition的权限才能正常使用IntelliSense。拿AdventureWorks来说,AdventureWorks下面有Person、Production、Purchasing和Sales四个架构,如果用户只有Sales架构的View Definition权限,那么当他输入"Production." 、"Person."或"Purchasing."的时候,他就看不到任何的提示。

    另外值得提到的是,IntelliSense提供的列表不仅仅是数据库中已经存在的对象,还可以包括在当前脚本中那些尚未提交的对象,这就使得在编写架构定义的脚本时IntelliSense仍然可以起到一定作用。

    DDL语句以及DCL语句

    没有提供IntelliSense的支持。

    参数支持

    SQL Server 2008的IntelliSense支持函数和存储过程的参数,不过非常遗憾的是要让IntelliSense提供参数信息必须要在XML Editor下。

    总结

    总体来说,个人觉得SQL Server 2008的IntelliSense功能还需要改进,至少要支持DDL语句么,而且不要把参数支持放到XML Editor下面去。

    November 16

    PerformancePoint Server 2007框架

    微软最近发布了PerformancePoint Server 2007,这个产品替代了微软用于平衡计分的产品——Business Score Card 2005,其中还整合了ProClarity产品,同时还添加了Planning Server。因此PerformancePoint Server 2007现在在微软商务智能产品体系中主要提供规划、监控以及分析三项服务,主要针对的是企业绩效管理(Corporate Performance Management CPM)市场。

    PPS整个产品架构分为三层:服务器层、客户端层以及后端存储层。

    服务器层

    在Server层主要包括了两项服务:Planning Web Service以及Planning Process Service。

    Planning Web Service主要处理来自于Planning Administration Console站点、Planning Business Modeler工具、PerformancePoint Add-in for Excel以及Planning Command Utility的任务请求。用户通常通过这工具来完成业务模型的设计、业务流程的设计、模型站点的部署或者在SQL Server以及SQL Server分析服务器中建立数据库。

    Planning Process Service是一个Windows服务,他的主要任务是处理作业、处理来自于PerformancePoint Add-in for Excel的数据或者多维数据的处理请求。

    客户端层

    客户端一般都使用Planning Business Modeler、PerformancePoint Add-in for Excel、Planning Administration Console以及Planning Command Utility四项工具。

    Planning Business Modeler可以运行在Windows XP或Windows Vista操作系统下。用户通过这个工具可以设计PerformancePoint Server应用,也可以利用这个工具从外部数据源载入参考或者事实数据。

    PerformancePoint Add-in for Excel作为Excel 2003或Excel 2007上的一个插件,允许用户通过他设计报表以及表格、启动作业或者输入数据。

    Planning Administration Console是一个宿主在IIS中的站点,用户通过浏览器使用该站点来连接到Planning Web Service并执行诸如创建和配置应用之类的任务。

    Planning Command Utility是一个命令行工具,主要用于配置Planning Server的集群环境。

    后端存储层

    PerformancePoint Server 2007的后端存储层包括了SQL Server Planning System以及Planning Service数据库,Planning应用、Staging以及Outbound数据库,SQL Server 2005分析服务数据库,SQL Server 2005报表服务以及共享存储。

    SQL Server Planning System以及Planning Service数据库主要宿主用于Planning Process Service以及Planning Web Service的数据表,同时也存储用于定义每个PerformancePoint应用的相关数据表。每个PerformancePoint Server的实例拥有独立的一套数据库。

    Planning应用数据库主要存储了一个PerformancePoint应用的元数据、关联数据、事实数据以及工作流定义。每个PerformancePoint应用都拥有一套数据库,但是Staging以及Outbound数据库是可选的。

    SQL Server 2005分析服务数据库保存了模型站点中定义的维度结构、多维数据集以及计算公式。每个PerformancePoint应用至少有一个或多个模型站点,因此每个PerformancePoint应用可以有一个或多个SQL Server 2005分析服务数据库。

    SQL Server 2005报表服务为PerformancePoint Server提供了报表操纵能力。

    共享存储层由Microsoft Office SharePoint服务器或者Windows SharePoint Service或者文件服务器组成,主要是为PerformancePoint Server提供表单模版以及报表的共享存储。

      组     件
    服务器层 Planning Web Service
    Planning Process Service
    客户端层 Planning Business Modeler
    PerformancePoint Add-in for Excel
    Planning Administration Console
    Planning Command Utility
    后端存储层 SQL Server Planning System
    Planning Service数据库
    Planning应用、Staging以及Outbound数据库
    SQL Server 2005分析服务数据库
    SQL Server 2005报表服务
    共享存储