MSSQL数据库bit型取反

作者:freethy 发布于:2014-5-9 16:44 Friday

 

ABS(bit字段-1)

评论(0) 引用(0) 浏览(2109)

js里父页面与子页面的相互调用

作者:freethy 发布于:2014-5-9 16:43 Friday

一、在页面里用 open 打开的子页面:

1、子页面调用父页面的方法,包括子页面给父页面传值:  

   window.opener.methodName();

   window.opener.methodName(param1,param2);

2、父页面关闭子页面:在父页面 

   openWin=window.open("");

   function bClick(){

        openWin.close();

   }

 

二、在Iframe 的子页面里

   子页面给父页面的Div赋值

   parent.window.document.getElementByIdx_x('id').innerHTML='';

   调用父页面的方法

   window.parent.window.methodName();

   刷新父页面

   window.parent.location.reload();

 

 

open  打开的属性

 

window.open(pageURL,name,parameters)
其中:
pageURL 为子窗口路径
name 为子窗口句柄
parameters 为窗口参数(各参数用逗号分隔)

参数 | 取值范围 | 说明

alwaysLowered | yes/no | 指定窗口隐藏在所有窗口之后
alwaysRaised | yes/no | 指定窗口悬浮在所有窗口之上
depended | yes/no | 是否和父窗口同时关闭
directories | yes/no | Nav2和3的目录栏是否可见
height | pixel value | 窗口高度
hotkeys | yes/no | 在没菜单栏的窗口中设安全退出热键
innerHeight | pixel value | 窗口中文档的像素高度
innerWidth | pixel value | 窗口中文档的像素宽度
location | yes/no | 位置栏是否可见
menubar | yes/no | 菜单栏是否可见
outerHeight | pixel value | 设定窗口(包括装饰边框)的像素高度
outerWidth | pixel value | 设定窗口(包括装饰边框)的像素宽度
resizable | yes/no | 窗口大小是否可调整
screenX | pixel value | 窗口距屏幕左边界的像素长度
screenY | pixel value | 窗口距屏幕上边界的像素长度
scrollbars | yes/no | 窗口是否可有滚动栏
titlebar | yes/no | 窗口题目栏是否可见
toolbar | yes/no | 窗口工具栏是否可见
Width | pixel value | 窗口的像素宽度
z-look | yes/no | 窗口被激活后是否浮在其它窗口之上

评论(0) 引用(0) 浏览(1562)

江苏各城市电信DNS地址

作者:freethy 发布于:2014-4-9 9:52 Wednesday

地市   主用DNS 备用DNS 
南京 218.2.135.1 61.147.37.1
苏州 61.177.7.1 221.228.255.1
无锡   221.228.255.1 218.2.135.1
常州  221.228.255.1 218.2.135.1
镇江 218.2.135.1 61.147.37.1
扬州 61.147.37.1 61.177.7.1
南通 61.147.37.1 61.177.7.1
泰州 221.228.255.1 218.2.135.1
徐州 61.147.37.1 61.177.7.1
淮安 61.147.37.1 61.177.7.1
盐城 61.177.7.1 221.228.255.1
连云港 218.2.135.1 61.147.37.1
宿迁 61.177.7.1 221.228.255.1

 

江苏总共四台DNS服务器,南京:218.2.135.1    苏州:61.177.7.1    无锡:221.228.255.1    徐州:61.147.37.1

评论(0) 引用(0) 浏览(2085)

p​h​p​M​y​A​d​m​i​n​错​误​ ​缺​少​ ​m​y​s​q​l​i​ ​扩​展

作者:freethy 发布于:2014-4-8 18:11 Tuesday

phpMyAdmin错误 缺少 mysqli 扩展。请检查 PHP 配置 的解决方案

phpMyAdmin 缺少 mysqli 扩展。请检查 PHP 配置 的解决方案:
缺少 mysqli 扩展。请检查 PHP 配置。

打开你的php.ini->一般在C:WINDOWS目录下。
找到

 代码如下

复制代码

;extension=php_msql.dll
;extension=php_mssql.dll
extension=php_mysql.dll
extension=php_mysqli.dll

 

需要开启哪个扩展,就把这一行前面的分号去掉就行,注意要重启Apache 或者IIS


解决步骤:
1.看看php的ext目录中是否有php_mysqli.dll文件
如果有,继续下面的步骤。没有的话重新装个php 
2.打开php.ini,找到

 代码如下

复制代码

;extension=php_mysqli.dll

把前面的分号";"去掉,改为

 代码如下

复制代码

extension=php_mysqli.dll

3.找到

 代码如下

复制代码

;extension_dir="./ext"

把前面的分号";"去掉,改为(相对于php的安装路径的ext目录)
extension_dir = "e:\php\ext"
e:php为php的存放目录

4.把php.ini拷贝到c:windows下

5.把libmysql.dll复制到Windows下面的System32目录(这个很关键)

6.重新启动IIS(不是网站)

cmd下运行 iisreset /RESTART 

如果上面方法还不行,可参考下面方法操作

第一步,输出 phpinfo() 看看 MySQL 模块是否成功装载,如果成功装载说明你见鬼了,否则看第二步,

IIS下phpinfo()无mysql模块是何原因?
原先用的php是.msi安装的,后来全部重来了一次,换成压缩包形式安装的。
又照着配置了php.ini,复制到windows,复制libmysql.dll到system32。
之后测试成功。。。
第二步,查看 extension_dir 的设置是否正确
第三步,查看是否把 libmysql.dll 文件复制到 c:windowssystem32 下

1、没有正确安装Mysql数据库,在系统服务中Mysql相关的服务没有启动   (请查看正确安装Mysql的方法)


2、在系统的 system32(C:windowssystem32) 目录下缺少 libmysql.dll文件,解决方法是找到php目录下的libmysql.dll,并将libmysql.dll复制到C:windowssystem32目录中,然后重新启动Web服务。


3、在C:windows目录下的php.ini文件中,没有将“;extension=php_mysql.dll”中的前面一个“;”去掉,所以不能使用相应功能,解决方法是打开php.ini文件


4、Mysql目录没有读取权限,正确的目录权限如下:
administrator   完全控制
system    完全控制
user    读取加运行
其他的用户权限全部删除(也可保留,但安全性不高,建议删除),然后重启MYsql服务和Web服务。

 

评论(0) 引用(0) 浏览(1442)

mssql和sqlite关于if not exists 的写法

作者:freethy 发布于:2014-3-31 8:52 Monday

mssql语法:

if not exists (SELECT 1 FROM [t_Table] where [fName] = '张三')
insert into [t_Table] ([fName]) values ('张三'); 
sqlite语法:

insert into [t_Table] ([fName]) select '张三'
where not exists (SELECT 1 FROM [t_Table] where [fName] = '张三'); 

评论(0) 引用(0) 浏览(1645)

用javascript获取屏幕高度和宽度等信息

作者:freethy 发布于:2014-1-15 10:15 Wednesday

网页可见区域宽: document.body.clientWidth
网页可见区域高: document.body.clientHeight
网页可见区域宽: document.body.offsetWidth (包括边线的宽)
网页可见区域高: document.body.offsetHeight (包括边线的高)
网页正文全文宽: document.body.scrollWidth
网页正文全文高: document.body.scrollHeight
网页被卷去的高: document.body.scrollTop
网页被卷去的左: document.body.scrollLeft
网页正文部分上: window.screenTop
网页正文部分左: window.screenLeft
屏幕分辨率的高: window.screen.height
屏幕分辨率的宽: window.screen.width
屏幕可用工作区高度: window.screen.availHeight
屏幕可用工作区宽度: window.screen.availWidth

 

<script language="javascript">
function screenInfo(){
    var  s = "";
   s += "\r\n网页可见区域宽:"+ document.body.clientWidth;
   s += "\r\n网页可见区域高:"+ document.body.clientHeight;
   s += "\r\n网页可见区域宽:"+ document.body.offsetWidth  +" (包括边线的宽)";
   s += "\r\n网页可见区域高:"+ document.body.offsetHeight +" (包括边线的宽)";
   s += "\r\n网页正文全文宽:"+ document.body.scrollWidth;
   s += "\r\n网页正文全文高:"+ document.body.scrollHeight;
   s += "\r\n网页被卷去的高:"+ document.body.scrollTop;
   s += "\r\n网页被卷去的左:"+ document.body.scrollLeft;
   s += "\r\n网页正文部分上:"+ window.screenTop;
   s += "\r\n网页正文部分左:"+ window.screenLeft;
   s += "\r\n屏幕分辨率的高:"+ window.screen.height;
   s += "\r\n屏幕分辨率的宽:"+ window.screen.width;
   s += "\r\n屏幕可用工作区高度:"+ window.screen.availHeight;
   s += "\r\n屏幕可用工作区宽度:"+ window.screen.availWidth;
   alert(s);
}
</script>

评论(0) 引用(0) 浏览(1448)

C#正则表达式匹配HTML中的图片路径,图片地址

作者:freethy 发布于:2013-11-22 15:41 Friday

C#正则表达式匹配HTML中的图片路径图片地址

       最近的项目中有个关于网页取图的功能需要我自己开发,那就是用正则表达式来匹配图片标签。
      一般来说一个 HTML 文档有很多标签,比如“<html>”、“<body>”、“<table>”等,想把文档中的 img 标签提取出来并不是一件容易的事。由于 img 标签样式变化多端,使提取的时候用程序寻找并不容易。于是想要寻找它们就必须写一个非常健全的正则表达式,不然有可能会找得不全,或者找出来的不是正确的 img 标签
我们可以从 HTML 标签的格式去想应该怎么建这个正则表达式。首先要想一下 img 标签有几种写法,忽略大小写不看的话,下面列出 img 标签可能出现的几种情况。
<img> <img/> <img src=/>
这一些标签不用考虑,因为没有图片资源地址。

<img src = /images/pic.jpg/ > <img src =" /images/pic.jpg" > <img src= '/images/pic.jpg ' / >
这 一些标签都有图片资源地址,另外还有一个特点就是有引号对,可能为单引号,也可能为双引号。因为不需要同时匹配引号对,所以正则表达式可以这么 写:@"<img\s*src\s*=\s*[""']?\s*(?[^\s""'<>]*)\s*/?\s*>"

<img width="320" height="240" src=/images/pic.jpg onclick="window.open('/images/pic.jpg')">
因为 img 和 src 之间可能会有其他的参数,所以“<img”要有个单词结束,比如说不能是“<imgabc”,同样 src 前面也是一样,使用单词结束符“\b”有一个好处就是省去了表示空格的“\s*”。另外由于 img 标签中不可以出现“<”、“>”这样的符号,所以要改写前面的正则表达式:@"<img\b[^<>]*?\bsrc\s*=\s*[""']?\s*(?<imgUrl>[^\s""'<>]*)[^<>]*?/?\s*>"

<img width="320" height="240" src = "
/images/pic.jpg" />
像这种可能会用回车符折行的问题有时候会出现,所以在有空格分开的地方要包含回车换行和 TAB 字符,另外在图片地址中不能出现空格、TAB、回车和换行字符。所以上面的正则表达式可 以改成:@"<img\b[^<>]*?\bsrc[\s\t\r\n]*=[\s\t\r\n]*[""']?[\s\t\r \n]*(?<imgUrl>[^\s\t\r\n""'<>]*)[^<>]*?/?[\s\t\r\n]*>"

下面写出取得HTML中所有图片地址的静态方法。

 

/// <summary>         /// 取得HTML中所有图片的 URL。
        /// </summary>         /// <param name="sHtmlText">HTML代码</param>         /// <returns>图片的URL列表</returns>         public static string[] GetHtmlImageUrlList(string sHtmlText)
        {
            // 定义正则表达式用来匹配 img 标签             Regex regImg = new Regex(@"<img\b[^<>]*?\bsrc[\s\t\r\n]*=[\s\t\r\n]*[""']?[\s\t\r\n]*(?<imgUrl>[^\s\t\r\n""'<>]*)[^<>]*?/?[\s\t\r\n]*>", RegexOptions.IgnoreCase);

            // 搜索匹配的字符串             MatchCollection matches = regImg.Matches(sHtmlText);

            int i = 0;
            string[] sUrlList = new string[matches.Count];

            // 取得匹配项列表             foreach (Match match in matches)
                sUrlList[i++] = match.Groups["imgUrl"].Value;

            return sUrlList;
        }

评论(0) 引用(0) 浏览(1862)

c#产生随机字符串

作者:freethy 发布于:2013-5-25 9:32 Saturday



        /// <summary>
        /// 获取随机字符串
        /// </summary>
        /// <param name="len">随机字符串长度</param>
        /// <param name="type">随机字符串模式 1.随机数字0-9  2.随机0-9A-B  其他.随机0-9A-Ba-b</param>
        /// <returns></returns>
        public string GetRandom(int len,int type)
        {
            StringBuilder sb = new StringBuilder();
            string temp = string.Empty;
            if (type == 1)
            {
                temp = "0123456789";
            }
            else if (type == 2)
            {
                temp = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            }
            else
            {
                temp = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
            }
           

            for (int i = 0; i < len; i++)
            {
                int num = new Random(Guid.NewGuid().GetHashCode()).Next(0, temp.Length);
                sb.Append(temp[num]);
            }
            return sb.ToString();
        }

评论(2109) 引用(0) 浏览(26876)

JQuery筛选器全系列

作者:freethy 发布于:2013-5-13 9:26 Monday

JQuery筛选器2010-05-24 10:41:20.0有508人喜欢这篇文章jQuery提供了强大的选择器让我们获取对象。在这边,我人为地将jQuery选择器分为两大部分:选择对象和筛选条件。选择对象表示要获取什么对象,筛选条件是对获取的对象进行筛选,最终留下符合某些特征的对象。

1.选择对象

1).基本

·#id 根据给定的ID匹配一个元素。例如:$("#id")
·element 根据给定的元素名匹配所有元素。例如:$("div")
·.class 根据给定的类匹配元素。例如:$(".style1");
·* 匹配所有元素。例如:$("*")
·selector1,selector2,selectorN 将每一个选择器匹配到的元素合并后一起返回。例如:$("#id,div,.style1")

2).表单

·:button 匹配所有按钮。例如:$(":button")
·:checkbox 匹配所有复选框。例如:$(":checkbox")
·:file 匹配所有文件域。例如:$(":File")
·:hidden 匹配所有不可见元素,或者type为hidden的元素。例如:$("input:hidden")
·:image 匹配所有图像域。例如:$(":image")
·:input 匹配所有 input, textarea, select 和 button 元素。例如:$(":input")
·:password 匹配所有密码框。例如:$(":password")
·:radio 匹配所有单选按钮。例如:$(":radio")
·:reset 匹配所有重置按钮。例如:$(":reset")
·:submit 匹配所有提交按钮。例如:$(":submit")
·:text 匹配所有的单行文本框。例如:$(":text")
·:header 匹配如 h1, h2, h3之类的标题元素。例如:$(":header").css("background", "#EEE");

2.筛选条件

1).属性筛选

·[attribute*=value] 匹配给定的属性是以包含某些值的元素。例如:$("input[name*='man'")
·[attribute!=value] 匹配所有含有指定的属性,但属性不等于特定值的元素。例如:$(input[name!='man');
·[attribute$=value] 匹配给定的属性是以某些值结尾的元素。例如:$("input[name$='man']")
·[attribute=value] 匹配给定的属性是某个特定值的元素。例如:$("input[name='man']");
·[attribute] 匹配包含给定属性的元素。例如:$("div[id]")
·[attribute^=value] 匹配给定的属性是以某些值开始的元素。例如:$("input[name^='man']")
·[selector1][selector2][selectorN] 同时满足多个条件。例如:$("input[id][name$='man']")
·:hidden 匹配所有的不可见元素。例如:$("tr:hidden")
·:visible 匹配所有的可见元素。例如:$("tr:visible")
·:checked 匹配所有选中的被选中元素(复选框、单选框等,不包括select中的option)。例如:$("input:checked")
·:disabled 匹配所有不可用元素。例如:$("input:disabled")
·:enabled 匹配所有可用元素。例如:$("input:enabled")
·:selected 匹配所有选中的option元素。例如:$("select option:selected")

2).内容筛选

·:contains(text) 匹配包含给定文本的元素。例如:$("div:contains('John')")
·:empty 匹配所有不包含子元素或者文本的空元素。例如:$("td:empty")
·:has(selector) 匹配含有选择器所匹配的元素的元素。例如:$("div:has(p)");
·:parent 匹配含有子元素或者文本的元素。例如:$("td:parent")

3).层级筛选

·ancestor descendant 在给定的祖先元素下匹配所有的后代元素。例如:$("form input")
·parent > child 在给定的父元素下匹配所有的子元素。例如:$("form > input")
·prev + next 匹配所有紧接在 prev 元素后的 next 元素。例如:$("label + input")
·prev ~ siblings 匹配 prev 元素之后的所有 siblings 元素。例如:$("form ~ input")
·:first-child 匹配第一个子元素。例如:$("ul li:first-child")
·:last-child 匹配最后一个子元素。例如:$("ul li:last-child")
·:nth-child(index/even/odd/equation) 匹配其父元素下的第N个子或奇偶元素。例如:$("ul li:nth-child(2)")
·:only-child 如果某个元素是父元素中唯一的子元素,那将会被匹配。例如:$("ul li:only-child")

4).方法筛选

·:animated 匹配所有正在执行动画效果的元素。例如:$("div:animated");
·:eq(index) 匹配一个给定索引值的元素。例如:$("tr:eq(1)")
·:even 匹配所有索引值为偶数的元素,从 0 开始计数。例如:$("tr:even")
·:first 匹配找到的第一个元素。例如:$("tr:first")
·:gt(index) 匹配所有大于给定索引值的元素,从 0 开始计数。例如:$("tr:gt(0)")
·:last 匹配找到的最后一个元素。例如:$("tr:last")
·:lt(index) 匹配所有小于给定索引值的元素。例如:$("tr:lt(2)")
·:not(selector) 去除所有与给定选择器匹配的元素。例如:$("input:not(:checked)")
·:odd 匹配所有索引值为奇数的元素,从 0 开始计数。例如:$("tr:odd") jQuery

文章作者:POPO4J
本文地址:http://www.popo4j.com/article/JQuery-Filter.html
版权所有 © 转载时必须以链接形式注明作者和原始出处!

评论(245) 引用(0) 浏览(12296)

关于数据库设计中主键问题的思考

作者:freethy 发布于:2013-5-4 20:57 Saturday

关于数据库设计中主键问题的思考

文章摘要:数据库主键在数据库中占有重要地位。主键的选取策略决定了系统是否可靠、易用、高效。本文探讨了数据库设计过程当中常见的主键选取策略,并剖析了其做主键的优缺点,提出了相应的解决问题的方法。

关键字:自增标识  GUID  COMB

在基于关系型数据库设计时候,通常要为每张表指定一个主键,所谓主键就是能够唯一标识表中某一行记录的属性或属性组,一个表只能有一个主键,但可以有多个候选索引。因为主键可以唯一标识某一行记录,所以可以确保执行数据更新、删除、修改时不出现错误。当然,其它字段可以辅助我们在执行这些操作时消除共享冲突,不是本文讨论的重点,不再赘述。主键除了上述作用外,常常与外键构成参照完整性约束,防止出现数据不一致。所以数据库在设计时,主键起到了很重要的作用。常见的数据库主键选取方式有: 自动增长式、手动增长式 、UniqueIdentifier、联合式(复合式)、时间序列+随机数式、“COMBCombine)”类型。

一、自动增长式 

很多数据库设计者喜欢使用自动增长型字段,因为它使用简单。自动增长式允许我们在向数据库添加数据时,不考虑主键的取值,记录插入后,数据库系统会自动为其分配一个值,确保绝对不会出现重复。如果使用SQL Server数据库的话,我们还可以在记录插入后使用@@IDENTITY全局变量获取系统分配的主键值。 

尽管自动增长式字段会省掉我们很多繁琐的工作,但使用它也存在潜在的问题,那就是在数据缓冲模式下,很难预先填写主键与外键的值。假设有主辅两张表: 

Order(OrderID, OrderDate)  订单表

OrderDetial(OrderID, LineNum, ProductID, Price)  订单明细表

Order 表中的OrderID是自动增长型的字段。假设现在需要我们录入一张订单,包括在Order表中插入一条记录以及在OrderDetail表中插入若干条记录。因为Order表中的OrderID是自动增长型的字段,那么我们在记录正式插入到数据库之前无法事先得知它的取值,只有在更新后才能知道数据库为它分配的是什么值。这会造成以下矛盾发生: 

首先,为了能在OrderDetailOrderID字段中添入正确的值,必须先更新 Order表以获取到系统为其分配的OrderID值,然后再用这个OrderID填充OrderDetail表的OrderID列。最后更新OderDetail表。但是,为了确保数据的一致性,OrderOrderDetail在更新时必须在事务模式下进行的,即要么两张表同时同时更新成功、要么全部失败,显然它们是相互矛盾的。 

 其次,当我们需要在多个数据库间进行数据的复制时(SQL Server的数据分发、订阅机制允许我们进行库间的数据复制操作),自动增长式字段可能造成数据合并时的主键冲突及表关联关系的丢失。设想一个数据库中的Order表向另一个库中的Order表复制数据库时,OrderID到底该不该自动增长呢?如果自动增长,其子表OrderDetial的关联关系会丢失,如果不增长就会和现有数据主键重复,是不是很矛盾呢? 

再次,自增量的值都是需要在系统中维护一个全局的数据值,每次插入数据时即对此次值进行增量取值。当在产生唯一标识的并发环境中,每次的增量取值都必须为此全局值加锁解锁以保证增量的唯一性。造成并发瓶颈,降低查询性能。

    还有当数据表足够大或频繁的更改和插入操作导致主键类型值超出范围,这种情况一般很少碰到,但也是我们进行数据表设计时必须考虑的一个问题

二、手动增长型字段 

既然自动增长型字段会带来如此的麻烦,我们不妨考虑使用手动增长型的字段,也就是说主键的值需要自己维护,通常情况下需要建立一张单独的表存储当前主键键值。为了叙述上的方便仍然利用上面的例子进行阐述,新建一张表叫IntKey,包含两个字段,KeyName以及KeyValue。就像一个HashTable,给一个KeyName,就可以知道目前的KeyValue是什么,然后手工实现键值数据递增。在SQL Server中可以编写这样一个存储过程,让取键值的过程自动进行。代码如下: 

CREATE PROCEDURE [GetKey] 

@KeyName char(10), 

@KeyValue int OUTPUT 

AS 

UPDATE IntKey SET @KeyValue = KeyValue = KeyValue + 1 WHERE KeyName = @KeyName 

GO 

这样,通过调用存储过程,我们可以获得最新键值,确保不会出现重复。若将OrderID字段设置为手动增长式字段,我们的程序可以由以下几步来实现:首先调用存储过程,获得一个OrderID,然后使用这个OrderID填充Order表与OrderDetail表,最后在事务机制下对两表进行更新。 

使用手动增长式字段作为主键在进行数据库间数据复制时,可以确保数据合并过程中不会出现键值冲突,只要为不同的数据表分配不同的主键取值段就行了。但是,使用手动增长型字段会增加网络的负担,必须通过增加一次数据库访问来获取当前主键键值,这会增加网络和数据库的负载,当处于一个低速或断开的网络环境中时,这种做法会有很大的弊端。同时,手工维护主键还要考虑并发冲突等种种因素,这更会增加系统的复杂程度。 

三、使用UniqueIdentifier 

SQL Server为我们提供了UniqueIdentifier数据类型,并提供了一个生成函数NEWID( ),使用NEWID( )可以生成一个唯一的UniqueIdentifierUniqueIdentifier在数据库中占用16个字节,出现重复的概率几乎为0,号称全球唯一标识。我们经常从注册表或WINDOWS程序出现错误需要调试时看到类似 768427bf-9b37-4776-97ca-000365e160d5{45F0EB02-0727-4F2E-AAB5-E8AEDEE0CEC5} 的东西实际上就是一个UniqueIdentifierWindows用它来做COM组件以及接口的标识,防止出现重复。在.NET中 UniqueIdentifier称之为GUIDGlobal Unique Identifier)。在C#中可以使用如下命令生成一个GUID: 

Guid u = System.Guid.NewGuid(); 

对于上面提到的OrderOrderDetail的程序,如果选用UniqueIdentifier作为主键的话,我们完全可以避免上面提到的增加网络RoundTrip的问题。通过程序直接生成GUID填充主键,不用考虑是否会出现重复。 但是UniqueIdentifier 字段也存在严重的缺陷:首先,它的长度是16字节,是整数的4倍长,会占用大量存储空间。更为严重的是,UniqueIdentifier的生成毫无规律可言,也就是说是无序的,要想在上面建立索引(绝大多数数据库在主键上都有索引)是一个非常耗时的操作。有人做过实验,当数据表记录比较大的时,在不同的数据量级别上插入同样的数据量,使用 UniqueIdentifier型数据做主键要比使用Integer型数据慢,且还没有考虑到表关联的情况,出于效率考虑,尽可能避免使用UniqueIdentifier型数据库作为主键值,但随着现代计算机计算速度越来越快,在中小型项目中使用UniqueIdentifier式主键也是一个选项。 

四、使用业务字段联合主键

    基于DEPHIPOWERBUILDER等数据库工具开发C/S系统的数据库设计人员,习惯上用有业务意义的字段组合成复合主键做数据表主键。使用业务主键当然有其与生俱来的好处,一般情况下数据库系统会在默认条件下建立聚簇索引,而且这个聚簇索引基于主键升序排列,当数据量比较小时,我们感觉不到这种差别,当数据量比较大时,这种基于主键定义的聚簇索引的优势就显现出来,这就使得数据表在每次存取数据时按照索引准确确认数据插入或更新的磁盘物理位置,减少磁头寻址时间,从而提高数据库性能,而且能够从业务意义上保证数据的完整性,增加程序的可靠性。但是基于业务字段的联合索引,当业务字段选用比较多时会占用比较多的磁盘空间,而且索引页会占用更多的内存页面,从而导致查询命中率降低;另外使用业务主键,当涉及到主键数据的修改时,要在编程过程中记录新值和原值的关系表,在更新时又要进行新值和原值的比对,增加编写程序的复杂度。

五、时间序列+随机数主键

采用精确到毫秒甚至钠秒级的时间和一个随机产生的两位数做主键,如200911282311528+两位随机数,不失为解决主键问题的一个有效办法。这样产生的主键既避免了UniqueIdentifier型字段做主键时的无序,又能有效避免自动增长型主键带来的诸如复制和数据导入的麻烦。但在使用用户众多的网络实时系统中,在时间和空间上仍然不能保证唯一性的问题。

六、使用“COMBCombine)”类型 

既然上面五种主键类型选取策略都存在各自的缺点,那么到底有没有好的办法加以解决呢?答案是肯定的。通过使用COMB类型(数据库中没有COMB类型,它是Jimmy Nilsson在他的“The Cost of GUIDs as Primary Keys”一文中设计出来的),可以在以上众多的主键策略之间采用中庸之道,找到一个很好的平衡点。 

COMB数据类型的基本设计思路是这样的:既然UniqueIdentifier数据因毫无规律可言造成索引效率低下,影响了系统的性能,那么我们能不能通过组合的方式,保留UniqueIdentifier的前10个字节,用后6个字节表示GUID生成的时间(DateTime),这样我们将时间信息与 UniqueIdentifier组合起来,在保留UniqueIdentifier的唯一性的同时增加了有序性,以此来提高索引效率。也许有人会担心 UniqueIdentifier减少到10字节会造成数据出现重复,其实不用担心,后6字节的时间精度可以达到1/300秒,两个COMB类型数据完全相同的可能性是在这1/300秒内生成的两个GUID10个字节完全相同,这几乎是不可能的!在SQL Server中用SQL命令将这一思路实现出来便是: 

DECLARE @aGuid UNIQUEIDENTIFIER 

SET @aGuid = CAST(CAST(NEWID() AS BINARY(10)) 

+ CAST(GETDATE() AS BINARY(6)) AS UNIQUEIDENTIFIER) 

经过测试,使用COMB做主键比使用INT做主键,在检索、插入、更新、删除等操作上仍然显慢,但比Unidentifier类型要快上一些。除了使用存储过程实现COMB数据外,我们也可以使用C#生成COMB数据,这样所有主键生成工作可以在客户端完成。C#代码如下: 

//================================================ 

/**////<summary> 

/// 返回 GUID 用于数据库操作,特定的时间代码可以提高检索效率 

/// </summary> 

/// <returns>COMB (GUID 与时间混合型类型 GUID 数据</returns> 

public static Guid NewComb() 

byte[] guidArray = System.Guid.NewGuid().ToByteArray(); 

DateTime baseDate = new DateTime(1900,1,1); 

DateTime now = DateTime.Now; 

// Get the days and milliseconds which will be used to build the byte string 

TimeSpan days = new TimeSpan(now.Ticks - baseDate.Ticks); 

TimeSpan msecs = new TimeSpan(now.Ticks - (new DateTime(now.Year, now.Month, now.Day).Ticks)); 

// Convert to a byte array 

// Note that SQL Server is accurate to 1/300th of a millisecond so we divide by 3.333333 

byte[] daysArray = BitConverter.GetBytes(days.Days); 

byte[] msecsArray = BitConverter.GetBytes((long)(msecs.TotalMilliseconds/3.333333)); 

// Reverse the bytes to match SQL Servers ordering 

Array.Reverse(daysArray); 

Array.Reverse(msecsArray); 

// Copy the bytes into the guid 

Array.Copy(daysArray, daysArray.Length - 2, guidArray, guidArray.Length - 6, 2); 

Array.Copy(msecsArray, msecsArray.Length - 4, guidArray, guidArray.Length - 4, 4); 

return new System.Guid(guidArray); 

//================================================ 

/**//// <summary> 

/// 从 SQL SERVER 返回的 GUID 中生成时间信息 

/// </summary> 

/// <param name="guid">包含时间信息的 COMB </param> 

/// <returns>时间</returns> 

public static DateTime GetDateFromComb(System.Guid guid) 

DateTime baseDate = new DateTime(1900,1,1); 

byte[] daysArray = new byte[4]; 

byte[] msecsArray = new byte[4]; 

byte[] guidArray = guid.ToByteArray(); 

// Copy the date parts of the guid to the respective byte arrays. 

Array.Copy(guidArray, guidArray.Length - 6, daysArray, 2, 2); 

Array.Copy(guidArray, guidArray.Length - 4, msecsArray, 0, 4); 

// Reverse the arrays to put them into the appropriate order 

Array.Reverse(daysArray); 

Array.Reverse(msecsArray); 

// Convert the bytes to ints 

int days = BitConverter.ToInt32(daysArray, 0); 

int msecs = BitConverter.ToInt32(msecsArray, 0); 

DateTime date = baseDate.AddDays(days); 

date = date.AddMilliseconds(msecs * 3.333333); 

return date; 

    纵上述六种主键选取策略,笔者认为使用“COMBCombine)”类型做主键是比较恰当的主键应用策略,但在实际使用过程中要根据客观实践、因时因事选取适当的主键,切不可生搬硬套、弄巧成拙。

参考文献:

1、《系统分析师教程》 张友生 主编

2《中文版SQL Server 2000开发与管理应用实例》 邹建 主编

3、《数据库中使用自增量字段与Guid字段主键的性能对比》作者 不详

4、《小议数据库主键选取策略》 作者 不详

评论(0) 引用(0) 浏览(2894)

Powered by emlog 苏ICP备11068260号-3