木木's profile宠辱不惊PhotosBlogListsMore Tools Help

Blog


    October 25

    关于OPENROWSET和Access与Sql Server交互

    同事问起,想给OPENROWSET带入参数。脑子里第一时间想到的,自然是定义一个变量就可以了。哪知道,一用才知道根本不行。查了帮助,才知道:OPENROWSET 不接受参数变量。
     
    脑子里马上想到了sp_executesql这个老大,一试验,还真成了。尽管如此,还是郁闷,不知道为什么MS要这么做。
     
    示例1:
    DECLARE @DSN nvarchar(1000)
    DECLARE @SQL nvarchar(4000)
    SET @DSN=
    N'DRIVER=Microsoft Access Driver (*.mdb);
    UID=admin;
    UserCommitSync=Yes;
    Threads=3;
    SafeTransactions=0;
    PageTimeout=5;
    MaxScanRows=8;
    MaxBufferSize=2048;
    FIL=MS Access;
    DriverId=281;
    DefaultDir=[YOUR DIRECTORY];
    DBQ=[FULL PATH OF MDB]'
    SET @SQL=N'
    SELECT
    *
    FROM OPENROWSET(''msdasql'', '''+@DSN+''', ''SELECT * FROM [AccessTable]'')
        AS [SomeAlias]'
    sp_executesql @sql
    对于访问Access,可以直接使用OLEDB。
    示例2:
    SELECT * FROM
    OPENROWSET('Microsoft.Jet.OLEDB.4.0', 
        'd:\northwind.mdb',
        Orders) t
    August 02

    遇到的两个问题

    一、Oracle 全文检索时DRG-11622错误
    现象:
    使用'URL_DATASTORE',并用FTP方式进行全文检索时,报类似:DRG-11622: URL 存储: 获取ftp:// tqnpc / webdev : caini @ 180.180.183.104 : 121 / adim_0001 / 4.pdf时,出现未知的 HTTP 错误。
    但是,如果将上述地址直接放到IE地址栏里面,是可以打开该文件的。
    分析:
    由于是局域网内,1000M主干的带宽,服务器配置也比较好,不存在带宽或者硬件问题。在IE中能打开也说明了这点。
    解决过程:
  • 官方帮助:

    DRG-11622 URL store: unknown HTTP error getting string

    Cause: HTTP error has occurred which ConText does not catch.

    Action: Consult HTTP error codes; if valid, contact ConText at Oracle.

  • 查看FTP服务器日志,也没发现什么特别之处。只是有关闭的记录。closed 421 121
  • 最终解决:

    重新在该服务器上创建一个FTP站点,索引没问题。于是重新删除原来FTP站点后重新建立,问题解决。但是原因不明。

    二、JavaScript的parseInt

    其实这个函数也很简单,可是在以前却用错了。原因是没有指明被转换字符串的进制。该方法默认将0开头的字符串认作是8进制。所以我在转换月份时,08、09月时就得到结果为0。

    所以使用该函数小心一下,尽量加上第二个参数。

    例如:parseInt("08")返回0,parseInt("08",10)返回8。

    July 14

    关于VS2005的两个问题

    一、在VS2005下,试图对项目进行调试时,报:“试图运行项目时出错,无法启动调试。绑定句柄无效”
     
    方法1: 出现这种情况的原因为:Terminal Services 这个服务被禁用了,打开服务启动这个服务就可以调试了(原来我前段时间在优化系统的时候禁用了这个服务)。   

    方法2:将项目属性调试里的:取消“启用Visual Studio 宿主进程”选择。或者选中“启用非托管代码调试”

     
    二、使用GetManifestResourceStream()时,在资源文件的属性中,将“生成操作”修改为“嵌入的资源”
    June 29

    安装Windows SharePoint Services 中文网站模板错误

    安装完制造业单位网站模板后报错:

    在此页的 Register 指令中未找到指定的 Microsoft.Office.DataParts, Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c 程序集。

     

    Web 部件维护网页:如果您有权限,您可以使用此网页临时禁用 Web 部件或删除个人设置。有关详细信息,请与网站管理员联系。


    Windows SharePoint Services 疑难解答

     

    解决办法:

    因为缺少了服务器组件(查看:c:\WINDOWS\assembly下不存在Microsoft.Office.DataParts程序集),所以报这个错误。在SPS安装包中有一个叫STSTPKPL的文件夹,把里面的ststpkpl.exe给安装一下就好了。

     

    模板下载地址:http://office.microsoft.com/zh-cn/assistance/HA011929182052.aspx

    June 22

    SharePoint Portal几个问题

    一、单点登录配置(SSO)
    连接到数据库服务器失败。请检查配置帐户的连接性和权利,然后再试一次。
    如果将该数据库名修改为一个已经存在的数据库,则会提示:
    该数据库已存在
    这又说明其实是已经连接上数据库了,看来是权限不够。根据SPS帮助,需要如下条件:
    1. Single Sign-on Service 帐户必须与 Single Sign-on 管理员帐户相同,或者是作为 Single Sign-on 管理员帐户的用户组帐户的成员。
    2. 此帐户必须是服务器场中运行 Microsoft Office SharePoint Portal Server 2003 的所有服务器上本地组 STS_WPG 的成员。
    3. 此帐户必须是服务器场中运行 SharePoint Portal Server 的所有服务器上本地组 SPS_WPG 的成员。
    4. 此帐户必须是 SharePoint Portal Server 配置数据库中公共数据库角色的成员。
    5. 此帐户必须是 Single Sign-on 数据库所在的 Microsoft SQL Server 实例中的“服务器管理员”服务器角色的成员。
    检查了一下俺的帐户,条件都满足。Google了半天,发现原来是MS的BUG。参看:
     
     
    解决方案:
    1. 单击 确定 、 开始 , 键入 regedit , 和 运行 。
    2. 找到并单击以下注册表子项:
      HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\ssosrv
    3. 在右窗格中, 右击 ImagePath , 然后单击 修改 。
    4. 从路径, 在数值数据 框中显示删除引号。
      该路径为类似于以下路径, 取决于您安装文件夹:
      C:\Program Files\Common Files\Microsoft Shared\Microsoft on\SSOSRV.EXE
    5. 退出注册表编辑器。
    6. 重新启动 Microsoft Single Sign-on Service 服务或者重新启动电脑
    二、发现事件日志中有错误:无法连接到 RDS02 (您的服务器名)上的数据库 SPS01_Config_db。请检查数据库连接信息,并确保数据库服务器正在运行。
     
    解决方案:
     
    经检查,从SPS服务器其实可以连接到数据库服务器。联想到之前的权限问题,考虑到可能是权限不够。发现目前启动SPS服务的帐户是:CAINI\BOOTER(域的管理员帐户)。而该帐户在数据库服务器上没有登录权限。于是将CAINI\BOOTER帐户添加的数据库服务器(RDS02)的本地管理员组中解决。或者在数据库的企业管理器中将CAINI\BOOTER添加为SQL SERVER登录帐户,并为其分配SPS01_Config_db的PUBLIC和DB_OWNER权限即可解决。
     
    TIPS:一般目前网上做试验,通常将SPS服务和数据库服务架设在同一机器上,并且在做试验时基本使用域名/administrator来进行演示。一般不容易出现或者发现这个问题。SharePoint Portal Administration 等的服务的启动帐户在数据库服务器上应具备相应权限。
     
    三、发现事件日志错误:SharePoint Portal Administration Service 同步意外发生。
     
    搜索中文,没有找到相关说明。用E文GOOGLE了一下,找到如下说明:
     
    Event ID: 202
    Source SharePoint Portal Administration Service 
    Type Error 
    Description Synchronization exception occurred. 
    Comments Michel Dawidowicz (Last update 1/28/2005):
    In my case, this symptom started to appear when the database backend was down for a short period. The event is logged twice per minute in the log. To resolve this restart the "Sharepoint Portal Administration" service on each affected server of the farm.

    Ionut Marin (Last update 11/3/2004):
    From a newsgroup post: "Go to IIS manager, open each site, and go to the home directory tab. Then, there is a section at the bottom called Application pools. Edit each and set the account to someone who has logon as a service right to the local server".  
    来自:
     
    应该是第一个解释,因为之前数据库连接失败,所以导致后面的错误。我重启动服务后,就不再出现这个提示。
    June 19

    SQL SERVER数据库表主键设计(三)

     

    =================================================================================

    -- 存储过程名 : SP_DB_COMMON_PAGER

    -- 创建人     : LDY

    -- 创建时间   : 2006-04-26

    -- 说明       : 基于SQL Server数据库的通用分页存储过程

    --            @S_TABLE_NAME    VARCHAR  IN  要选择数据的表

    --            @S_WHERE_CLAUSE  VARCHAR  IN  条件语句

    --            @ORDER_COLUMNS   VARCHAR  IN  排序字段,用逗号分开:COL1,COL2,...,COLN

    --            @I_PAGE_SIZE     VARCHAR  IN  每页的行数

    --            @I_CURRENT_PAGE  VARCHAR  OUT 要获取的页号,当前第几页。如果当前页超过总页数,那么返回最后一页

    --            @I_RECORD_COUNT  VARCHAR  OUT 返回总共的记录数

    --            @I_PAGE_COUNT    VARCHAR  OUT 返回总共的页数

    -- =================================================================================

    IF EXISTS (SELECT name

           FROM   SYSOBJECTS

           WHERE  NAME = N'SP_DB_COMMON_PAGER' AND   

              TYPE = 'P')

        DROP PROCEDURE SP_DB_COMMON_PAGER

    GO

     

    CREATE PROCEDURE SP_DB_COMMON_PAGER

        @S_TABLE_NAME     VARCHAR(128) = '',

        @S_WHERE_CLAUSE   VARCHAR(500) = '',

        @S_ORDER_COLUMNS  VARCHAR(200) = '',

        @I_PAGE_SIZE      BIGINT       = 10,

        @I_CURRENT_PAGE   BIGINT  OUT      ,

        @I_RECORD_COUNT   BIGINT  OUT      ,

        @I_PAGE_COUNT     BIGINT  OUT        

    WITH ENCRYPTION

    AS

    BEGIN

       

        --不对SQL计数

        SET NOCOUNT ON

        --声名变量

        DECLARE @SQL_TEMP NVARCHAR(4000)

        --参数合法性检查

        IF @S_TABLE_NAME IS NULL RAISERROR('表名不能为空',16,1)

        IF NOT EXISTS (SELECT name

           FROM   SYSOBJECTS

           WHERE  NAME = @S_TABLE_NAME AND

                  type = 'U')

        RAISERROR('表名不存在',16,1)

        IF @S_WHERE_CLAUSE IS NULL SET @S_WHERE_CLAUSE = ''

        IF @S_ORDER_COLUMNS  IS NULL SET @S_ORDER_COLUMNS = ' U_ID DESC '

        --每页至少两行

        IF @I_PAGE_SIZE < 2 SET @I_PAGE_SIZE = 2

        --获取总行数(假设每个表都有唯一主键字段U_ID)

        IF LEN(@S_WHERE_CLAUSE) > 0

            SET @SQL_TEMP = 'SELECT @I_RECORD_COUNT=COUNT(U_ID) FROM ' + @S_TABLE_NAME + ' WHERE ' + @S_WHERE_CLAUSE       

        ELSE

            SET @SQL_TEMP = 'SELECT @I_RECORD_COUNT=COUNT(U_ID) FROM ' + @S_TABLE_NAME

       

        EXEC SP_EXECUTESQL @SQL_TEMP,N'@I_RECORD_COUNT BIGINT OUT',@I_RECORD_COUNT OUT

        --PRINT @SQL_TEMP

        --PRINT @I_RECORD_COUNT

        --

        IF @I_RECORD_COUNT >0

        BEGIN

            --获取页数

            IF @I_RECORD_COUNT % @I_PAGE_SIZE > 0

                SET @I_PAGE_COUNT = @I_RECORD_COUNT / @I_PAGE_SIZE + 1

            ELSE

                SET @I_PAGE_COUNT = @I_RECORD_COUNT / @I_PAGE_SIZE

            --当前页

            IF @I_CURRENT_PAGE > @I_PAGE_COUNT SET @I_CURRENT_PAGE = @I_PAGE_COUNT

            --获取当前页的记录'

            IF LEN(@S_WHERE_CLAUSE) > 0

                SET @SQL_TEMP = 'SELECT TOP ' + CAST(@I_PAGE_SIZE AS VARCHAR) + ' ' + @S_TABLE_NAME + '.* FROM ' + @S_TABLE_NAME + ' ' +

                        'WHERE ' + @S_TABLE_NAME + '.U_ID NOT IN ' +

                        '(SELECT TOP ' + CAST((@I_CURRENT_PAGE-1) * @I_PAGE_SIZE AS VARCHAR) + ' U_ID FROM ' + @S_TABLE_NAME + ' WHERE ' + @S_WHERE_CLAUSE + ' ORDER BY ' + @S_ORDER_COLUMNS + ')' + ' AND ' + @S_WHERE_CLAUSE +

                        'ORDER BY ' + @S_ORDER_COLUMNS

            ELSE

                SET @SQL_TEMP = 'SELECT TOP ' + CAST(@I_PAGE_SIZE AS VARCHAR) + ' ' + @S_TABLE_NAME + '.* FROM ' + @S_TABLE_NAME + ' ' +

                        'WHERE ' + @S_TABLE_NAME + '.U_ID NOT IN ' +

                        '(SELECT TOP ' + CAST((@I_CURRENT_PAGE-1) * @I_PAGE_SIZE AS VARCHAR) + ' U_ID FROM ' + @S_TABLE_NAME + ' ORDER BY ' + @S_ORDER_COLUMNS + ')' +

                        'ORDER BY ' + @S_ORDER_COLUMNS

            --PRINT @SQL_TEMP

            SET NOCOUNT OFF

            EXEC(@SQL_TEMP)

        END

        ELSE

        BEGIN

            --返回值设置

            SET @I_PAGE_SIZE = 0

            SET @I_CURRENT_PAGE = 1

            SET @I_PAGE_COUNT = 0

            --返回空的表记录集

            SET @SQL_TEMP = 'SELECT * FROM ' + @S_TABLE_NAME + ' WHERE U_ID IS NULL'

            SET NOCOUNT OFF

            --PRINT @SQL_TEMP

            EXEC(@SQL_TEMP)

        END

        /**/

    END

    从存储过程的实现过程我们可以清楚的看到,如果没有”U_ID”,也就是唯一关键字段的存在,在实现分页时将会复杂很多。同样,也正是有了这样一个字段,也可以使程序的统一性提高,以便重复利用。

    6.   总结

    主键设计问题的提出,是来源于项目过程中的经验教训。通过总结,提出了具有通用性的主键设计思想,并从实践去实现了前文提出的设计思想。

    在今后的数据库设计中,参考本主键设计思想,可以为设计开发提高效率和应用的性能。

    SQL SERVER数据库表主键设计(二)

    4.   使用序列的问题及解决办法

    4.1    如何保证业务逻辑约束

    由于系统使用一个额外增加一个字段作为主键,因此没有为业务逻辑建立主键约束。比如在企业用户信息表中,要求企业中用户登录名必须唯一。一般在创建表时,以登录名作为主键,这个时候在数据库层自然的创建另一个主键唯一性约束。而现在没有使用登录名作为主键,那么就没有这个约束。解决办法:

    一是在数据库层解决。可以为该表创建一个唯一(UNIQUE)约束或者唯一索引。如:

    ALTER TABLE T_PK_DEMO ADD CONSTRAINT C_T_PK_DEMO UNIQUE NONCLUSTERED(COL_OTHER)-唯一约束

    CREATE  UNIQUE  INDEX IX_T_PK_DEMO ON T_PK_DEMO(COL_OTHER) 唯一索引

    二是在应用端解决。也就是在应用中判断该列是否有重复值,然后根据判断结果来保证唯一性。

    4.2    为何主键采用非聚蔟方式

    我们注意到,在之前的例子中,主键采用了NONCLUSTERED(非聚蔟)的索引方式。关于如何设计索引,不是本文的重点,在这里仅提供一个建立索引时采用聚蔟方式还是非聚蔟方式的一个一般原则:

    动作描述

    使用聚集索引

    使用非聚集索引

    列经常被分组排序

    返回某范围内的数据

    ×

    一个或极少不同值

    ×

    ×

    小数目的不同值

    ×

    大数目的不同值

    ×

    频繁更新的列

    ×

    外键列

    主键列

    频繁修改索引列

    ×

    作为非业务字段的主键列,是一个没有重复值的、基本不进行更新操作的列。并且,在SQL Server数据库中,聚蔟索引在一个表中只能有一个。因此,聚蔟索引非常重要,需要留给更重要的字段来使用。因此,对照上表和根据聚蔟索引的重要程度,在此处采用非聚蔟方式创建其索引。

    5.   具体应用

    采用这种主键设计方式,有诸多好处,这已经在前文说明。现在就以一个具体的应用来说明如何使用这个主键。

    当前的应用系统基本上都已经采用B/S方式,尽管现在的网络速度已经有大幅度的提高,但是由于在WEB应用上用户数量众多、同时基本上所有的运算都集中在WEB应用服务器上,所以在WEB设计上更要考虑到性能的优化,以减少网络流量和对服务器的压力。最常见的一个应用就是列表方式展现时的分页方式。一般的,在数据量小的情况下,一般不会怎么注意这个问题,通常采用将数据完全取出,然后在WEB服务器上进行分页。但是,当数据量庞大时,这种方式就会导致速度降低,甚至根本不可用。所以,一般采用存储过程,在数据库端进行分页。下面就提供一个通用的分页存储过程SP_COMMON_PAGER

    SQL SERVER数据库表主键设计(一)

     

    摘要:本文根据笔者在历往项目中的数据库表主键设计的情况,发现过去在主键设计上存在着不合理性。在此基础上,提出了新的主键设计思想和具体解决办法。该设计思路具有通用意义,针对大多数情况是一种行之有效的办法,具有现时的指导意义。

     

    关键词:SQL Server 数据库 主键 序列 分页

     

    1.   序言

    当前,随着信息量的急剧增加,对于数据的存储和管理方式,各企业都逐渐摆脱了之前的依靠文件系统(文本文件或者Excel)或者一些桌面型的小型数据库系统(如AccessFoxBASE或者DBase)的状态,转而通过一些大型数据库来管理企业的信息。这些大型数据库系统包括OracleMS SQL Server或者IBM DB2。尽管目前数据库系统也在向面向对象的数据库系统方向发展,但是上述的传统的关系型数据库系统依然占据着主要位置。

    笔者从九十年代末开始以关系型数据库系统为基础为客户进行管理软件的定制化开发。主要是以PowerBuilder为前台开发工具,开发出一些列的C/S结构的软件。进入到本世纪,尤其是最近两年,笔者又以Visual Studio 2003为工具,开发了一些B/S结构的应用。但是,无论是使用何种开发工具,还是开发何种结构的软件,其后台数据库系统依旧是关系型数据库系统。根据客户的应用环境,主要是在MS SQL Server数据库上进行开发,当然也有基于Oracle数据库的软件开发。

    也正是因为数据量信息量的增加,采用大型的关系型数据库系统作为企业的数据存储管理方式,也就要求基于数据库开发的开发者在数据库设计时必须遵循相应的规范。关于数据库逻辑设计,最重要的就是数据库表的设计,都有一套相应的理论支持,比如要满足相应的范式要求。一般而言,数据库表设计满足第二或者第三范式即可。

    在开发过程中,也尽量遵循这些相应的规则,但由于之前的经验所限或者是在详细设计时做的工作不够充分,导致一些表结构不是很合理。正是这些不合理,目前出现一些问题,并且已经在部分系统中有所体现。

    关于数据库的逻辑设计,是一个很广泛的问题。本文主要针对笔者开发应用中的现状,论述在MS SQL Server上进行表设计时,对表的主键设计应注意的问题以及相应的解决办法。

    2.   主键设计现状和问题

    2.1    现状

    关于数据库表的主键设计,一般而言,是根据业务需求情况,以业务逻辑为基础,形成主键。

    比如,销售时要记录销售情况,一般需要两个表,一个是销售单的概要描述,记录诸如销售单号、总金额一类的情况,另外一个表记录每种商品的数量和金额。对于第一个表(主表),通常我们以单据号为主键;对于商品销售的明细表(从表),我们就需要将主表的单据号也放入到商品的明细表中,使其关联起来形成主从关系。同时该单据号与商品的编码一起,形成明细表的联合主键。这只是一般情况,我们稍微将这个问题延伸一下:假如在明细中,我们每种商品又可能以不同的价格方式销售。有部分按折扣价格销售,有部分按正常价格销售。要记录这些情况,那么我们就需要第三个表。而这第三个表的主键就需要第一个表的单据号以及第二个表的商品号再加上自身需要的信息一起构成联合主键;又或者其他情况,在第一个主表中,本身就是以联合方式构成联合主键,那么也需要在从表中将主表的多个字段添加进来联合在一起形成自己的主键。

    笔者在以前的项目中,也基本上是采取这样的表设计思路来设计系统的表结构和主键。

    2.2    存在的问题

    在上面小节中,我们描述了当前在表的主键设计时的现状。从中我们不难看出存在这样的问题:

    n        数据冗余存储:随着这种主从关系的延伸,数据库中需要重复存储的数据将变得越来越庞大。或者当主表本身就是联合主键时,就必须在从表中将所有的字段重新存储一次。

    n        SQL复杂度增加:当存在多个字段的联合主键时,我们需要将主表的多个字段与子表的多个字段关联以获取满足某些条件的所有详细情况记录。

    n        程序复杂度增加:可能需要传递多个参数。

    n        效率降低:数据库系统需要判断更多的条件,SQL语句长度增加。同时,联合主键自动生成联合索引

    n        WEB分页困难:由于是联合主键方式(对于多数的子表),那么在WEB页面上要进行分页处理时,在自关联时,难于处理。

    3.   解决方案

    3.1    概述

    从上面,我们已经看到现有结构存在着相当多的弊端,主要是导致程序复杂、效率降低并且不利于分页。

    为解决上述问题,本文提出:当应用系统后台数据库表间存在主从关系时,数据库表额外增加一非业务字段作为主键,该字段为数值型;或者当该表需要在应用中进行分页查询时,也应考虑如此设计。一般地,我们也可以几乎为任何表增加一个与业务逻辑无关的字段作为该表的主键字段。

    3.2    字段定义方式

    由于该字段要作为表的主键,那么其首要条件是要保证在该表中要具有唯一性。同时,结合SQL Server数据库自身的特性,可以为其建立一个自增列:

    CREATE TABLE T_PK_DEMO

    (

            U_ID           BIGINT          NOT NULL IDENTITY(1,1),--唯一标识记录的ID

            COL_OTHER  VARCHAR(20)     NOT NULL              ,--其他列

            CONSTRAINT PK_T_PK_DEMO PRIMARY KEY  NONCLUSTERED (U_ID)--定义为主键

    )

    但是,SQL Server中的自增列却存在一个比较尴尬的事实,那就是该字段一旦定义和使用,用户无法直接干预该字段的值,完全由数据库系统自身控制:

    n        完全数据库系统控制,用户无法修改值

    n        在数据库的发布和订阅时,使用自增列会比较麻烦

    n        恢复部分数据时,使用自增列会比较麻烦

    n        该列的值必须在插入数据后才能获取

    鉴于此,建议不以自增列的方式来定义,而是参考Oracle数据库系统中序列,在SQL Server系统中实现类似Oracle数据库系统序列功能。这个具体在下面的小节中介绍。我们只需要按照普通字段的定义方式修改表定义为:

    CREATE TABLE T_PK_DEMO

    (

            U_ID           BIGINT         NOT NULL ,--唯一标识记录的ID

            COL_OTHER  VARCHAR(20)     NOT NULL ,--其他列

            CONSTRAINT PK_T_PK_DEMO PRIMARY KEY  NONCLUSTERED (U_ID)--定义为主键

    )

    3.3    序列的实现

    参照Oracle序列的功能,我们需要在SQL Server数据库中创建一个新表,以管理序列值:

    CREATE TABLE T_DB_SEQ

    (

        SEQ_NAME        VARCHAR(50)     NOT NULL              ,--序列名称

        SEQ_OWNER       VARCHAR(50)     NOT NULL DEFAULT 'DBO',--序列所有者(SYSTEM_USER)

        SEQ_CURRENT     BIGINT          NOT NULL DEFAULT 0    ,--序列当前值

        SEQ_MIN         BIGINT          NOT NULL DEFAULT 0    ,--序列最小值

        SEQ_MAX         BIGINT          NOT NULL DEFAULT 0    ,--序列最小值

        SEQ_MAX         BIGINT          NOT NULL DEFAULT 0    ,--序列最大值

        SEQ_STEP        INT             NOT NULL DEFAULT 1    ,--序列增长步长

        IF_CYCLE        INT             NOT NULL DEFAULT 0    ,--是否循环(0,不循环;1,循环)

        CONSTRAINT T_DB_SEQ PRIMARY KEY  CLUSTERED (SEQ_NAME,SEQ_OWNER)--主键

    )

    应用系统为需要创建自增列的表创建一个序列名称,在表“T_DB_SEQ”中反映为数据库中的一行。

    3.4    使用序列

    第一,需要为需要建立序列的表创建一个序列。采用方法:F_CREATE_SEQ(序列名)。该函数传入序列的名称,在表“T_DB_SEQ”插入一行。序列的所有者,采用系统变量SYSTEM_USER

    第二,获取下一个值。采用方法:F_GET_NEXT_SEQ_VAL(序列名)。该函数根据序列名获取该序列的下一个值,根据当前值与增长步长得到。同时,该函数保证在同时获取同一个序列时,应保证并发一致性。

    第三、将返回值返回到应用使用。

    此外,为保证应用的完整性,可能还需要提供一些方法的重载方法,同时提供一些其他方法:

    n        获取序列当前值:F_GET_SEQ_CUR_VAL(序列名)

    n        设置序列值:F_SET_SEQ_VAL(序列名)

    n        删除序列:F_DEL_SEQ(序列名)

    n        判断序列是否存在:F_SEQ_EXISTS(序列名)

    在主从关系的表设计中,子表也使用序列字段作为唯一主键,将父表的序列字段作为外键关联:

    CREATE TABLE T_PK_DEMO_C

    (

        U_ID           BIGINT         NOT NULL ,--唯一标识记录的ID

        COL_OTHER  VARCHAR(20)     NOT NULL ,--其他列

        P_ID       INT             NOT NULL ,--父表ID

        CONSTRAINT PK_T_PK_DEMO_C PRIMARY KEY  NONCLUSTERED (U_ID)--定义为主键

        CONSTRAINT FK_T_PK_DEMO_C FOREIGN KEY (P_ID) REFERENCES T_PK_DEMO(U_ID) ON DELETE CASCADE,

    )

    April 03

    基于函数的索引

    使用基于函数的索引(BFI, Based Function Index):

    Oracle 8i开始,可以使用基于函数的索引来提高查询性能,

     

    使用基于函数的索引,需要几个条件:

    1,  用户需要有create index或者create any index权限

    2,  用户需要有query rewrite或者global query rewirte权限

    3,  设置系统参数 query_rewrite_enabled=TRUE

    query_rewrite_integrity=enforced

    4,  设置系统参数 :COMPATIBLE=8.1.0.0.0 或者更高

    5,创建了BFI后,需要对表进行分析

     

    请看下面的例子:

    首先,在没有建立函数索引的情况下,我们看到查询没有如我们想想一样使用单列(dname)索引:

    SQL> set autotrace traceonly

    SQL> select * from dept where substr(dname,1,5)='aaa';

     

    未选定行

     

    已用时间:  00: 00: 00.00

     

    Execution Plan

    ----------------------------------------------------------

       0      SELECT STATEMENT Optimizer=CHOOSE

       1    0   TABLE ACCESS (FULL) OF 'DEPT'

     

     

     

     

    Statistics

    ----------------------------------------------------------

            134  recursive calls

              0  db block gets

             20  consistent gets

              0  physical reads

              0  redo size

            323  bytes sent via SQL*Net to client

            372  bytes received via SQL*Net from client

              1  SQL*Net roundtrips to/from client

              2  sorts (memory)

              0  sorts (disk)

              0  rows processed

     

     

     

    下面直接建立基于函数的索引,看看是否查询是否可以使用我们建立的索引

    SQL> create index dept_id5 on dept(substr(dname,1,5));

    create index dept_id5 on dept(substr(dname,1,5))

                                                 *

    ERROR 位于第 1 :

    ORA-01031: 权限不足

     

     

    已用时间:  00: 00: 00.00

    SQL> set autotrace off

    SQL> col username format a10

    SQL> col privilege format a20

    SQL> select username,privilege from user_sys_privs;

     

    USERNAME   PRIVILEGE

    ---------- --------------------

    DEMO       UNLIMITED TABLESPACE

    PUBLIC     SELECT ANY TABLE

     

    已用时间:  00: 00: 00.00

    SQL> select username, granted_role from user_role_privs;

     

    USERNAME   GRANTED_ROLE

    ---------- ------------------------------

    DEMO       CONNECT

    DEMO       RESOURCE

    PUBLIC     PLUSTRACE

     

    已用时间:  00: 00: 00.01

     

    我们看到,虽然用户有connectresource角色,但是仍然没有建立函数索引的权限。

     

    我们使用sysdba身份登陆,给demo用户授create any index global query rewrite权限:

    SQL> conn lunar/lunar@test1 as sysdba

    已连接。

    SQL> grant create any index to demo;

     

    授权成功。

     

    已用时间:  00: 00: 00.00

    SQL> grant global query rewrite to demo;

     

    授权成功。

     

    已用时间:  00: 00: 00.00

    SQL> conn demo/demo@test1

    已连接。

    SQL> select username,privilege from user_sys_privs;

     

    USERNAME   PRIVILEGE

    ---------- --------------------

    DEMO       CREATE ANY INDEX

    DEMO       GLOBAL QUERY REWRITE

    DEMO       UNLIMITED TABLESPACE

    PUBLIC     SELECT ANY TABLE

     

    已用时间:  00: 00: 00.00

    SQL> select username, granted_role from user_role_privs;

     

    USERNAME   GRANTED_ROLE

    ---------- ------------------------------

    DEMO       CONNECT

    DEMO       RESOURCE

    PUBLIC     PLUSTRACE

     

    已用时间:  00: 00: 00.00

     

    再修改系统参数,将query_rewrite_enabled设置为true,这个参数是动态参数,设置后可以有立杆见影的效果:

    SQL> conn /@test1 as sysdba

    已连接。

    SQL> show parameter query

     

    NAME                                 TYPE        VALUE

    ------------------------------------ ----------- ------------------------------

    query_rewrite_enabled                string      FALSE

    query_rewrite_integrity              string      enforced

    SQL> alter system set query_rewrite_enabled=true;

     

    系统已更改。

     

    已用时间:  00: 00: 00.00

    SQL> show parameter query

     

    NAME                                 TYPE        VALUE

    ------------------------------------ ----------- ------------------------------

    query_rewrite_enabled                string      TRUE

    query_rewrite_integrity              string      enforced

     

    好了,再使用demo用户登陆,创建函数索引

    SQL> conn demo/demo@test1

    已连接。

    SQL>  create index dept_id5 on dept(substr(dname,1,5));

     

    索引已创建。

     

    已用时间:  00: 00: 00.00

    SQL> select index_type,index_name from user_indexes where table_name='DEPT';

     

    INDEX_TYPE                  INDEX_NAME

    --------------------------- ------------------------------

    FUNCTION-BASED NORMAL       DEPT_ID5

     

    已用时间:  00: 00: 00.00

     

    可见已经创建成功了。

     

    下面,我们看看查询是否会使用我们创建的函数索引:

    SQL> set autotrace traceonly

    SQL> select * from dept where substr(dname,1,5)='aaa';

     

    未选定行

     

    已用时间:  00: 00: 00.00

     

    Execution Plan

    ----------------------------------------------------------

       0      SELECT STATEMENT Optimizer=CHOOSE

       1    0   TABLE ACCESS (FULL) OF 'DEPT'

     

     

     

     

    Statistics

    ----------------------------------------------------------

             29  recursive calls

              0  db block gets

             10  consistent gets

              0  physical reads

              0  redo size

            323  bytes sent via SQL*Net to client

            372  bytes received via SQL*Net from client

              1  SQL*Net roundtrips to/from client

              0  sorts (memory)

              0  sorts (disk)

              0  rows processed

     

     

    在我们分析表之后,我们看到,查询如我们所希望的那样,使用了索引。

    SQL> analyze table dept compute statistics

      2  for table

      3  for all indexes

      4  for all indexed columns;

     

    表已分析。

     

    已用时间:  00: 00: 00.02

    SQL> select * from dept where substr(dname,1,5)='aaa';

     

    未选定行

     

    已用时间:  00: 00: 00.02

     

    Execution Plan

    ----------------------------------------------------------

       0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=23)

       1    0   TABLE ACCESS (BY INDEX ROWID) OF 'DEPT' (Cost=2 Card=1 Byt

              es=23)

     

       2    1     INDEX (RANGE SCAN) OF 'DEPT_ID5' (NON-UNIQUE) (Cost=1 Ca

              rd=1)

     

     

     

     

     

    Statistics

    ----------------------------------------------------------

              0  recursive calls

              0  db block gets

              1  consistent gets

              0  physical reads

              0  redo size

            323  bytes sent via SQL*Net to client

            372  bytes received via SQL*Net from client

              1  SQL*Net roundtrips to/from client

              0  sorts (memory)

              0  sorts (disk)

              0  rows processed

     

    SQL>

     

    通过所有的statistics,我们可以清楚的看到,适当的使用索引会是性能提高几倍甚至更多。

    March 29

    Oracle下的通用分页存储过程

    问题:
     
    看了很多的分页程序,发现不是很通用,与具体的表结构关系比较紧密。自己写了一个分页的存储过
    程,还算通用。
     
    逻辑:
     
    第一层:SELECT选出所有的满足条件的行并排序(可根据参数来定义排序条件)。
    第二层:SELECT选出第一层SELECT中前N页的行数(内嵌临时表,子查询)。
    第三曾:SELECT选出第二层SELECT中第N页的行数(内嵌临时表,子查询)。
     
    注意:
     
    1、第一层和第二层选出的列应为该表的关键字列,以便在第三层中进行关联。示例中给出的是ID。
    2、最好还要按照ROW_NUM进行排序。
    3、一定是要有三层查询。不能在第一层中将ROWNUM选出。因为排序后ROWNUM顺序不对。
     

    附代码:

    CREATE OR REPLACE PROCEDURE SP_COMMON_PAGER(
                S_WHERE IN VARCHAR2,
                S_ORDER IN OUT VARCHAR2,
                I_CURRENT_PAGE IN OUT NUMBER,
                I_PAGE_RECORD IN OUT NUMBER,
                I_RECORD_COUNT OUT NUMBER,
                I_PAGE_COUNT OUT NUMBER,
                RTN_CURSOR OUT PAC_QUERY.REF_CURSOR)
      IS
        S_TABLE_NAME VARCHAR2(
    10);
      BEGIN
       
    --你的表名
        S_TABLE_NAME :=
    'T_TEST';
       
    --获取总行数
        OPEN RTN_CURSOR FOR
    'SELECT COUNT(' || S_TABLE_NAME || '.*) FROM ' || S_TABLE_NAME || ' WHERE ' || S_WHERE;
        FETCH RTN_CURSOR INTO I_RECORD_COUNT;
        CLOSE RTN_CURSOR;
        IF I_RECORD_COUNT >
    0 THEN
         
    --获得页数
          I_PAGE_COUNT := CEIL(I_RECORD_COUNT / I_PAGE_RECORD);
          IF I_CURRENT_PAGE > I_PAGE_COUNT THEN
             I_CURRENT_PAGE := I_PAGE_COUNT;
          END IF;
         
    --获取第N页的记录集
          OPEN RTN_CURSOR FOR
         
    'SELECT * ' || CHR(13) ||CHR(10) ||
         
    'FROM  ' || CHR(13) ||CHR(10) ||
         
    '( ' || CHR(13) ||CHR(10) ||
         
    '  SELECT ' || S_TABLE_NAME || '.ID,ROWNUM AS ROW_NUM ' || CHR(13) ||CHR(10) ||
         
    '  FROM ' || S_TABLE_NAME || ',' || CHR(13) ||CHR(10) ||
         
    '  (' || CHR(13) ||CHR(10) ||
         
    '   SELECT ' || S_TABLE_NAME || '.ID ' || CHR(13) ||CHR(10) ||
         
    '   FROM ' || S_TABLE_NAME || '  ' || CHR(13) ||CHR(10) ||
         
    '   WHERE ' || S_WHERE || CHR(13) ||CHR(10) ||
         
    '   ORDER BY ' || S_ORDER || CHR(13) ||CHR(10) ||
         
    '  ) T1 ' || CHR(13) ||CHR(10) ||
         
    '  WHERE ' || S_TABLE_NAME || '.ID = T1.ID AND ' || CHR(13) ||CHR(10) ||
         
    '        ROWNUM < ' || TO_CHAR(I_CURRENT_PAGE * I_PAGE_RECORD + 1) || CHR(13) ||CHR(10) ||
         
    ') TEMP JOIN ' || S_TABLE_NAME || ' ON ' || S_TABLE_NAME || '.ID = ' || ' TEMP.ID '  || CHR(13) ||CHR(10) ||
         
    'WHERE ROW_NUM > ' || TO_CHAR((I_CURRENT_PAGE - 1) * I_PAGE_RECORD) || CHR(13) ||CHR(10) ||
         
    '      ORDER BY ROW_NUM ';
          I_PAGE_COUNT :=
    1;
          OPEN RTN_CURSOR FOR
         
    ' SELECT * ' ||
         
    ' FROM ' || S_TABLE_NAME ||
         
    ' WHERE ROWNUM=0';
        END IF;
      END
    SP_COMMON_PAGER;

    March 18

    [原创]Microsoft Visual Studio .NET 2003调试时,CPU占用100%问题

    最近接手别人的一个程序,在调试时出现一个奇怪现象,就是一旦跟踪到断点,然后按F10逐句跟踪时,发现CPU占用达到100%。然后运行N长时间后终于到下一句。
    1、由于本系统需要跟别的系统接口,调用WEB SERVICE,当时以为是这个接口应用有BUG。因为在此之前,未添加与这个系统接口部分程序时,调试正常。
    但是在别的机器上调试同样程序时不存在这个问题。
    2、又怀疑本机安装的Rational有冲突。
    卸载后问题依然。
    3、重新安装.NET开发环境。
    问题依然。
    ............................................................
    再一次跟踪,突然发现断点有N多,逐一清除。重新调试,问题解决!
    1、在新安装的XP系统上调试,不存在这个问题。怀疑是与操作系统有关。本人系统是WIN 2003 SERVER(SP1),最新补丁。(.NET 2003环境一致)。【不知道算不算BUG】
    2、由于本程序由多个人开发,每个人都在自己所属模块添加断点,并都没有清除。导致断点众多。
     
    建议:保持良好的开发习惯,看好的程序是一种享受。不要产生一些垃圾。随时清除不必要的东西:包括断点、监视不能添加太多。
     
    附:
    将该程序放在一台HP服务器上(4*3.4G XEON,3.5G RAM,WIN2003 SERVER)上调试也存在这个问题,其中一个CPU占用100%,别的变化不大。内存变化不大。同时.NET开发工具不支持多CPU。看来现在的双核意义不大。
    March 13

    删除无效的乱码Oracle数据库对象

    问题描述:
    某日,偶用PL/SQL Developer(7.0.0.1050)的COMMAND WINDOW执行SQL。偶直接从记事本拷贝,结果拷贝进去时,出现乱码(不知道什么原因)。因此生成一堆无效的对象,并且对象名也是乱码,显示为“P_??_???”这样的形式。直接在PL/SQL Developer中删除,提示:ORA-00911:无效字符。
    解决过程:
    1。怀疑字符为不可显示字符,想将对象名取出,用动态SQL实现删除。
    --删除无效的对象
    CREATE OR REPLACE PROCEDURE P_DROP_ALL_USER_INVAID_OBJECTS
      IS 
      sObjectName USER_OBJECTS.OBJECT_NAME %TYPE;--对象名
      sObjectType USER_OBJECTS.OBJECT_TYPE %TYPE;--对象类型
      CURSOR cCursor IS 
      SELECT OBJECT_NAME,OBJECT_TYPE FROM USER_OBJECTS
      WHERE STATUS = 'INVALID';
    BEGIN
      --该节点的所有子节点找出来。
      OPEN cCursor;
      LOOP
        FETCH cCursor INTO sObjectName,sObjectType;
        EXIT WHEN cCursor%NOTFOUND;
        EXECUTE IMMEDIATE 'DROP ' || sObjectType || ' "' || sObjectName || '"';
      END LOOP;
      CLOSE cCursor;
    END P_DROP_ALL_USER_INVAID_OBJECTS;
    结果删除了函数和包为乱码的。对于存储过程的没有删除掉。
    2、觉得通过客户端工具不可以,就直接到服务器端删除。对于名字为乱码显示的依然不能删除,但是可以删除程序体内部有乱码的。
    3、最后,觉得死马当作活马医,直接按照显示名字删除。结果成功!
    BEGIN
        EXECUTE IMMEDIATE 'DROP PROCEDURE "P_???_???????"';
    END;
    注意:
    1、保证"?"个数与显示一致。
    2、"?"是英文下的,而非中文下的"?"。
    遗留问题: 
    1、偶的PL/SQL Developer拷贝到记事本总是乱码,与操作系统的字符集有什么关系,如何处理?
    2、ORACLE中是否可以根据对象的唯一ID来删除对象?
    3、为什么这些带问号的对象不能删除?
    March 12

    System.UnauthorizedAccessException: 拒绝访问和ORA-12638: 身份证明检索失败

    一、System.UnauthorizedAccessException: 拒绝访问
    当试图在ASP.NET里面使用COM对象的时候,常常出现"System.UnauthorizedAccessException: 拒绝访问"这个异常。解决办法:
    1、在web.config里面添加以下一行以解决这个问题:<identity impersonate="true" userName="YourAdminUsr" password="YourAdminPwd"/>。模拟指定用户身份运行COM。
    2、在运行里输入"dcomcnfg"(组件服务。也可以在管理工具中运行)设置COM组件的运行权限,将ASPNET帐户添加进去。比如用Excel做报表,那么在Microsoft Excel中设置权限。【组件服务-计算机-我的电脑-DCOM配置-Microsoft Excel 应用程序-属性-安全-启动和激活权限-自定义-添加用户ASPNET】
    二、ORA-12638: 身份证明检索失败
    1、程序-Oracle OraHome-Configuration and Migration Tools-Net Manager-本地-概要文件-ORACLE高级安全性-验证-去掉NTS
    2、或者直接打开network/admin下的sqlnet.ora,修改SQLNET.AUTHENTICATION _SERVICES=(NONE)。
    3、重新启动ORACLE(可能不需要)。
    4、原因:由于Oracle不能应用OS认证而导致凭证检索失败
    三、附
    1、问题一的错误信息:
    说明: 执行当前 Web 请求期间,出现未处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。
    异常详细信息: System.UnauthorizedAccessException: 拒绝访问。
    未授权此 ASP.NET 进程访问所请求的资源。出于安全原因,默认的 ASP.NET 进程标识为“{machinename}ASPNET”,它只具有有限的特权。请考虑授予该 ASP.NET 进程标识访问此资源的权限。
    若要授予 ASP.NET 对文件的写访问权,请在资源管理器中右击该文件,选择“属性”,然后选择“安全”选项卡。单击“添加”以添加“{machinename}ASPNET”用户。突出显示此 ASP.NET 帐户,在“允许”列中选中“写”框。
    2、与ASP.NET应用相关的几个帐号:ASPNET、SERVICE、NETWORK SERVICE。出现权限问题时,可以考虑在NTFS中把这几个帐号的权限添加一下。【运行ASP.NET应用的帐号可以这样查看:控制面板-管理工具-Internet 信息服务(IIS)管理器-本地计算机-应用程序池-DefaultPool(具体是哪个应用程序池,需要看你虚拟目录选择的应用程序池)-属性-标识-选择应用程序池的安全性帐户-预定义帐户-网络服务】
    3、本来这两个问题似乎不相关,但是之前我的ORACLE一直是正常工作的,发现出现第一个问题时,导致第二个问题出现。也不知道是否有关系,就放一起了。
    4、我的环境是:ORACLE9.2,WINDOWS2003,.NET FRAMEWORK1.1。数据库服务器和WEB服务器在同一机器上。
    March 11

    Create Oracle Database Links

    一、创建数据库链接
    1、使用SQL的方式:
    -- Drop existing database link
    drop database link FILEADIM;
    -- Create database link
    create database link FILEADIM
      connect to FILE_ADMIN identified by ADMIN
      using 'oraes';
    2、此外还有很多可视化工具,比如ORACLE的管理控制台,或者PL/SQL以及TOAD这样的工具都很方便。
    二、访问链接数据库表
    1、SELECT * FROM FILE_ADMIN.FILE_INFO@FILEADIM

    如果有全局名:SELECT * FROM FILE_ADMIN.FILE_INFO@FILEADIM.US.ORACLE.COM
    2、为方便使用,创建同义词。
    CREATE SYNONYM FILE_INFO FOR FILE_ADMIN.FILE_INFO@FILEADIM
    三、注意事项
    1、必须在您想创建链接数据库的数据库服务器上配置服务名,以指向要链接的数据库。
    2、如果创建好的链接名出现诸如:US.ORACLE.COM这样的后缀,是因为安装数据库时在init.ora文件中把GLOBAL_NAME的侄设成TRUE了
     

    October 06

    关于帮助的帮助

    遇到问题,总是要寻求答案或者解决办法。有个高人在身边,那自然是好(师傅带徒弟真是好哇)。囿于环境,偶身边就很少有这样的时候。唯一的办法就是找无声的老师:
     
    1、专门的搜索站点。偶最最常用的就是www.google.com了。偶尔用用www.baidu.com。偶的感觉是,目前百度与google相比还是嫩:百度主要集中在中文方面,如果想搜其他语种就勉为其难了。不过,偶常用百度搜MP3。呵呵,不过最近这哥们也被告了,准备掏钞票买单。嘿嘿,扯远了。优点:内容几乎无所不包,只要你想得到,基本就能找到;缺点:一般搜出来的东西太多,找起来也麻烦,有的页面还打不开(快照也不一定管用)。还有可能被搜索出的其他结果带到爪哇国去了。
     
    2、专门的论坛。首先看精华帖,或者FAQ贴;其次搜索历史(看是否有人提过同样的问题);自己提问。优点:比较有针对性;缺点:反应比较慢,同时容易跑去灌水。
     
    3、QQ群或者MSN组。物以类聚,人以群分。平时注意搜集一些强人的QQ或者MSN,说不定用上了。优点:交流比较直接;缺点:牛人一般比较忙或者不在线,容易哈拉哪个MM或者GG之类的。
     
    4、联机帮助。目录(您必须对问题的分类比较清楚,同时熟悉联机帮助的体系结构),这个比较系统;索引,输入关键字;搜索,和其他搜索没有什么区别。关于如何使用帮助,可以参看联机帮助中使用帮助的说明。优点:现在的帮助已经很全了,并且附有大量示例;缺点:只是介绍一般性和概念性知识。一些技巧性较强的东西可能没有。
     
    5、相关书籍。谁也不会随时随身带着砖头似的书,只有出恭或者安息的时候翻翻,说不定会在什么时候有用。优点:比较系统,全面;缺点:唉,现在的书太粗制滥造了,不想多说。
     
     
    排序一下:4,1,2,3,5。另外说明一下,请教他人,可以归纳到第三点中。
    August 14

    为SqlHelper增加UpdateDataTable方法

    //修改一下原来的AttachParameters方法
    private static void AttachParameters(SqlCommand command, SqlParameter[] commandParameters)
      {
       if( command == null ) throw new ArgumentNullException( "SqlCommand 为空." );
       if( commandParameters != null )
       {
        foreach (SqlParameter p in commandParameters)
        {
         if( p != null )
         {
          bool bAdded = false;
          foreach(SqlParameter param in command.Parameters)
          {
           //查看是否重复
           if(param.ParameterName == p.ParameterName)
           {
            bAdded = true;
            break;
           }
          }
          if(bAdded) break;
          // 检查参数的方向属性,当为输入/输入输出类型时,为值为null的参数设置值为DBNull.Value
          if (( p.Direction == ParameterDirection.InputOutput || p.Direction == ParameterDirection.Input ) && (p.Value == null))
          {
           p.Value = DBNull.Value;
          }
          command.Parameters.Add(p);
         }
        }
       }
      }
    //新增的UpdateDataTable
    /// <summary>
      /// 更新表
      /// </summary>
      /// <param name="connection">连接对象</param>
      /// <param name="dtTable">要更新的DataTable对象</param>
      /// <param name="tableName">要更新的数据库中的表名</param>
      /// <param name="updateCols">要更新或者插入的列</param>
      /// <param name="whereClauseCols">更新或者删除的条件列</param>
    public static void UpdateDataTable(SqlConnection connection,DataTable dtTable,string tableName,string[] updateCols,string[] whereClauseCols)
      {
       if( connection == null ) throw new ArgumentNullException( "连接对象不能为空!" );
       if( tableName == null || tableName.Length == 0 ) throw new ArgumentNullException( "要更新的表名不能为空!" );
       if( dtTable == null ) throw new ArgumentNullException( "要更新的表不能为空!" );
       if( whereClauseCols == null || whereClauseCols.Length == 0 ) throw new ArgumentNullException( "您必须至少提供一个列作为条件!" );
       //构造SQL语句   
       string whereSQL = " WHERE ";   
       for(int i = whereClauseCols.Length - 1;i >=0;i--)
       {
        if(i == 0)
        {
         whereSQL += whereClauseCols[i] + " = @" + whereClauseCols[i];
        }
        else
        {
         whereSQL += whereClauseCols[i] + " = @" + whereClauseCols[i] + " AND ";
        }
       }
       string updateSQL = "UPDATE " + tableName + " SET ";
       for(int i = updateCols.Length - 1;i >=0;i--)
       {
        if(i == 0)
        {
         updateSQL += updateCols[i] + " = @" + updateCols[i];
        }
        else
        {
         updateSQL += updateCols[i] + " = @" + updateCols[i] + ",";
        }
       }
       updateSQL += whereSQL;
       string insertSQL = "INSERT INTO " + tableName + "(";
       for(int i = updateCols.Length - 1;i >=0;i--)
       {
        if(i == 0)
        {
         insertSQL += updateCols[i] + ") Values(";
        }
        else
        {
         insertSQL += updateCols[i] + ",";
        }
       }
       for(int i = updateCols.Length - 1;i >=0;i--)
       {
        if(i == 0)
        {
         insertSQL += "@" + updateCols[i] + ")";
        }
        else
        {
         insertSQL += "@" + updateCols[i] + ",";
        }
       }
       string deleteSQL = "DELETE FROM " + tableName + whereSQL;
       //构造命令
       int icount = 0;
       SqlCommand updateCommand = new SqlCommand(updateSQL,connection);
       SqlHelper.AttachParameters(updateCommand,SqlHelperParameterCache.GetSqlParameterSet(connection,tableName,updateCols));
       icount = updateCommand.Parameters.Count;
       SqlHelper.AttachParameters(updateCommand,SqlHelperParameterCache.GetSqlParameterSet(connection,tableName,whereClauseCols));
       //
       icount = updateCommand.Parameters.Count;
       SqlCommand insertCommand = new SqlCommand(insertSQL,connection);
       SqlHelper.AttachParameters(insertCommand,SqlHelperParameterCache.GetSqlParameterSet(connection,tableName,updateCols));
       SqlCommand deleteCommand = new SqlCommand(deleteSQL,connection);
       SqlHelper.AttachParameters(deleteCommand,SqlHelperParameterCache.GetSqlParameterSet(connection,tableName,whereClauseCols));
       SqlDataAdapter dataAdapter = new SqlDataAdapter();
       // Set the data adapter commands
       dataAdapter.UpdateCommand = updateCommand;
       dataAdapter.InsertCommand = insertCommand;
       dataAdapter.DeleteCommand = deleteCommand;
       // Update the dataset changes in the data source
       dataAdapter.Update (dtTable);
       // Commit all the changes made to the DataTable
       dtTable.AcceptChanges();
      }
    //在SqlHelperParameterCache中增加新方法GetSqlParameterSet
    public static SqlParameter[] GetSqlParameterSet(SqlConnection connection,string tableName,params string[] tableCols)
      { 
       string hashKey = connection.ConnectionString + ":" + tableName;
       SqlParameter[] cachedParameters;
       cachedParameters = paramCache[hashKey] as SqlParameter[];
       if (cachedParameters == null)
       {
        string colAttribute  = "select A.name as NAME,A.length as LENGTH,A.Xprec as PREC,A.Xscale AS SCALE,B.Name AS TYPE from syscolumns A join systypes B on A.type = B.type AND A.Xtype = B.Xtype AND a.usertype = B.usertype and a.xusertype = B.xusertype where id = object_id('" + tableName + "')";
        DataSet ds = SqlHelper.ExecuteDataset(connection,CommandType.Text,colAttribute);
        if(ds.Tables[0].Rows.Count == 0) throw new ArgumentException("您提供的表名不存在!",tableName);
        SqlParameter[] sqlParameters = new SqlParameter[ds.Tables[0].Rows.Count];
        DataRow dr;
        for(int i = ds.Tables[0].Rows.Count - 1;i >= 0;i-- )
        {
         dr = ds.Tables[0].Rows[i];
         sqlParameters[i] = new SqlParameter("@" + dr["NAME"].ToString(),GetSqlTypeByName(dr["TYPE"].ToString()),Convert.ToInt32(dr["LENGTH"]),ParameterDirection.Input,true,Convert.ToByte(dr["PREC"]),Convert.ToByte(dr["SCALE"]),dr["NAME"].ToString(),DataRowVersion.Current,DBNull.Value);
        }
        paramCache[hashKey] = sqlParameters;
        cachedParameters = sqlParameters;
        ds.Tables[0].Dispose();
        ds.Dispose();
       }
       SqlParameter[] sqlparams = new SqlParameter[tableCols.Length];
       for(int i = tableCols.Length - 1;i >= 0;i --)
       {
        foreach(SqlParameter param in cachedParameters)
        {
         if(param.ParameterName == "@" + tableCols[i])
         {
          sqlparams[i] = param;
          break;
         }
        }
       }
       return CloneParameters(sqlparams);
    }
    //新增的通过名字获取SqlDataType类型
    private static SqlDbType GetSqlTypeByName(string sqlTypeName)
      {
       switch(sqlTypeName.ToLower())
       {
        case "bigint":
         return SqlDbType.BigInt;
        case "binary":
         return SqlDbType.Binary;
        case "bit":
         return SqlDbType.Bit;
        case "char":
         return SqlDbType.Char;
        case "datetime":
         return SqlDbType.DateTime;
        case "decimal":
         return SqlDbType.Decimal;
        case "float":
         return SqlDbType.Float;
        case "image":
         return SqlDbType.Image;
        case "int":
         return SqlDbType.Int;
        case "money":
         return SqlDbType.Money;
        case "nchar":
         return SqlDbType.NChar;
        case "ntext":
         return SqlDbType.NText;
        case "nvarchar":
         return SqlDbType.NVarChar;
        case "real":
         return SqlDbType.Real;
        case "smalldatetime":
         return SqlDbType.SmallDateTime;
        case "smallint":
         return SqlDbType.SmallInt;
        case "smallmoney":
         return SqlDbType.SmallMoney;
        case "text":
         return SqlDbType.Text;
        case "timestamp":
         return SqlDbType.Timestamp;
        case "tinyint":
         return SqlDbType.TinyInt;
        case "uniqueidentifier":
         return SqlDbType.UniqueIdentifier;
        case "varbinary":
         return SqlDbType.VarBinary;
        case "varchar":
         return SqlDbType.VarChar;
        case "variant":
         return SqlDbType.Variant;
        default:
         return SqlDbType.Int;
       }
       
      }

    组策略编辑器

    用了这么久的2003,关机时老是提示要关机原因。今天Google了一下,原来可以这样解决:
     
    开始--》运行:gpedit.msc
     
    计算机配置--》管理模板--》系统--》显示“关闭事件跟踪程序”。禁用它就好了。
    August 06

    神话在继续... ...

     

    北京时间8月5日23:40,百度在美NASDAQ上市交易,发行价27美元,开盘价为66美元,报收于122.54美元,涨幅353.85%。最低为60美元,最高151.21美元。

     

    公司创始人CEO李彦宏将售出个人3%的股份,共计25万股。但是IPO之后,他仍将持有公司22%的股份,按每股27美元计算,李彦宏仅百度的持股就已经价值1.963亿美元。而这个身价已足以令李彦宏荣登中国内地的百富榜。 如果按122美元计算,则近10亿美元。

     

    百度现有员工750名,公司上市成功后,按照目前5.5%的员工持股计算,百度上市将产生约200名百万富翁。

     

     

    August 04

    学习方法

    好久都没有看书了,无论是专业技术书还是不沾边的其它诸如诗词歌赋小说散文都已经很久没看了。最近新来了一些刚毕业的学生,看着他们捧着书,埋头苦学,心里也有了些许看书的冲动。

     

    记得刚来的时候,与领导讨论问题的时候,也是据书力争。可是经过这些年,总觉得实践中来得更快,并且印象更深。遇到问题,首先就是看看联机帮助,如果一时找不到,那么久上GOOGLE,对着问题找。通常会很快解决问题。在我现在看来,GOOGLE几乎是最好的帮助了。只要你想的到,就几乎都能查得到。有句话,叫作:只有想不到,没有做不到。的确,在这个网络的时代,只要你能想到的几乎任何一个奇怪的问题,到网络上看看,也许早就人已经想过了。

     

    不过,这也带来了一些问题,那就是每每问题解决之后,往往也就不深究了。因此,也就停留在技术层面和经验上。以后遇到同样的问题,可以解决,但是遇到别的问题就又要去搜索一番了。如果仅仅想做一个熟练工,只有也就可以了。但是要继续往下走,还是需要理论的指导。现在是遇到问题解决问题,没去找根源,只停留在形而下而没去追究形而上的问题。这样对于按进度完成任务应该是可以。但解决问题之后,现在越来越觉得有必要归纳总结,追根究底。

     

    从看书,到放弃看书,再到想看书,似乎又回到了起点,不过理解起来似乎有些不一样了。还是应了那句话:从实践(群众)中来,到实践(群众)中去。

    August 02

    Microsoft SQL Server 2005

    之前在MS站点上登记了一下,昨天MS如约的将光盘邮寄过来了。MS的站点上已经有关于2005特性的详细介绍,俺就不费口水了。不过令我惊讶的是对系统的要求:

    由于光盘是DVD,因此DVD光驱是少不了的了。最让俺惊讶的是对RAM的要求。最低要求是512M,推荐配置是1G,估计要达到优化的性能没个2G看来是不行了。IE也要求6.0SP1,并且分辨率要在1024*768以上。

    拿到手里的是英文版(晕,俺英语那么臭... ...)。俺的本本又是512M的内存,还要考虑一下要不要装... ...不过倒是很想看看SQL 2005到底是什么样子。尤其是俺还关注其中几个功能。

    无独有偶,昨天的《计算机世界》上看到IBM的DB2的广告:

    开放的DB2,帮您彻底摆脱现有信息的禁锢,畅行无阻

    开放的DB2,兼容各种服务器平台。无论您的系统运行UNIX,LINUX或WINDOWS,它都能自主监控、安全、稳定

    开放的DB2,采用业界领先的联邦数据库技术。无论您使用ORACLE、MICROSOFT SQL SERVER或SYBASE,它都能紧密集成,看上去就象一个单一的数据库,浏览查询省时、省力

    开放的DB2,支持各种开发架构。无论您采用J2EE或.NET,它都能权力协助,任设计思路随意驰骋。

    开放的DB2,... ...

    没有使用过DB2,印象中IBM也没有如此宣传过自己的这款产品。因为DB2作为高端产品,往往是跟IBM的操作系统和服务器绑定在一起,无须单独推销。IBM现今如此推广DB2,估计来自于MS的压力也是一个方面。当年为IBM打工的小弟,居然让昔日的老板感到了恐惧。

    记得有个MS的哥们,他说他认为最好的数据库是DB2,其次是MS SQL,最后是ORACLE。不知道号称数据库老大的ORACLE有何感想。(私下认为,ORACLE其最后的命运就是被收购,不是MS就是IBM,估计被IBM收购的可能更大)

    以前不时有报道,说我们国内由某某开发出了什么什么关系型数据库系统,甚至是面向对象的数据库系统。只是俺至今没有看到。

    就象CPU,咱就看到AMD和INTEL在打架。我们只有选择支持AMD或者INTEL。其实,我更想支持别的,可惜没有。以前还有一个Cyrix,被威胜收购之后,似乎还挣扎了一阵子,可惜现在好像也没多少人知道了。至于中芯国际,要走的路也很长。不过祝福它走好。