RDIFramework.NET » :: 数据库精典 http://blog.rdiframework.net  ——  专注、交流、分享、成长 Fri, 20 Apr 2018 09:38:35 +0000 zh-CN hourly 1 https://wordpress.org/?v=4.2.20 SQLServer特殊字符/生僻字与varchar http://blog.rdiframework.net/1034.html http://blog.rdiframework.net/1034.html#comments Fri, 17 Nov 2017 02:46:33 +0000 http://blog.rdiframework.net/?p=1034 SqlServer

 

 

 

 

 

 

 

 

 

对于中文版的SQL SERVER,默认安装后使用的默认排序规则为Chinese_PRC_CI_AS,在此排序规则下,使用varchar类型来可以“正常存取”存放中文字符以及一些东南亚国家的字符,同时varchar类型在存放英文字符和数字时比nvarchar节省一半的存储空间,因此很多DBA都习惯使用varchar类型来存放字符数据,但这样便存在一些乱码隐患!
首先是特殊字符如上下标或版权字符,测试Code如下:

--准备测试表
DROP TABLE TB1
GO
CREATE TABLE TB1 
(
C1 VARCHAR(200),
C2 NVARCHAR(200)
)
GO
--插入测试数据
INSERT INTO TB1(C1,C2)
SELECT N'm²',N'm²'
UNION
SELECT N'®',N'®'
--查询
SELECT C1,
CAST(C1 AS NVARCHAR(200)) AS C1_N,
C2,
CAST(C2 AS VARCHAR(200)) AS C2_V
FROM  TB1

测试结果如下:

可以明显地看到上标在varchar类型下转换成普通数字2,而版权符号在varchar类型下直接就乱码。

对于这些特殊字符,可能不会被使用到,比如用户姓名字段,那么是不是就可以使用varchar类型了呢?

当然不是,能避开特殊字符,还得考虑“有文化的父母”给子女来点生僻字以展示有文化!!!比如五代十国中南汉的创建者刘䶮就自认为很牛叉,于是自己创了一个“䶮”字,取意为飞龙在天,如此牛叉的意义就不招varchar的“喜欢”,测试code如下:测试结果如下:

 

可以明显地看到上标在varchar类型下转换成普通数字2,而版权符号在varchar类型下直接就乱码。

对于这些特殊字符,可能不会被使用到,比如用户姓名字段,那么是不是就可以使用varchar类型了呢?

当然不是,能避开特殊字符,还得考虑“有文化的父母”给子女来点生僻字以展示有文化!!!比如五代十国中南汉的创建者刘䶮就自认为很牛叉,于是自己创了一个“䶮”字,取意为飞龙在天,如此牛叉的意义就不招varchar的“喜欢”,测试code如下:

INSERT INTO TB1(C1,C2)
SELECT N'刘䶮',N'刘䶮'

SELECT C1,
CAST(C1 AS NVARCHAR(200)) AS C1_N,
C2,
CAST(C2 AS VARCHAR(200)) AS C2_V
FROM  TB1

显示结果如下:

“䶮”字只能在NVARCHAR模式下才能完好地显示哈!

建议使用NVARCHAR来存放非英文字符数据理由:
理由1:VARCHAR类型存放特殊字符或生僻字时存在乱码或字符被转变的问题
理由2:对于中文字符,使用VARCHAR和NVARCHAR消耗同样的空间,对于英文字符,使用VARCHAR比NVARCHAR节省一倍的空间,但随着磁盘成本越来越低,其提升的性能和节省的成本有限。(例外:如果数据中存在大量英文字符和少量非英文字符,则可以考虑VARCHAR类型)
理由3:对于需要国际化的企业,后期将VARCHAR升级为NVARCHAR的成本太高或难以实现
理由4:使用VARCHAR存放非英文字符时,容易生成错误的预估值,尤其在执行LIKE这类前缀匹配的预估时。显示结果如下:

“䶮”字只能在NVARCHAR模式下才能完好地显示哈!
建议使用NVARCHAR来存放非英文字符数据理由:
理由1:VARCHAR类型存放特殊字符或生僻字时存在乱码或字符被转变的问题
理由2:对于中文字符,使用VARCHAR和NVARCHAR消耗同样的空间,对于英文字符,使用VARCHAR比NVARCHAR节省一倍的空间,但随着磁盘成本越来越低,其提升的性能和节省的成本有限。(例外:如果数据中存在大量英文字符和少量非英文字符,则可以考虑VARCHAR类型)
理由3:对于需要国际化的企业,后期将VARCHAR升级为NVARCHAR的成本太高或难以实现
理由4:使用VARCHAR存放非英文字符时,容易生成错误的预估值,尤其在执行LIKE这类前缀匹配的预估时。


相关文章:

RDIFramework 平台代码生成器V3.2 发布版

   RDIFramework.NET — 基于.NET的快速信息化系统开发框架 — 系列目录

   RDIFramework.NET ━ .NET快速信息化系统开发框架 ━ 工作流程组件介绍      


一路走来数个年头,感谢RDIFramework.NET框架的支持者与使用者,大家可以通过下面的地址了解详情。

      RDIFramework.net官方网站:http://www.rdiframework.net/

      RDIFramework.Net官方博客:http://blog.rdiframework.net/

      同时需要说明的,以后的所有技术文章以官方网站为准,欢迎大家收藏!

RDIFramework.NET框架由专业团队长期打造、一直在更新、一直在升级,请放心使用!

欢迎关注RDIFramework.net框架官方公众微信微信号:rdiframework-net),及时了解最新动态。

扫描二维码立即关注

 

 

 

转载请注明:RDIFramework.NET » SQLServer特殊字符/生僻字与varchar

]]>
http://blog.rdiframework.net/1034.html/feed 0
MySQL 使用规范 http://blog.rdiframework.net/857.html http://blog.rdiframework.net/857.html#comments Mon, 17 Oct 2016 02:34:32 +0000 http://blog.rdiframework.net/?p=857 mysqlguifan

MySQL 使用规范

以下规范适用在线交易(OLTP)系统的数据库。数据仓库与分析系统也可以参考。

命名规范

  • 表名、字段名、索引名使用小写字母、数字,采用下划线分割
  • 表名采用模块名3个缩小字符_前缀,之后顺序为表名,最后_分表后缀
  • 表名、字段名不超过 32 个字符
  • 存储实体数据的表,名称使用名词,单数
  • 索引名称采用 idx_ 前缀,之后顺序跟随索引的字段名,字段名直接以下划线分割
  • 不使用保留字
  • 存储实体表间多对多对应关系的表,名称建议采用 noun_verb_noun 这样的模式。例如: member_like_propertyproperty_has_tag

SQL 语句中,

  • 保留字使用全大写
  • 字符串使用单引号('
sql
-- 正确
SELECT id, title FROM xiaoqu WHERE id = 1
SELECT id, title FROM xiaoqu WHERE areacode = '000100010001'

-- 错误
select ID, title from XiaoQu where id = 1
SELECT id, title FROM xiaoqu WHERE areacode = "000100010001"

表的设计

MySQL 存储引擎使用 InnoDB

不用纠结,没有特殊原因的情况下,作为 OLTP 的 MySQL 使用 InnoDB 引擎。

字符集使用 UTF-8

Charset 为 utf8;Collation 为 utf8_general_ci

平衡是门艺术

效率优先,提升性能

没有绝对的对与错

适当牺牲范式,加入冗余

会增加代码复杂度

正确使用时间类型

MySQL 应当正确设置 time_zone

  • 精确到秒的时间采用 TIMESTAMP
  • 精确到日期使用 DATE
  • 一般不使用 DATETIME 类型
  • 每个表都含有ins_timeedt_time
  • 不允许使用字符串类型存储时间

字段定义为 NOT NULL

真的需要 NULL 值吗?如果不确定,就将字段设置为 NOT NULL

避免使用NULL字段

很难进行查询优化
NULL列加索引,需要额外空间
含NULL复合索引无效

字段设置 DEFAULT 值

设置为 NOT NULL 的字段,需要设置一个缺省值。

优先使用ENUM或SET

字符串
可能值已知且有限
ENUM占用1字节,转为数值运算
SET视节点定,最多8字节
比较时需要加‘ 单引号(即使是数值)

用好数值类型

原则:适用就好,越小越好

TINYINT< SMALLINT < MEDIUMINT < INT < BIGINT

1 Byte/2 Bytes/3 Bytes/4 Bytes/8 Bytes

FLOAT(4B)/DOUBLE(8B)

DECIMAL(M,D) (M+2 B)

不使用浮点类型(FLOAT、DOUBLE)

没有充分的理由,不要使用浮点数。

例如金额可以用分为单位,然后采用 INT。如果依然要以元为单位,可以采用 DECIMAL

数值类型VS字符串类型

更高效
查询更快
占用空间更小

使用 INT UNSIGNED 来存储 IPv4 地址

使用 INET_ATON 将 IP 地址的字符串形式转换成数字形式;使用 INET_NTOA 将 IP 地址数字形式转换成字符串形式,以便查看。

当要查询某段的 IP 时,请参考以下示例:

sql
 SELECT user_id FROM user_ip
 WHERE ip > INET_ATON('192.168.0.0') AND ip < INET_ATON('192.168.255.255')

当程序使用自带的函数进行 IP 地址的字符串形式与数字形式之间的转换时,需要注意数字的存储类型至少应为 32 位的无符号整型(如 uint32_t),并注意字节顺。

不直接存储图片、音频、视频等大容量内容

请使用分布式文件系统来存储图片、音频、视频等内容。数据库里只存储文件的位置。

少用并拆分TEXT/BLOB

TEXT类型处理性能远低于VARCHAR

  • 强制生成硬盘临时表
  • 浪费更多空间
  • VARCHAR(65535)=> 64K 尽量不用TEXT/BLOB 如需使用则拆分到独立的表存放

字段个数不超过 32 个

一个表有很多很多字段,是坏设计的味道。请再认真考虑设计是否正确。
IO高效/表修复快/Alter快/高并发
以1G Size 500W Rows来评估

  • 顺序读取需N秒
  • 单行不超过200Byte
  • 单表不超50个INT字段,不超20个CHAR(10)字段
  • 单表字段数上限控制在20-50个

控制单表数据量

单表年数据量预估

  • 纯INT单表<1000W Rows
  • 含CHAR单表<500W Rows
  • 合理分表不超载

索引

谨慎合理添加索引

改善查询
减慢更新
索引并非越多越好
能不加的索引就不加

  • 综合评估数据密度和数据分布
  • 最好不要超过字段数20% 结合核心SQL优先考虑覆盖索引

使用数字主键

存储实体数据的表,其主键应该是数字类型。

不使用联合主键

存储实体数据的表,不使用联合主键。
存储实体表间多对多对应关系的表(仅有两个字段)允许例外。

不使用外键

所有的表不建立外键约束。

联合索引字段数不超过 5 个

一个联合索引的字段数太多,很可能是设计得不好,还很难符合命名的规范。

前缀索引长度不超过 8 个字符

对字符串类型的字段建立索引,采用前缀索引,且长度不超过 8 个字符。

字符类添加前缀索引

区分度

  • 单字母区分度:26
  • 4字母区分度:264=456,976
  • 5字母区分度:265=11,881,376
  • 8字母区分度:268=…
举例:字符型字段建前缀索引
Picdesc varchar(255) NOT NULL DEFAULT ‘’
Key `idx_1` (`picdesc`(8)),

SQL 语句

不使用联表查询

OLTP 不使用 JOIN 联合查询。

不使用子查询

没有特别好的理由,OLTP 不允许使用子查询。

不使用负向查询

负向查询是指,如果查询条件描述的是不要什么数据,其余的都要。例如 !=<>NOT EXISTSNOT IN 以及 NOT LIKE 等就是负向查询,它们利用索引将会很辛苦。

一次查询的结果集不超过 100 行

必要时使用 LIMIT 100

LIMIT m, n,其中 m 应当小于 500

使用 SELECT ... LIMIT offset, row_count 或者 SELECT ... LIMIT row_count OFFSET offset 时,当 offset 小于 500 时,允许使用。

 sql
-- 允许
 SELECT ... FROM property WHERE broker_id=? ORDER BY update_time LIMIT 40, 20
 -- 不允许
 SELECT ... FROM property WHERE areacode=? ORDER BY update_time LIMIT 4000, 20

能够不使用 offset 的情况应当避免,如下面的例子(其中 id 是主键),

sql
-- 建议
SELECT ... FROM property WHERE broker_id=? AND id>? ORDER BY id LIMIT 20
-- 避免
SELECT ... FROM property WHERE broker_id=? ORDER BY id LIMIT 40, 20

避免使用 COUNT() 函数

能不使用就不使用,尽量用其他方法来解决。

例如判断经纪人是否有房源,可以不使用 COUNT() 函数,

 -- 正确
 SELECT 1 FROM propertys WHERE broker_id=? LIMIT 1

 -- 错误
 SELECT COUNT(*) FROM propertys WHERE broker_id=?

一次 COUNT() 可能扫描的行数应当确保小于 500 行

COUNT() 函数需要扫描所有的结果集之后才能得出结果。而结果集的大小需要业务知识来判断(EXPLAIN 方法只能来来检验某一个条件下的当前情况)。因此需要使用 COUNT() 查询的代码应当经过审阅。

 sql

 -- 允许。审阅。经纪人的房源数不允许超过 200 套

 SELECT COUNT(*) FROM property WHERE broker_id=?

 -- 不允许。一个区域板块下的房源数量不定,可能非常多

 SELECT COUNT(*) FROM property WHERE areacode=?

  • 其他聚合函数,例如 SUM()AVG()MAX() 等,同样适用。

统一使用 COUNT(*) 而不是 COUNT(1)

  • 当统计行数时,
  • 统一使用 COUNT(*) 而不是 COUNT(1)
  • 不使用 COUNT(PK)COUNT(column),除非真的是想统计 Nullable 字段的行数。

不在索引列做运算

  • 不在索引列进行数学运算或函数运算
  • 无法使用索引
  • 导致全表扫描
举例:
root:xxx_db> select BrokerId from ajk_propertys where proid-100=101960636; 
1 row in set (5.11 sec)

root:xxx_db> select BrokerId from ajk_propertys where proid=101960736;
1 row in set (0.00 sec)

尽量不在数据库做运算

  • 尽量不在数据库做运算
  • 复杂的运算移到程序端CPU
  • 尽可能简单的来使用Mysql

同数据类型的列值比较

原则:数字对数字,字符对字符
字符列与数值类型比较
数值列于字符列比较

  • 同时转换为双精度进行比较 字符列于数值列比较
  • 字符列整列转数值,不会使用索引
举例:字段UserMobile varchar(15)
root:xxx_db> select BrokerId from ajk_brokerextend where UserMobile = 15863194010 limit 1;           
1 row in set (2.07 sec)
root:xxx_db> select BrokerId from ajk_brokerextend where UserMobile = '18213821864' limit 1;              
1 row in set (0.00 sec)

禁止在查询条件中对字段进行数学运算、函数调用、隐式类型转换

  • 这类查询语句在使用索引时将非常困难。
sql

 -- 禁止

 SELECT id FROM property WHERE NOW() - update_time < 3600

 SELECT id FROM property WHERE update_time + 3600 > NOW()

 -- 改为

 SELECT id FROM property WHERE update_time > NOW() - 3600

 sql

  -- 禁止

 SELECT id FROM property WHERE CHAR_LENGTH(title) > 20

 sql

 -- 假设字段 property.status 的类型为 TINYINT

 -- 禁止

 SELECT id FROM property WHERE status = '1'

 -- 改为

 SELECT id FROM property WHERE status = 1

禁止隐式类型转换

  • 不仅在查询条件中禁止隐示类型转换,INSERTUPDATE 也不允许隐式类型转换。
 sql
 -- 假设字段 property.status 的类型为 TINYINT
 -- 禁止
 INSERT INTO property (..., status) VALUES (..., '1')
 UPDATE property SET status = '1' WHERE id = '43'

 -- 改为
 INSERT INTO property (..., status) VALUES (..., 1)
 UPDATE property SET status = 1 WHERE id = 43

禁止使用 % 前导查询

尽量不使用 LIKE 查询,不得不用的情况下也禁止使用 % 前导查询。

  • 使用不了索引
  • 导致全表扫描
sql
 -- 禁止
 SELECT id FROM property WHERE title LIKE '%最%'

禁止SQL语句拼接

  • 应用程序不能有拼接语句出现

禁止使用DELETE语句使用

  • 应用程序不能有DELETE语句出现

禁止全表更新语句使用

  • 应用程序不能执行全表更新语句出现

拒绝3B

BIG SQL (大SQL)

BIG Transactions (大事务)

BIG Batch (大批量)

SQL语句尽可能简单

大SQL VS 多个简单SQL

  • 传统设计思想,BUT Mysql NOT
  • 一条SQL只能在一个CPU运算
  • 1000+ QPS的高并发中,1秒大SQL可能把数据库都堵死

拒绝大SQL,拆解成多条简单SQL

  • 简单SQL缓存命中率更高
  • 减少锁表时间,特别是MYISAM
  • 用上多CPU

改写OR为IN/UNION

同一字段,将OR改写为IN()

  • OR效率:O(n)
  • IN效率:O(Log n)
  • 当n很大时,OR会慢很多
  • 注意控制IN的个数,建议n小于200

不同字段,将or改为union

  • 减少对不同字段进行 or 查询
  • Merge index 往往很弱智

保持事务(连接)短小

保持事务/DB连接短小精悍

  • 事务/连接使用原则:即开即用,用完即关
  • 与事务无关操作放到事务外面,减少锁资源的占用
  • 不破坏一致性前提下,使用多个短事务代替长事务

约定规范

约定类规范(一)

隔离线上线下
构建数据库生态环境

  • 开发无线上DB权限
  • 原则:线上连线上,线下连线下

统一字符集为UTF8
禁用子查询

  • 大部分情况优化较差
  • 特别是Where中使用IN id的子查询
  • 一般可用JOIN改写

约定类规范(二)

  • 永远不在程序端显示加锁
  • 永远不在程序端对数据库显式加锁
  • 外部锁对数据库不可控
  • 高并发是灾难
  • 极难调试和排查 可采用事务操作
举例:
Select GET_LOCK(‘str’)

约定类规范(三)

  • 请不要使用存储过程
  • 请不要使用trigger

转载请注明:RDIFramework.NET » MySQL 使用规范

]]>
http://blog.rdiframework.net/857.html/feed 0
RDIFramework.NET — 基于.NET的快速信息化系统开发框架 — 系列目录 http://blog.rdiframework.net/123.html http://blog.rdiframework.net/123.html#comments Mon, 08 Aug 2016 00:08:00 +0000 http://blog.scbz.org/index.php/archives/123 RDIFramework.NET基于.NET的快速信息化系统开发框架 — 系列目录

RDIFramework.NET,基于.NET的快速信息化系统开发、整合框架,给用户和开发者最佳的.Net框架部署方案。

      框架简单介绍

RDIFramework.NET,基于.NET的快速信息化系统开发、整合框架,为企业或个人在.NET环境下快速开发系统提供了强大的支持,开发人员不需要开发系统的基础功能和公共模块,框架自身提供了强大的函数库和开发包,开发人员只须集中精力专注于业务部分的开发,因此大大提高开发效率和节约开发成本。框架采用目前最主流的C#语言开发完成,支持多种数据库类型(SqlServer、Oracle、MySql等),支持WinForm与Web(Mvc、WebForm)。使用RDIFramework.NET能提高管理类软件系统的整体质量、提高模块与模块之间的兼容性、提高代码的重复利用率,使软件系统架构更加合理、质量更加过硬,使得劳动成果最大程度上重复利用。框架基础模块包括:强大灵活的权限控制组件,统一的模块分配与管理组件,灵活使用的工作流组件、集中的数据字典管理组件,自动升级组件,各种常用的商业控件(分页控件、组合查询控制、图片浏览控件等),强大的基于本框架的代码生成器,丰富的基础类库与开发辅助工具、内置MiNiWeb浏览器等各基础常用功能组件,下步将会考虑整合报表框架等。应用系统建立在此框架之上,采用构件式、可复用开发,节省开发成本,加快开发速度,在软件开发上更好的做到多快省。

RDIFramework.NET开发框架经过严格的技术测试和功能测试,包括压力测试和数百用户的使用体验。经过数年的功能升级和优化改进,最新版本已经非常稳定和强大,提供了丰富多彩的例子功能及配套的开发文档,开发员深入研究框架核心功能变得更加快速方便。 我们秉着分享成功经验与坚持创新的原则,提供优质售后服务与技术保障,我们积极收集用户反馈的系统改进意见和BUG,经数次改良和完善系统目前的版本极少发现系统性的缺陷。我们始终坚持技术创新,每次发布的新的版本会让用户惊喜不已,免费升级让用户感到优质服务带来的温暖,同样我们也感受到成功的喜悦。开发框架成熟稳定、思路新颖、用户体验与反应良好,人性化的界面布局与简洁直观的操作方式赢得大量用户的认可和赞美,欢迎大家选购我们的框架加速企业信息化的建设。

rdi_stractor

RDIFramework.NET工作流程组件

RDIFramework.NET工作流程组件是以RDIFramework.NET框架为支撑,根据我们多年的项目经验和项目实践,结合国内各大工作流产品的特点研发的一套流程管理组件。该组件不仅考虑到从零搭建业务系统,也考虑到与现有业务系统的整合。从零搭建系统我们可以使用RDIFramework.NET框架以基础,来快速搭建业务系统。与现有系统的整合,我们的流程引擎提供了丰富的功能接口,供三方业务系统调用,并提供组件的全部源码方便用户进行整合。RDIFramework.NET工作流程组件采用SOA架构模式,流程引擎通过WCF方式访问。支持B/S、C/S系统,同时支持SQLServer、ORACLE、MySQL等主流数据库。

RDIFramework.NET━工作流组件主要涵盖工作流的设计与定义、流程实例的发起与运行、业务流程的监控与管理、工作流组件与业务系统的集成与协作等内容。

系列介绍

本系统针对RDIFramework.NET产品进行了较为详细介绍,是集中了解与掌握框架整体的集中文章。后续还会有很多文章面世,都会在这儿集中,欢迎大家多提意见,框架的发展离不开你宝贵的建议,谢谢。关于理解框架所需的附带知识,如:《设计模式》、《PowerDesigner建模》、《软件架构设计》、《架构之类》、《系统分析与设计》之类的知识,可以参考相关书籍,博客园上也有很多此类优秀的知识供大家阅读。

架构设计之中包含着太多太多的知识和技术难点,作者水平有限,很多地方我也不甚了解,错误之处难免,还请大家多多交流与指导。

系列文章

RDIFramework.NET ━ .NET快速信息化系统开发框架钜献 V3.2 版本正式发布

RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.2->新增记录SQL执行过程

RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.2 新增解压缩工具类ZipHelper

RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.2->用户管理模块新增“重置用户密码”功能

RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.2-新增锁定用户与解除锁定用户的功能
RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.2-模块管理按子系统进行分类管理

RDIFramework.NET平台代码生成器V3.2版本全新发布(提供下载-免费使用)

.NET快速信息化系统开发框架 V3.2->新增“行政区域管理”,同时大批量树采用异步加载
.NET快速信息化系统开发框架 V3.2->新增模块管理界面导出功能(可按条件导出)

RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.2-> “Tab”标签新增可“最大化”显示功能

.NET快速信息化系统开发框架 V3.2-> Web版本新增新的用户权限设置界面效率更高、更规范

.NET快速信息化系统开发框架 V3.2->WinForm版本新增新的用户权限设置界面效率更高、更规范

.NET快速信息化系统开发框架 V3.2->Web版本模块管理界面新增模块排序功能

.NET快速信息化系统开发框架 V3.2->Web版本新增新的角色授权管理界面效率更高、更规范

.NET快速信息化系统开发框架 V3.2->WinForm版本新增新的角色授权管理界面效率更高、更规范

NET快速信息化系统开发框架 V3.2->Web版本新增“文件管理中心”集上传、下载、文件共享等一身,非常实用的功能

.NET快速信息化系统开发框架 V3.2->Web版本工作流部分业务处理界面与查看界面全新展示 
.NET快速信息化系统开发框架 V3.2->Web版本“产品管理”事例编辑界面新增KindEditor复文本编辑控件

.NET快速信息化系统开发框架 V3.2 -> Web 用户管理模块编辑界面-组织机构选择支持级联选择

.NET快速信息化系统开发框架 V3.2 -> WinForm“组织机构管理”界面组织机构权限管理采用新的界面,操作权限按模块进行展示

NET快速信息化系统开发框架 V3.2 -> “用户管理”主界面使用多表头展示、增加打印功能

.NET快速信息化系统开发框架 V3.2 ->WinForm部分全部重构为Dev风格界面

.NET快速信息化系统开发框架 V3.2->WinForm版本重构岗位授权管理界面更规范、高效与美观

RDIFramework 平台代码生成器V3.2 发布版


RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.0 版本强势发布

RDIFramework.NET平台代码生成器V3.2版本全新发布-更新于2017-02-27(提供下载)

RDIFramework.NET平台代码生成器V3.1版本全新发布-更新于2016-10-08(提供下载)

RDIFramework.NET 平台代码生成器V3.0版本全新发布-更新于20160518(提供下载)

RDIFramework.NET (.NET快速信息化系统开发整合框架) 【开发实例】之产品管理(MVC版)

RDIFramework.NET (.NET快速信息化系统开发整合框架) 【开发实例】之产品管理(WebForm版)

RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.0 版新增消息管理

RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.0 版本新增序列

RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.0 版新增系统参数管理

RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.0 版新增查询引擎管理

RDIFramework.NET ━ .NET快速信息化系统开发框架钜献 V2.9 版本震撼发布

RDIFramework.NET V2.9版本 WinFom部分新增与修正的功能

RDIFramework.NET V2.9版本多语言的实现

RDIFramework.NET V2.9版本 Web新增至14套皮肤风格+三套界面组合(共42套皮肤组合)

RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.9 版本震撼发布-全新钜献

RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.8 版本发布

RDIFramework.NET ━ .NET快速信息化系统开发框架 ━ 工作流程组件介绍

RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.8 产品使用说明书

RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.8 工作流产品使用说明书

实例演示如何使用RDIFramework.NET 框架的工作流组件进行业务流程的定义—请假申请流程-WinForm

实例演示使用RDIFramework.NET 框架的工作流组件进行业务流程的定义—请假申请流程-Web

RDIFramework.NET ━ .NET快速信息化系统开发框架 ━ 工作流程组件WinForm业务平台

RDIFramework.NET ━ .NET快速信息化系统开发框架 ━ 工作流程组件Web业务平台

RDIFramework.NET 框架之组织机构权限设置

RDIFramework.NET开发实例━表约束条件权限的使用-Web

RDIFramework.NET开发实例━表约束条件权限的使用-WinForm

RDIFramework.NET V2.8版本 ━ 开发实例之产品管理(WinForm)

RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.8 版本━新增企业通(内部简易聊天工具)

RDIFramework.NET ━ Web中打印的各种方案参考-欢迎补充

RDIFramework.NET 框架兼容各种数据库类型事务使用范例参考

RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.8 版本━新增岗位管理-WinForm部分

RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.8 版本━新增岗位管理-Web部分

RDIFramework.NET V2.7 Web版本升手风琴+树型目录(2级+)方法

RDIFramework.NET框架SOA解决方案(集Windows服务、WinForm形式与IIS形式发布)-分布式应用

RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.7 版本发布

RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.7 产品使用说明书

【推荐】捕获WCF服务端与客户端产生的通讯数据并分析

WinForm部分功能介绍

RDIFramework.NET ━ .NET快速信息化系统开发框架-第1章 引言

RDIFramework.NET ━ .NET快速信息化系统开发框架-第2章 产品概述

RDIFramework.NET ━ .NET快速信息化系统开发框架-第3章 软件安装、配置、运行方法

RDIFramework.NET ━ .NET快速信息化系统开发框架-4.1 平台登录模块

RDIFramework.NET ━ .NET快速信息化系统开发框架-4.2 平台自动升级模块

RDIFramework.NET ━ .NET快速信息化系统开发框架-4.3 平台主界面

RDIFramework.NET ━ .NET快速信息化系统开发框架-4.4 员工(职员)管理

RDIFramework.NET ━ .NET快速信息化系统开发框架-4.5 用户管理模块

RDIFramework.NET ━ .NET快速信息化系统开发框架-4.6 角色管理模块

RDIFramework.NET ━ .NET快速信息化系统开发框架-4.7 组织机构管理模块

RDIFramework.NET ━ .NET快速信息化系统开发框架- 4.8 模块(菜单)管理

RDIFramework.NET ━ .NET快速信息化系统开发框架 – 4.9 操作(功能)权限管理模块

RDIFramework.NET ━ .NET快速信息化系统开发框架- 4.10 用户权限管理模块

RDIFramework.NET — 基于.NET的快速信息化系统开发框架- 4.11 角色权限管理模块

RDIFramework.NET — 基于.NET的快速信息化系统开发框架- 4.12 岗位(职位)管理模块

RDIFramework.NET — 基于.NET的快速信息化系统开发框架 – 5.1 数据字典管理模块

RDIFramework.NET — 基于.NET的快速信息化系统开发框架 – 5.2 表字段综合管理模块

RDIFramework.NET — 基于.NET的快速信息化系统开发框架 – 5.3 数据库连接管理模块

RDIFramework.NET — 基于.NET的快速信息化系统开发框架- 5.4平台日志、异常管理、生成自动升级配置文件模块

CAutoupdater通用 自动升级组件用户手册

RDIFramework.NET — 基于.NET的快速信息化系统开发框架 【开发实例】之产品管理(WinForm)

Web部分功能介绍

RDIFramework.NET  Web版介绍 

RDIFramework.NET ━ 9.2 员工管理 ━ Web部分

RDIFramework.NET ━ 9.3 用户管理 ━ Web部分

RDIFramework.NET ━ 9.4 角色管理 ━ Web部分

RDIFramework.NET ━ 9.5 组织机构管理 ━ Web部分

RDIFramework.NET ━ 9.6 模块(菜单)管理 ━ Web部分

RDIFramework.NET ━ 9.7 操作权限项管理 ━ Web部分

RDIFramework.NET ━ 9.8 用户权限管理 ━ Web部分

RDIFramework.NET ━ 9.9 角色权限管理 ━ Web部分

RDIFramework.NET ━ 9.10 岗位(职位)管理 ━ Web部分

RDIFramework.NET ━ 9.11 数据字典管理 ━ Web部分

RDIFramework.NET ━ 9.12 表字段管理 ━ Web部分

RDIFramework.NET ━ 9.13 系统日志与系统异常管理 ━ Web部分

RDIFramework.NET ━ 9.14 数据库连接管理 ━ Web部分

RDIFramework.NET ━ 9.15 个性化设置 ━ Web部分

RDIFramework.NET ━ 9.16 案例模块━ Web部分

RDIFramework.NET V2.5(.NET快速信息化系统开发框架)Web版介绍

相关文章

通用软件注册功能之建立有效的软件保护机制

RDIFramework.NET平台代码生成器V1.5发布

RDIFramework.NET V2.5(.NET快速信息化系统开发整合框架)项目结构讲解

RDIFramework.NET 答客户问

   RDIFramework.NET视频列表:

第1部分、框架在不同数据库中的配置

第1.1讲、RDIFramework.NET框架在SqlServer2008+中的配置与运行讲解

第1.2讲、RDIFramework.NET框架在Oracle11g+中的配置与运行讲解

第1.3讲、RDIFramework.NET框架在MySql 5.0+ 中的配置与运行讲解

第2部分、框架基于代码生成器的开发

第2.1讲、RDIFramework.NET框架基于代码生成器业逻辑层代码的生成

第2.2讲、RDIFramework.NET框架基于代码生成器生成MvcUI的讲解

第2.2.1讲、RDIFramework.NET框架基于代码生成器生成MvcUI的讲解_第2.2讲补充视频

第2.3讲、RDIFramework.NET框架基于代码生成器生成WebFormUI的讲解

第2.4讲、RDIFramework.NET框架基于代码生成器生成的业务逻辑进行WinForm程序的开发

 其他更多文章待续…


        如需采购,请登录:

  淘宝店地址:http://yonghu86.taobao.com/

一路走来数个年头,感谢RDIFramework.NET框架的支持者与使用者,了解详情可通过下面的地方访问。

RDIFramework.NET官方网站:http://www.rdiframework.net/

RDIFramework.NET官方博客:http://blog.rdiframework.net/

      同时需要说明的,以后的所有技术文章以官方网站为准,欢迎大家收藏!

RDIFramework.NET框架由专业团队长期打造、一直在更新、一直在升级,请放心使用!

欢迎关注RDIFramework.net框架官方公众微信微信号:guosisoft),及时了解最新动态。

扫描二维码立即关注

转载请注明:RDIFramework.NET » RDIFramework.NET — 基于.NET的快速信息化系统开发框架 — 系列目录

]]>
http://blog.rdiframework.net/123.html/feed 5
SQL Server 海量数据查询代码优化以及建议 http://blog.rdiframework.net/750.html http://blog.rdiframework.net/750.html#comments Fri, 27 May 2016 01:18:09 +0000 http://blog.rdiframework.net/?p=750 sqltiXiJieGou

1.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:

select id from t where num is null

可以在num上设置默认值0,确保表中num列没有 null值,然后这样查询:

select id from t where num=0

2.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。优化器将无法通过索引来确定将要命中的行数,因此需要搜索该表的所有行。

3.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:

select id from t where num=10 or num=20

可以这样查询:

 select id from t where num=10
 union all
 select id from t where num=20

4.in 和 not in 也要慎用,因为 IN 会使系统无法使用索引,而只能直接搜索表中的数据。如:

select id from t where num in(1,2,3)

对于连续的数值,能用 between 就不要用 in 了:

select id from t where num between 1 and 3

5.尽量避免在索引过的字符数据中,使用非打头字母搜索。这也使得引擎无法利用索引。
见如下例子:

 SELECT * FROM T1 WHERE NAME LIKE ‘%L%’
 SELECT * FROM T1 WHERE SUBSTING(NAME,2,1)=’L’
 SELECT * FROM T1 WHERE NAME LIKE ‘L%’

即使 NAME 字段建有索引,前两个查询依然无法利用索引完成加快操作,引擎不得不对全表所有数据逐条操作来完成任务。而第三个查询能够使用索引来加快操作。

6.必要时强制查询优化器使用某个索引,如在 where 子句中使用参数,也会导致全表扫描。因为 SQL 只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:

select id from t where num=@num

可以改为强制查询使用索引:

select id from t with(index(索引名)) where num=@num

7.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:

SELECT * FROM T1 WHERE F1/2=100

应改为:

 SELECT * FROM T1 WHERE F1=100*2
 SELECT * FROM RECORD WHERE SUBSTRING(CARD_NO,1,4)=’5378’

应改为:

 SELECT * FROM RECORD WHERE CARD_NO LIKE ‘5378%’
 SELECT member_number, first_name, last_name FROM members
 WHERE DATEDIFF(yy,datofbirth,GETDATE()) > 21

应改为:

 SELECT member_number, first_name, last_name FROM members
 WHERE dateofbirth < DATEADD(yy,-21,GETDATE())

即:任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询时要尽可能将操作移至等号右边。

8.应尽量避免在where 子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:

 select id from t where substring(name,1,3)='abc'--name以 abc开头的id
 select id from t where datediff(day,createdate,'2005-11-30')=0--‘2005-11-30’生成的 id

应改为:

 select id from t where name like 'abc%'
 select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'

9.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

10.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。

11.很多时候用 exists是一个好的选择:

select num from a where num in(select num from b)

用下面的语句替换:

 select num from a where exists(select 1 from b where num=a.num)
 SELECT SUM(T1.C1)FROM T1 WHERE(
 (SELECT COUNT(*)FROM T2 WHERE T2.C2=T1.C2>0) SELECT SUM(T1.C1) FROM T1WHERE EXISTS(
 SELECT * FROM T2 WHERE T2.C2=T1.C2)

两者产生相同的结果,但是后者的效率显然要高于前者。因为后者不会产生大量锁定的表扫描或是索引扫描。 如果你想校验表里是否存在某条纪录,不要用count(*)那样效率很低,而且浪费服务器资源。可以用EXISTS代替。如:

IF (SELECT COUNT(*) FROM table_name WHERE column_name = 'xxx')

可以写成:

IF EXISTS (SELECT * FROM table_name WHERE column_name = 'xxx')

经常需要写一个T_SQL 语句比较一个父结果集和子结果集,从而找到是否存在在父结果集中有而在子结果集中没有的记录,如:

 SELECT a.hdr_key FROM hdr_tbl a---- tbl a 表示 tbl用别名 a代替
 WHERE NOT EXISTS (SELECT * FROM dtl_tbl b WHERE a.hdr_key = b.hdr_key)
 SELECT a.hdr_key FROM hdr_tbl a
 LEFT JOIN dtl_tbl b ON a.hdr_key = b.hdr_key WHERE b.hdr_key IS NULL
 SELECT hdr_key FROM hdr_tbl
 WHERE hdr_key NOT IN (SELECT hdr_key FROM dtl_tbl)

三种写法都可以得到同样正确的结果,但是效率依次降低。

12.尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。

13.避免频繁创建和删除临时表,以减少系统表资源的消耗。

14.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。

15.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。

16.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。

17.在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。

18.尽量避免大事务操作,提高系统并发能力。

19.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

20. 避免使用不兼容的数据类型。例如 float和int、char 和varchar、binary和varbinary是不兼容的。数据类型的不兼容可能使优化器无法执行一些本来可以进行的优化操作。

例如:

 

SELECT name FROM employee WHERE salary > 60000

在这条语句中,如salary字段是money型的,则优化器很难对其进行优化,因为60000是个整型数。我们应当在编程时将整型转化成为钱币型,而不要等到运行时转化。

21.充分利用连接条件,在某种情况下,两个表之间可能不只一个的连接条件,这时在 WHERE 子句中将连接条件完整的写上,有可能大大提高查询速度。
例:

 SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD B WHERE A.CARD_NO = B.CARD_NO
 SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD B WHERE A.CARD_NO = B.CARD_NO AND A.ACCOUNT_NO=B.ACCOUNT_NO

第二句将比第一句执行快得多。

22、使用视图加速查询
把表的一个子集进行排序并创建视图,有时能加速查询。它有助于避免多重排序 操作,而且在其他方面还能简化优化器的工作。例如:

 SELECT cust.name,rcvbles.balance,„„other columns FROM cust,rcvbles
 WHERE cust.customer_id = rcvlbes.customer_id
 AND rcvblls.balance>0
 AND cust.postcode>“98000”
 ORDER BY cust.name

如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放在一个视图中,并按客户的名字进行排序:

 CREATE VIEW DBO.V_CUST_RCVLBES
 AS
 SELECT cust.name,rcvbles.balance,„„other columns
 FROM cust,rcvbles
 WHERE cust.customer_id = rcvlbes.customer_id
 AND rcvblls.balance>0
 ORDER BY cust.name

然后以下面的方式在视图中查询:

 SELECT * FROM V_CUST_RCVLBES
 WHERE postcode>“98000”

视图中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。

23、能用DISTINCT 的就不用 GROUP BY

SELECT OrderID FROM Details WHERE UnitPrice > 10 GROUP BY OrderID

可改为:

SELECT DISTINCT OrderID FROM Details WHERE UnitPrice > 10

24.能用UNION ALL就不要用 UNION UNION ALL不执行SELECT DISTINCT 函数,这样就会减少很多不必要的资源

25.尽量不要用SELECT INTO语句。

SELECT INOT 语句会导致表锁定,阻止其他用户访问该表。 上面我们提到的是一些基本的提高查询速度的注意事项,但是在更多的情况下,往往需要反复试验比较不同的语句以得到最佳方案。最好的方法当然是测试,看实现 相同功能的 SQL语句哪个执行时间最少,但是数据库中如果数据量很少,是比较不出来的,这时可以用查看执行计划,即:把实现相同功能的多条SQL语句考到 查询分析器,按 CTRL+L看查所利用的索引,表扫描次数(这两个对性能影响最大),总体上看询成本百分比即可。

转载请注明:RDIFramework.NET » SQL Server 海量数据查询代码优化以及建议

]]>
http://blog.rdiframework.net/750.html/feed 0
【干货】再上数据分页控件 ━ 更加灵活,更加实用-提供源码 http://blog.rdiframework.net/97.html http://blog.rdiframework.net/97.html#comments Tue, 06 Jan 2015 17:50:00 +0000 http://blog.scbz.org/index.php/archives/97 再上数据分页控件-更加灵活,更加实用 

  关于数据分页的文章太多了,各有各的一套方案,但大多都很类似,要么使用存储过程,要么直接使用代码进行分页。各种方案分页的效率也不尽相同,我们不一定要找一个最高效的(根据实际的项目情况),找一个最合适的就OK了。下面我要谈的分页控件非常灵活,可以支持任意类型的数据库,同时可以支持存储过程或代码分页(会自动判断),也支持多表的分页,非常的方便。对于数据分页的相关文章,在我的博客中可以找到很多,下面我做一个简单的汇总,方便大家查阅。 

1、 原创企业级控件库之大数据量分页控件

2、 再上数据分页控件(不用存储过程)

3、 RDIFramework.NET 中多表关联查询分页实例

  下面要给大家分享的分页控件只做分页的处理,不做与数据库相关的操作。直接提供分页的数据给分页控件即可。它不关心你的数据来源是什么,也不关心你采用的数据分页的方式(存储过程或代码等)。这个分页控件我取名为:UcPagerEx,如下图所示:

 

  实现分页控件的代码非常的简单,下面直接给出全部源码,大家可以参考下,整个分页控件的源码如下:

  1 using System;
  2 using System.ComponentModel;
  3 using System.Windows.Forms;
  4 namespace RDIFramework.Controls
  5 {
  6     public delegate void PageChangedEventHandler(object sender, EventArgs e);
  7     /// 
  8     /// 分页用户控件,仅提供分页信息显示及改变页码操作
  9     /// 
 10 public partial class UcPagerEx : UserControl
 11 {
 12         public event PageChangedEventHandler PageChanged;
 13         private int _pageSize;
 14         private int m_PageCount;
 15         private int _recordCount;
 16         private int _pageIndex;
 17 
 18 
 19         public UcPagerEx()
 20         {
 21             InitializeComponent();
 22             this._pageSize = 10;
 23             this._recordCount = 0;
 24             this._pageIndex = 1; //默认为第一页
 25         }
 26         ///  
 27         /// 带参数的构造函数
 28         /// 每页记录数
 29         /// 总记录数
 30         /// 
 31         public UcPagerEx(int recordCount, int pageSize)
 32         {
 33             InitializeComponent();
 34 
 35             this._pageSize = pageSize;
 36             this._recordCount = recordCount;
 37             this._pageIndex = 1; //默认为第一页
 38             this.InitPageInfo();
 39         }
 40         protected virtual void OnPageChanged(EventArgs e)
 41         {
 42             if (PageChanged != null)
 43             {
 44                 InitPageInfo();
 45                 PageChanged(this, e);
 46             }
 47         }
 48         [Description("设置或获取一页中显示的记录数目"), DefaultValue(20), Category("分页")]
 49         public int PageSize
 50         {
 51             set
 52             {
 53                 this._pageSize = value;
 54             }
 55             get
 56             {
 57                 return this._pageSize;
 58             }
 59         }
 60 
 61         [Description("获取记录总页数"), DefaultValue(0), Category("分页")]
 62         public int PageCount
 63         {
 64             get
 65             {
 66                 return this.m_PageCount;
 67             }
 68         }
 69 
 70         [Description("设置或获取记录总数"), Category("分页")]
 71         public int RecordCount
 72         {
 73             set
 74             {
 75                 this._recordCount = value;
 76             }
 77             get
 78             {
 79                 return this._recordCount;
 80             }
 81         }
 82 
 83         [Description("当前的页面索引, 开始为1"), DefaultValue(0), Category("分页")]
 84         [Browsable(false)]
 85         public int PageIndex
 86         {
 87             set
 88             {
 89                 this._pageIndex = value;
 90             }
 91             get
 92             {
 93                 return this._pageIndex;
 94             }
 95         }
 96 
 97         ///  
 98         /// 初始化分页信息
 99         /// 每页记录数
100         /// 总记录数
101         /// 
102         public void InitPageInfo(int recordCount, int pageSize)
103         {
104             this._recordCount = recordCount;
105             this._pageSize = pageSize;
106             this.InitPageInfo();
107         }
108 
109         ///  
110         /// 初始化分页信息
111         /// 总记录数
112         /// 
113         public void InitPageInfo(int recordCount)
114         {
115             this._recordCount = recordCount;
116             this.InitPageInfo();
117         }
118         ///  
119         /// 初始化分页信息
120         /// 
121         public void InitPageInfo()
122         {
123             if (this._pageSize < 1)
124                 this._pageSize = 10; //如果每页记录数不正确,即更改为10
125             if (this._recordCount < 0)
126                 this._recordCount = 0; //如果记录总数不正确,即更改为0
127 
128             //取得总页数
129             if (this._recordCount % this._pageSize == 0)
130             {
131                 this.m_PageCount = this._recordCount / this._pageSize;
132             }
133             else
134             {
135                 this.m_PageCount = this._recordCount / this._pageSize + 1;
136             }
137 
138             //设置当前页
139             if (this._pageIndex > this.m_PageCount)
140             {
141                 this._pageIndex = this.m_PageCount;
142             }
143             if (this._pageIndex < 1)
144             {
145                 this._pageIndex = 1;
146             }
147 
148             //设置上一页按钮的可用性
149             bool enable = (this.PageIndex > 1);
150             this.btnPrevious.Enabled = enable;
151 
152             //设置首页按钮的可用性
153             enable = (this.PageIndex > 1);
154             this.btnFirst.Enabled = enable;
155 
156             //设置下一页按钮的可用性
157             enable = (this.PageIndex < this.PageCount);
158             this.btnNext.Enabled = enable;
159 
160             //设置末页按钮的可用性
161             enable = (this.PageIndex < this.PageCount);
162             this.btnLast.Enabled = enable;
163             this.txtPageIndex.Text = this._pageIndex.ToString();
164             this.lblPageInfo.Text = string.Format("共 {0} 条记录,每页 {1} 条,共 {2} 页", this._recordCount, this._pageSize, this.m_PageCount);
165         }
166 
167         public void RefreshData(int page)
168         {
169             this._pageIndex = page;
170             EventArgs e = new EventArgs();
171             OnPageChanged(e);
172         }
173 
174         private void btnFirst_Click(object sender, System.EventArgs e)
175         {
176             this.RefreshData(1);
177         }
178 
179         private void btnPrevious_Click(object sender, System.EventArgs e)
180         {
181             if (this._pageIndex > 1)
182             {
183                 this.RefreshData(this._pageIndex - 1);
184             }
185             else
186             {
187                 this.RefreshData(1);
188             }
189         }
190         private void btnNext_Click(object sender, System.EventArgs e)
191         {
192             if (this._pageIndex < this.m_PageCount)
193             {
194                 this.RefreshData(this._pageIndex + 1);
195             }
196             else if (this.m_PageCount < 1)
197             {
198                 this.RefreshData(1);
199             }
200             else
201             {
202                 this.RefreshData(this.m_PageCount);
203             }
204         }
205 
206         private void btnLast_Click(object sender, System.EventArgs e)
207         {
208             this.RefreshData(this.m_PageCount > 0 ? this.m_PageCount : 1);
209         }
210 
211         private void txtPageIndex_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
212         {
213             if (e.KeyCode == Keys.Enter)
214             {
215                 int num;
216                 try
217                 {
218                     num = Convert.ToInt16(this.txtPageIndex.Text);
219                 }
220                 catch
221                 {
222                     num = 1;
223                 }
224 
225                 if (num > this.m_PageCount)
226                     num = this.m_PageCount;
227                 if (num < 1)
228                     num = 1;
229 
230                 this.RefreshData(num);
231             }
232         }
233     }
234 }

  代码基本没有什么难度,相信大家都能看得懂,那么如何使用这个控件呢?

  首先在生成的工具箱中拖动这个分页控件到界面上,再后再做数据绑定的代码即可。我直接展示一个已经做成的界面,如下图所示:

  上面就是分页的效果,如何实现的呢?下面给出实现代码。

  我们可以在Load事件中调用下面的Search()方法对数据进行绑定,如下代码所示:

 

 1 private void Search()
 2 {
 3     var recordCount = 0;            
 4     this.DTProductInfo = GetData(out recordCount, ucPager.PageIndex, ucPager.PageSize, this.searchValue);
 5     ucPager.RecordCount = recordCount;
 6     ucPager.InitPageInfo();
 7     // 加载绑定数据
 8     this.GetList();
 9 }
10 
11 private DataTable GetData(out int recordCount, int pageIndex, int pageSize,string search)
12 {
13     return new ProductInfoManager(dbProvider).GetDTByPage(out recordCount, pageIndex, pageSize, search,ProductInfoTable.FieldCreateOn + " DESC ");
14 }
15 
16 public override void GetList()
17 {
18     this.dgvProductInfo.AutoGenerateColumns = false;
19     if (this.DTProductInfo.Columns.Count > 0)
20     {
21         this.DTProductInfo.DefaultView.Sort = ProductInfoTable.FieldCreateOn;
22     }
23 
24     this.dgvProductInfo.DataSource = this.DTProductInfo.DefaultView;
25     this.SetControlState();
26 }

   同时需要对UcPagerEx的PageChanged事件做处理,以启用用户分页的需求,代码如下:

1 private void ucPager_PageChanged(object sender, EventArgs e)
2 {
3     var holdCursor = this.Cursor;
4     this.Cursor = Cursors.WaitCursor;
5     Search();
6     this.Cursor = holdCursor;
7 }

  附注:对于上面的“GetDTByPage”方法可以任意实现,可以调用存储过程,也可以使用代码进行分页。只要返回分页的数据即可。

  下面给出一些分页的效果,如下图所示:

 

 

转载请注明:RDIFramework.NET » 【干货】再上数据分页控件 ━ 更加灵活,更加实用-提供源码

]]>
http://blog.rdiframework.net/97.html/feed 0
删除Sqlserver数据库中所有表和字段的备注说明 http://blog.rdiframework.net/112.html http://blog.rdiframework.net/112.html#comments Thu, 07 Nov 2013 22:53:00 +0000 http://blog.scbz.org/index.php/archives/112 删除Sqlserver数据库中所有表和字段的备注说明

 

  有时我们有这样的需求,我们做了一个产品发布到互联网上,但又不想让用户知道了数据库各个表名、字段的备注信息(这些信息我们可能在设计时放了中文名称),别人用个PowerDesigner逆向工程,一下子就把你的设计给导进去了,中文都暴露在用户眼前,有时这不是我们所期望的,那么在SqlServer中如何删除数据库中所有表和字段的备注说明呢,下面的存储过程即可做到!  

IF EXISTS ( SELECT  1
            FROM    sys.objects
            WHERE   name = 'P_DeleteDscirption'
                    AND type = 'p' ) 
    BEGIN
        DROP PROCEDURE P_DeleteDscirption
    END
GO
CREATE PROCEDURE P_DeleteDscirption
AS 
    BEGIN
    
        IF OBJECT_ID('tempdb..#temp') IS NOT NULL 
            DROP TABLE #temp
            
        DECLARE @index INT ,
            @rowCount INT ,
            @tableID INT ,
            @colID INT ,
            @Sql NVARCHAR(4000) ,
            @colName NVARCHAR(200) ,
            @tableName NVARCHAR(200) 
        -------------缓存临时数据    
        SELECT  t.name AS TableName ,
                c.name AS ColumnName ,
                c.object_id AS TableID ,
                c.column_id AS ColID
        INTO    #temp
        FROM    sys.columns AS c
                LEFT JOIN sys.objects AS t ON c.object_id = t.object_id
        WHERE   t.type = 'u'
        
        -------------获取循环阀值
        SELECT  @index = 0 ,
                @rowCount = COUNT(1)
        FROM    (SELECT DISTINCT
                        TableName
                 FROM   #temp
                ) AS t
        
        WHILE (@index < @rowCount) 
            BEGIN
                SELECT  @Sql = N'' ,
                        @index = @index + 1
                -------------取出表名和表的object_id      
                SELECT  @tableName = t.TableName ,
                        @tableID = T.TableID
                FROM    (SELECT DISTINCT
                                TableName ,TableID,
                                DENSE_RANK() OVER (ORDER BY TableName) AS [rowIndex]
                         FROM   #temp
                        ) AS t
                WHERE   t.rowIndex = @index 
                -------------判断表是否有说明
                IF EXISTS ( SELECT  * FROM    sys.extended_properties WHERE   major_id = @tableID AND minor_id = 0 AND name = N'MS_Description' ) 
                    BEGIN
                        SET @Sql = @Sql
                            + 'EXEC sys.sp_dropextendedproperty @name = N''MS_Description'' ,@level0type = N''SCHEMA'', @level0name = N''dbo'' , @level1type = N''TABLE'' ,@level1name = '
                            + 'N''' + @tableName + '''' + ';' + CHAR(13)
              
                        EXECUTE sys.sp_executesql @Sql
                        PRINT @Sql
                    END
            END
 
        -------------获取循环阀值
        SELECT  @index = 0 ,
                @rowCount = COUNT(1)
        FROM    #temp
 
 
        WHILE (@index < @rowCount) 
            BEGIN
                SELECT  @Sql = N'' ,
                        @index = @index + 1
                
                -------------取出表明列名,object_id,column_id
                SELECT  @tableID = t.TableID ,
                        @colID = t.ColID ,
                        @tableName = t.TableName ,
                        @colName = t.ColumnName
                FROM    (SELECT DISTINCT TableName , ColumnName , colID, TableID,
                                ROW_NUMBER() OVER (ORDER BY TableName, ColumnName) AS [rowIndex]
                         FROM   #temp
                        ) AS t
                WHERE   t.rowIndex = @index
                -------------判断列是否存在说明
                IF EXISTS ( SELECT  * FROM sys.extended_properties WHERE major_id = @tableID AND minor_id = @colID AND name = N'MS_Description' ) 
                    BEGIN
                        SET @Sql = @Sql
                            + 'EXEC sys.sp_dropextendedproperty @name = N''MS_Description'',@level0type = N''SCHEMA'', @level0name = N''dbo'' , @level1type = N''TABLE'' ,@level1name = '
                            + N'''' + @tableName + ''''
                            + ',@level2type = N''COLUMN'''
                            + ',@level2name =''' + N'' + @colName + ''';'
                            + CHAR(13)
                        
                        PRINT @Sql
                        EXECUTE sys.sp_executesql @Sql
                    END
            END
        -------------清除临时表
        IF OBJECT_ID('tempdb..#temp') IS NOT NULL 
            DROP TABLE #temp
    END

 

 

 

转载请注明:RDIFramework.NET » 删除Sqlserver数据库中所有表和字段的备注说明

]]>
http://blog.rdiframework.net/112.html/feed 0
解决SQLSERVER在还原数据时出现的“FILESTREAM功能被禁用”问题 http://blog.rdiframework.net/121.html http://blog.rdiframework.net/121.html#comments Fri, 26 Jul 2013 17:43:00 +0000 http://blog.scbz.org/index.php/archives/121 解决SQLSERVER在还原数据时出现的“FILESTREAM功能被禁用”问题

  今天由于测试需要,在网上下载了Adventureworks2008实例数据库的BAK文件,进行还原时出现了这样的错误“FILESTREAM功能被禁用”,如下图所示:

sqlError1

  遇到上面的问题,我们通常的做法是通过SQLSERVER配置管理器,对SQLSERVER实例服务做如下设置再重启SQLSERVER实例服务即可成功,如下图所示:

  一般情况通过上面的设置,即可成功还原,但在还原Adventureworks2008数据库时,通过上面的设置后,还是不能成功还原,错误依旧,还得重新找其他可靠方法,通过查看MSDN的帮助文件,得知其因SQLSERVER的访问级别造成,手动不行,就用命令,通过执行下面的命令后,即可成功还原。

USE master
GO
EXEC sp_configure filestream_access_level, 2
RECONFIGURE

再次附加,即可成功。

  

 

附注:

  使用 filestream_access_level 选项可以更改此 SQL Server 实例的 FILESTREAM 访问级别。

注意 注意
必须先启用 Windows FILESTREAM 管理设置,然后此选项才会生效。 可以在安装 SQL Server 时启用这些设置,也可以使用 SQL Server 配置管理器进行启用。

定义
0 为此实例禁用 FILESTREAM 支持。
1 针对 Transact-SQL 访问启用 FILESTREAM。
2 针对 Transact-SQL 和 Win32 流访问启用 FILESTREAM。

 

在此仅做下记录,帮助遇到同样问题的人,少走弯路。

 欢迎加入数据库交流群(多位DBA阵容),群号:190401986 (注:博客园邀请进入)

 

转载请注明:RDIFramework.NET » 解决SQLSERVER在还原数据时出现的“FILESTREAM功能被禁用”问题

]]>
http://blog.rdiframework.net/121.html/feed 0
(喷血分享)利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句 http://blog.rdiframework.net/122.html http://blog.rdiframework.net/122.html#comments Wed, 24 Jul 2013 23:30:00 +0000 http://blog.scbz.org/index.php/archives/122  

(喷血分享)利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句

 

在我们RDIFramework.NET代码生成器中,有这样一个应用,就是通过数据库表自动生成表的CREATE语句,如下图所示:

sql11 

在实现此功能前摸索了很多方法,最后借助MSSQLSERVER自带的dll文件来完成。先截图展示下此功能生成后的效果,然后再分享代码与方法,欢迎大家讨论其他可行方式,谢谢。 

  通过上图可以看到,生成的表CREATE语句与SQLSERVER企业管理器生成的语句完全一样。现在我们来看一看如何实现。在上面我说过,我采用的是SQLSERVER自带的dll文件的方法来完成。因此,我们首先要引用MSSQLSERVER的相关dll文件,如我的SQLSERVER安装在“D:\Program Files\Microsoft SQL Server\”,打开目录“D:\Program Files\Microsoft SQL Server\100\SDK\Assemblies”,就可以看到SQLSERVER的全部dll文件了,其实通过这些dll文件,我们可以完成像SQLSERVER企业管理器一样的功能,非常强大,看你怎么使用了,在此仅抛砖引玉。我们需要在我们的项目中添加两个dll文件的引用,分别为:

Microsoft.SqlServer.ConnectionInfo.dll

Microsoft.SqlServer.Management.Sdk.Sfc.dll

如下图所示:

   

引用了上面两个dll文件后,在我们的项目中添加命名空间:

using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;

现在,我们就可以使用MSSQLSERVER提供的类库来生成表的CREATE语句了。

下面给出创建的全部代码:

     private void ScriptOption()
        {
            scriptOption.ContinueScriptingOnError = true;
            scriptOption.IncludeIfNotExists = true;
            scriptOption.NoCollation = true;
            scriptOption.ScriptDrops = false;
            scriptOption.ContinueScriptingOnError = true;
            //scriptOption.DriAllConstraints = true;
            scriptOption.WithDependencies = false;
            scriptOption.DriForeignKeys = true;
            scriptOption.DriPrimaryKey = true;
            scriptOption.DriDefaults = true;
            scriptOption.DriChecks = true;
            scriptOption.DriUniqueKeys = true;
            scriptOption.Triggers = true;
            scriptOption.ExtendedProperties = true;
            scriptOption.NoIdentities = false;
        }

        /// 
        /// 生成数据库类型为SqlServer指定表的DDL
        /// 
        private void GenerateSqlServerDDL()
        {
            //对于已经生成过的就不用再次生成了,节约资源。
            if (!string.IsNullOrEmpty(textEditorDDL.Text) && textEditorDDL.Text.Trim().Length > 10)
            {
                return;
            }

            ScriptOption();
            ServerConnection sqlConnection = null;
            try
            {
                StringBuilder sbOutPut = new StringBuilder();
                
                if (dbSet.ConnectStr.ToLower().Contains("integrated security")) //Windows身份验证
                {
                    sqlConnection = new ServerConnection(dbSet.Server);
                }
                else        //SqlServer身份验证
                {
                    string[] linkDataArray = dbSet.ConnectStr.Split(';');
                    string userName = string.Empty;
                    string pwd = string.Empty;
                    foreach (string str in linkDataArray)
                    { 
                        if(str.ToLower().Replace(" ","").Contains("userid="))
                        {
                            userName = str.Split('=')[1];
                        }

                        if (str.ToLower().Replace(" ", "").Contains("password"))
                        {
                            pwd = str.Split('=')[1];
                        }
                    }

                    sqlConnection = new ServerConnection(dbSet.Server,userName,pwd);
                }

                Server sqlServer = new Server(sqlConnection);
                Table table = sqlServer.Databases[dbSet.DbName].Tables[txtName.Text];
                string ids;
                //编写表的脚本
                sbOutPut = new StringBuilder();
                sbOutPut.AppendLine();
                sCollection = table.Script(scriptOption);

                foreach (String str in sCollection)
                {
                    //此处修正smo的bug
                    if (str.Contains("ADD  DEFAULT") && str.Contains("') AND type = 'D'"))
                    {
                        ids = str.Substring(str.IndexOf("OBJECT_ID(N'") + "OBJECT_ID(N'".Length, str.IndexOf("') AND type = 'D'") - str.IndexOf("OBJECT_ID(N'") - "OBJECT_ID(N'".Length);
                        sbOutPut.AppendLine(str.Insert(str.IndexOf("ADD  DEFAULT") + 4, "CONSTRAINT " + ids));
                    }
                    else
                        sbOutPut.AppendLine(str);

                    sbOutPut.AppendLine("GO");
                }

                //生成存储过程
                this.textEditorDDL.SetCodeEditorContent("SQL", sbOutPut.ToString());
                this.textEditorDDL.SaveFileName = this.TableName + ".sql";
                sbOutPut = new StringBuilder();
            }
            catch (Exception ex)
            {
                LogHelper.WriteException(ex);
            }
            finally
            {
                sqlConnection.Disconnect();
            }
        }  

说明:textEditorDDL为显示生成表CREATE语句后的控件。

欢迎讨论其他方法,谢谢!

如果觉得对你有引导与帮助,可以点下推荐,谢谢!

转载请注明:RDIFramework.NET » (喷血分享)利用.NET生成数据库表的创建脚本,类似SqlServer编写表的CREATE语句

]]>
http://blog.rdiframework.net/122.html/feed 0
Introduction to Change Data Capture (CDC) in SQL Server 2008[转] http://blog.rdiframework.net/126.html http://blog.rdiframework.net/126.html#comments Tue, 14 May 2013 01:34:00 +0000 http://blog.scbz.org/index.php/archives/126  

Change Data Capture records INSERTs, UPDATEs, and DELETEs applied to SQL Server tables, and makes a record available of what changed, where, and when, in simple relational ‘change tables’ rather than in an esoteric chopped salad of XML. These change tables contain columns that reflect the column structure of the source table you have chosen to track, along with the metadata needed to understand the changes that have been made. Pinal Dave explains all, with plenty of examples in a simple introduction. [原文地址:http://www.simple-talk.com/sql/learn-sql-server/introduction-to-change-data-capture-(cdc)-in-sql-server-2008/;作者:Pinal Dave]

1. Introduction 
2. Enabling Change Data Capture on a Database 
3. Enabling Change Data Capture on one or more Database Tables 
4. Example of Change Data Capture 
5. Capture Selected Column 
Summary

1. Introduction

Often, you’ll be told that the specification of an application requires that  the value of  data in the database of an application must be recorded before it is changed. In other words, we are required to save all the history of the changes to the data. This feature is usually implemented for data security purposes. To implement this, I have seen a variety of solutions from triggers, timestamps and complicated queries (stored procedures) to audit data.

SQL Server 2005 introduced the new features of ‘after update’, ‘after insert’ and ‘after delete’ triggers that  almost solved the problem of tracking changes in data.  A better solution was introduced in SQL Server 2008 and is called Change Data Capture (CDC). CDC has allowed SQL Server developers to deliver SQL Server data archiving and capturing without any additional programming.

CDC is one of the new data tracking and capturing features of SQL Server 2008. It only tracks changes in user-created tables. Because captured data is then stored in relational tables, it can be easily accessed and retrieved subsequently, using regular T-SQL.

When you apply Change Data Capture features on a database table, a mirror of the tracked table is created with the same column structure of the original table, but with additional columns that include the metadata used to summarize the nature of the change in the database table row.  The SQL Server DBA can then easily monitor the activity for the logged table using these new audit tables .

2. Enabling Change Data Capture on a Database

CDC first has to be enabled for the database. Because CDC is a table-level feature, it then has to be enabled for each table to be tracked. You can run following query and check whether it is enabled for any database.

   1: USE master 
   2: GO 
   3: SELECT [name], database_id, is_cdc_enabled  
   4: FROM sys.databases       
   5: GO

This query will return the entire database name along with a column that shows whether  CDC is enabled.

clip_image001

You can run this stored procedure in the context of each database to enable CDC at database level. (The following script will enable CDC in AdventureWorks database. )

   1: USE AdventureWorks
   2: GO 
   3: EXEC sys.sp_cdc_enable_db 
   4: GO

As soon as CDC is enabled, it will show this result in SSMS.

clip_image002

Additionally, in the database AdventureWorks, you will see that a schema with the name ‘cdc’ has now been  created.

clip_image003

Some System Tables will have been created within the  AdventureWorks database as part of the cdc schema.

clip_image004

The table which have been created are listed here.

  • cdc.captured_columns – This table returns result for list of captured column.
  • cdc.change_tables – This table returns list of all the tables which are enabled for capture.
  • cdc.ddl_history – This table contains history of all the DDL changes since capture data enabled.
  • cdc.index_columns – This table contains indexes associated with change table.
  • cdc.lsn_time_mapping – This table maps LSN number (for which we will learn later) and time.

3. Enabling Change Data Capture on one or more Database Tables

The CDC feature can be applied at the table-level  to any database for which CDC is enabled.  It has to be enabled for any table which needs to be tracked. First run following query to show which tables of database have already been enabled for CDC.

   1: USE AdventureWorks
   2: GO 
   3: SELECT [name], is_tracked_by_cdc  
   4: FROM sys.tables 
   5: GO

The above query will return a result that includes a column with the  table name, along with a column which displays if CDC is enabled or not.

clip_image005

You can run the following stored procedure to enable each table. Before enabling CDC at the table level, make sure that you have  enabled SQL Server Agent. When CDC is enabled on a table, it creates two CDC-related jobs that are specific to the database,  and executed using SQL Server Agent. Without SQL Server Agent enabled, these jobs will not execute.

Additionally, it is very important to understand the role of the required parameter @role_name. If there is any restriction of how data should be extracted from database, this option is used to specify any role which is following restrictions and gating access to data to this option if there is one.  If you do not specify any role and, instead, pass a NULL value, data access to this changed table will not be tracked and will be available to access by everybody.

Following script will enable CDC on HumanResources.Shift table.

   1: USE AdventureWorks
   2: GO 
   3: EXEC sys.sp_cdc_enable_table 
   4: @source_schema = N'HumanResources', 
   5: @source_name   = N'Shift', 
   6: @role_name     = NULL 
   7: GO

clip_image006

As we are using AdventureWorks database, it creates the jobs with following names.

1. cdc.AdventureWorks_capture – When this job is executed it runs the system stored procedure sys.sp_MScdc_capture_job.  The procedure sys.sp_cdc_scan  is called internally bysys.sp_MScdc_capture_job. This procedure cannot be executed explicitly when a change data capture log scan operation is already active or when the database is enabled for transactional replication. This system SP enables SQL Server Agent, which in facts enable Change Data Capture feature.

2. cdc.AdventureWorks_cleanup – When this job is executed it runs the system stored procedure sys.sp_MScdc_cleanup_job. This system SP cleans up database changes tables.

The Stored Procedure sys.sp_cdc_enable_table enables CDC. There are several options available with this SP but we will only mention the required options for this SP. CDC is very powerful and versatile tool. By understanding the Stored Procedure  sys.sp_cdc_enable_table you will gain the true potential of the CDC feature. One more thing to notice is that when these jobs are created they are automatically enabled as well.

clip_image007

By default, all the columns of the specified table  is taken into consideration of this operation. If you want to only few columns of this table to be tracked in that case you can specify the columns as one of the parameters of above mentioned SP.

When everything is successfully completed,  check  the system tables again and you will find a new table  called cdc.HumanResources_Shift_CT. This table will contain all the changes in the tableHumanResources.Shift. If you expand this table, you will find five additional columns as well. 

As you will see there are five additional columnsto the mirrored original table

  • __$start_lsn
  • __$end_lsn
  • __$seqval
  • __$operation
  • __$update_mask

There are two values which are very important to us is __$operation and __$update_mask.

Column _$operation contains value which corresponds to DML Operations. Following is quick list of value and its corresponding meaning.

  • Delete Statement = 1
  • Insert Statement = 2
  • Value before Update Statement = 3
  • Value after Update Statement = 4

The column _$update_mask shows, via a bitmap,   which columns were updated in the DML operation that was specified by _$operation.  If this was  a DELETE or INSERT operation,   all columns are updated and so the mask contains value which has all 1’s in it. This mask is contains value which is formed with Bit values.

4. Example of Change Data Capture

We will test this feature by doing  DML operations such as INSERT, UPDATE and DELETE on the table HumanResources.Shift which we have set up for CDC. We will observe the effects on the CDC table cdc.HumanResources_Shift_CT.

Before we start let’s first SELECT from both tables and see what is in them.

   1: USE AdventureWorks
   2: GO 
   3: SELECT * 
   4: FROM HumanResources.Shift 
   5: GO 
   6: USE AdventureWorks
   7: GO 
   8: SELECT * 
   9: FROM cdc.HumanResources_Shift_CT 
  10: GO

The result  of the query is as displayed here.

clip_image008

The original table HumanResources.Shift has three rows in it, whereas the  table cdc.HumanResources_Shift_CT is totally empty. This table will have entries after an operation on the tracked table.

Insert Operation

   1: Lets run an INSERT operation on the table HumanResources.Shift.
   2:  
   3: USE AdventureWorks
   4: GO 
   5: INSERT INTO [HumanResources].[Shift] 
   6: ([Name],[StartTime],[EndTime],[ModifiedDate]) 
   7: VALUES ('Tracked Shift',GETDATE(), GETDATE(), GETDATE()) 
   8: GO 
   9:  

Once the script is run, we will check the content of two of our tables HumanResources.Shift and cdc.HumanResources_Shift_CT.

clip_image009

Because of the INSERT operation, we have a newly inserted fourth row in the tracked table HumanResources.Shift . The tracking table also has the same row visible. The value of _operation is 2 which means that this is an INSERT operation.

Update Operation

To illustrate the effects of an UPDATE we will update a newly inserted row.

   1: USE AdventureWorks
   2:  
   3: GO
   4:  
   5: UPDATE [HumanResources].[Shift]
   6:  
   7: SET Name = 'New Name',
   8:  
   9:       ModifiedDate = GETDATE()
  10:  
  11: WHERE ShiftID = 4
  12:  
  13: GO
  14:  

Once more, we check our tables HumanResources.Shift and cdc.HumanResources_Shift_CT.

clip_image010

UPDATE operations always result in two different entries in the tracking table. One entry contains the previous values before the UPDATE is executed.  The second entry is for new data after the UPDATE is executed. In our case we have only changed two columns of the table but we are tracking the complete table so all the entries in the tableare logged before, and after, the update happens. The Change Data Capture mechanism always captures all the columns of the table unless, when CDC is set up on a table, it is restricted to track only a few columns. We will see how this can be done later on this article.

Delete Operation

To verify this option we will be running a DELETE operation on a newly inserted row.

   1: USE AdventureWorks
   2: GO 
   3: DELETE 
   4: FROM [HumanResources].[Shift] 
   5: WHERE ShiftID = 4 
   6: GO

Once this script is run, we can see the contents of  our tables HumanResources.Shift and cdc.HumanResources_Shift_CT.

clip_image011

Due to the DELETE operation, we now have only three rows in the tracked table HumanResources.Shift We can see the deleted row visible in the tracking table as new entry. The value of_operation is 4 , meaning that this is a delete operation.

Change Data Capture and Operations

We have now verified that, by using CDC, we are able to capture all the data  affected by DML operation. In the tracked table we have four values of the operation. We can see this operation’s value in the following image.

clip_image012

Understanding Update mask

It is important to understand the Update mask column in the tracking table. It is named as _$update_mask. The value displayed in the field is hexadecimal but is stored as binary.

In our example we have three different operations. INSERT and DELETE operations are done on the complete row and not on individual columns. These operations are listed marked masked with 0x1F is translated in binary as 0b11111, which means all the five columns of the table.

In our example, we had an UPDATE on only two columns – the second and fifth column. This is represented with 0x12 in hexadecimal value ( 0b10010 in binary).  Here, this value stands for second and fifth value if you look at it from the right, as a bitmap. This is a useful way of finding out which columns are being updated or changed.

The tracking table shows  two columns which contains the suffix lsn in them i.e. _$start_lsn and _$end_lsn. These two values correspond to the  Log Sequential Number. This number is associated with committed transaction of the DML operation on the tracked table.

Disabling Change Data Capture on a table

Disabling this feature is very simple. As we have seen earlier, if we have to enable CDC we have to do this in two steps – at table level and at database level,: In the same way, when we have to disable this feature, we can do this at same two levels. Let us see both of them one after one.

For dropping any tracking of any table we need three values the Source Schema, the Source Table name, and the Capture Instance. It is very easy to get schema and table name. In our case, the schema is HumanResource and table name is Shift, however we do not know the name of the Capture Instance. We can retrieve it very easily by running following T-SQL Query.

   1: USE AdventureWorks; 
   2: GO 
   3: EXEC sys.sp_cdc_help_change_data_capture 
   4: GO

this will return a result which contains all the three required information for disabling CDC ona table.

clip_image013

The Stored Procedure  sys.sp_cdc_help_change_data_capture provides lots of other useful information as well. Once we have name of the capture instance, we can disable tracking of the table by running this T-SQL query.

   1: USE AdventureWorks;
   2:  
   3: GO
   4:  
   5: EXECUTE sys.sp_cdc_disable_table
   6:  
   7: @source_schema = N'HumanResources',
   8:  
   9:     @source_name = N'Shift',
  10:  
  11:     @capture_instance = N'HumanResources_Shift';
  12:  
  13: GO
  14:  

Once Change Data Capture is disabled on any table, it drops the change data capture table as well all the functions which were associated with them. It also deletes all the rows and data associated with this feature from all the system tables and changes relevant data in catalog views.

clip_image014

In our example, we can clearly see that capture table cdc.HumanResources_Shift_CT is dropped.

Disable Change Data Capture Feature on Database

This is the easiest task out of all process. Running following T-SQL query will disable CDC on whole database.

   1: USE AdventureWorks
   2: GO 
   3: EXEC sys.sp_cdc_disable_db 
   4: GO

This Stored Procedure will delete all the data, functions, tables related to CDC. If this data is needed for any reason, you must take a  backup  before dropping CDC from any database

5. Capture Selected Column

When CDC is enabled on any table, it usually captures the data of all the columns. During INSERT or DELETE operations, it is necessary to capture all the data but in UPDATE operations  only the data of the updated columns are required. CDC is not yet advanced enough to provide this kind of dynamic column selection but CDC can let you select the columns from which changes to data should be captured from the beginning.

This stored procedure should be run in the context of each database to enable it at database level. Following script will enable CDC in AdventureWorks database.

   1: USE AdventureWorks
   2: GO 
   3: EXEC sys.sp_cdc_enable_db 
   4: GO

Now we will enable this feature at table level but for selected columns of ShiftID and Name only. This script will enable table-level change data capture for only two columns.

   1: USE AdventureWorks
   2: GO 
   3: EXEC sys.sp_cdc_enable_table 
   4: @source_schema = N'HumanResources', 
   5: @source_name   = N'Shift', 
   6: @role_name     = NULL, 
   7: @captured_column_list = '[ShiftID],[Name]' 
   8: GO

So what’s in the system table which will be created by data capturing purpose in AdventureWorks Database?

clip_image015

So you can see that there are now only two rows which are tracked.

We will change the data of one of the columns that weren’t specified so as to see  the value in cdc.HumanResources_Shift_CT table.

Before we start let us first select from both of the table and observe its content.

   1: USE AdventureWorks
   2: GO 
   3: SELECT * 
   4: FROM HumanResources.Shift 
   5: GO 
   6: USE AdventureWorks
   7: GO 
   8: SELECT * 
   9: FROM cdc.HumanResources_Shift_CT 
  10: GO

Here is the result.

clip_image008[1]

The original table HumanResources.Shift now has three rows in it; whereas  table cdc.HumanResources_Shift_CT is totally empty. Lets update ModifiedDate for ShiftID =1 and see if that record creates an entry in the tracking table.

   1: USE AdventureWorks
   2: GO 
   3: UPDATE [HumanResources].[Shift] 
   4: SET        ModifiedDate = GETDATE() 
   5: WHERE  ShiftID = 3 
   6: GO

Now to check the contents of the tracking table  table cdc.HumanResources_Shift_CT and see whether that change is captured.

clip_image016

The tracking table is empty because it only tracks the changes which it contains, and it ignores any changes in other columns.

Retrieve Captured Data of Specific Time Frame

Quite often, one is asked for data to be tracked over a  time interval. If you look at the tracking data there is apparently no time captured at all. It always provides all the information. However, there are few fields which can definitely help us out i.e. _$start_lsn . LSN stands for Last Sequence Number. Every record in transaction log is uniquely identified by a LSN. They are always incrementing numbers.

LSN numbers are always associated with time and their mapping can be found after querying system table  cdc.lsn_time_mapping. This table is one of the tables which was created whenAdventureWorks database was enabled for CDC. You can run this query to get all the data in the table  cdc.lsn_time_mapping.

   1: USE AdventureWorks
   2: GO 
   3: SELECT * 
   4: FROM cdc.lsn_time_mapping 
   5: GO 

When  this query is run it will give us all the rows of table. It is a little difficult to find the  necessary information from all the data. The usual case is when we need to inspect a change that occurred in a particular  time period.

clip_image017

We can find the time that corresponds to the LSN by using the system function sys.fn_cdc_map_time_to_lsn. If we want all the changes done yesterday, we can run this function as described below and it will return all the rows from yesterday.

Before we run this query let us explore two table valued functions (TVF) in AdventureWorks database. You can see that there are two new TVF are created with schema cfc. These functions are created when table level CDC was enabled.

clip_image018

The function cdc.fn_cdc_get_all_changes_HumanResources_Shift can be used to get events that occurred over a particular time period. You can run this T-SQL script to get event happened during any specific time period. In our case, we will be retrieving this data for the past 24 hours.

Following query should do retrieve data which was modified in the past 24 hours..

   1: USE AdventureWorks
   2: GO 
   3: DECLARE @begin_time DATETIME, @end_time DATETIME, @begin_lsn BINARY(10), @end_lsn BINARY(10); 
   4: SELECT @begin_time = GETDATE()-1, @end_time = GETDATE(); 
   5: SELECT @begin_lsn = sys.fn_cdc_map_time_to_lsn('smallest greater than', @begin_time); 
   6: SELECT @end_lsn = sys.fn_cdc_map_time_to_lsn('largest less than or equal', @end_time); 
   7: SELECT * 
   8: FROM cdc.fn_cdc_get_all_changes_HumanResources_Shift(@begin_lsn,@end_lsn,'all') 
   9: GO 

we have used relational operations in the function sys.fn_cdc_map_time_to_lsn. There can be total of four different relational operations available to use in that function:

  • largest less than
  • largest less than or equal
  • smallest greater than
  • smallest greater than or equal

This way captured data can be queried very easily and query based on time interval.

Automatic Clean Up Process

If we track every change of all the  data in our database, there is very good chance that we will outgrow the hard drive of main server. This will also lead to issues with maintenance and input/output buffer issues.

In CDC this there is automatic cleanup process that runs at regular intervals. By default the interval is of 3 days but it can be configured. We have observed that, when we enable CDC on the database, there is one additional system stored procedure created with the  name sys.sp_cdc_cleanup_change_table which cleans up all the tracked data at interval.

Summary

For years, programmers have tried to create systems that record all the changes made to the data in a database application.  At last, with SQL Server 2008, we have a robust way, CDC, that comes ‘out of the box’ to deliver this functionality in a standard way. This should be useful for auditing databases and for tracking obscure problems that require you to know exactly when and where a change to a base table was made. 
This article has been written keeping SQL Server 2008 SP1 Cumulative Update 3 in mind. I would encourage any of your suggestions or ideas on this subject as comments to the article.

 

复制代码

作者: EricHu
出处: http://blog.csdn.net/chinahuyong
微博: 腾讯
Email: 406590790@qq.com
QQ 交流:406590790 
平台博客: 【CSDN】http://blog.csdn.net/chinahuyong
         【CNBLOGS】http://www.cnblogs.com/huyong
关于作者:高级工程师、信息系统项目管理师、DBA。专注于微软平台项目架构、管理和企业解决方案,多年项目开发与管理经验,曾多次组织并开发多个大型项目,精通DotNet,DB(SqlServer、Oracle等)技术。熟悉Java、Delhpi及Linux操作系统,有扎实的网络知识。在面向对象、面向服务以及数据库领域有一定的造诣。现从事DB管理与开发、WinForm、WCF、WebService、网页数据抓取以及ASP.NET等项目管理、开发、架构等工作。
如有问题或建议,请多多赐教!
本文版权归作者和CNBLOGS博客共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,如有问题,可以通过邮箱或QQ 联系我,非常感谢。
复制代码

转载请注明:RDIFramework.NET » Introduction to Change Data Capture (CDC) in SQL Server 2008[转]

]]>
http://blog.rdiframework.net/126.html/feed 0
(八大方法、逐层深入,有你一定没见过的)使用INSERT语句向表中插入数据 http://blog.rdiframework.net/152.html http://blog.rdiframework.net/152.html#comments Wed, 05 Sep 2012 00:45:00 +0000 http://blog.scbz.org/index.php/archives/152

(八大方法、逐层深入,有你一定没见过的)

使用INSERT语句向表中插入数据(MSSQLSERVER版)

 

         做开发的同仁对于向数据库中插入数据可谓是太普通不过了,也没什么说的,一般都是采用常用的INSERT INTO [(字段列表)] VALUES(字段值列表),这样的方式进行操作。今天,我要给大家介绍的是其他一些非常规的方式,一定会让你耳目一新,眼前一亮。

         进入今天的主题,以了便于操作,我们首先建立一个实例数据表:

USE tempdb ;

GO

CREATE TABLE Fruit

   (

      Id INT NOT NULL ,

      Name VARCHAR(100) NOT NULL ,

      Color VARCHAR(100) NULL ,

      Quantity INT DEFAULT 1

) ;

 

方式一:传统方式

 

语法:INSERT (column_list) VALUES (value_list);

 

我们插入一条数据到数据表中。

   INSERT  INTO Fruit

        ( Id, Name, Color, Quantity )

VALUES  ( 1, ‘Banana’, ‘Yellow’, 1 ) ;

这种方式是我们常用的一种插入数据的方式,给出所有的列名集合与值集合。

 

方式二:省略列集合

 

   INSERT  INTO Fruit

VALUES  ( 2, ‘Grapes’, ‘Red’, 15 ) ;

 

这种方式我们省略了列名集合,这种方式值集合必须与数据表定义时列的顺序相一至,必须赋的值不能省略

 

方式三:插入部分字段

  

   INSERT  INTO Fruit

        ( Id, Name )

VALUES ( 3, ‘Apples’) ;

这种方式,我们不必给出所有字段列表,只插入我们需要的字段即可。

以上三种方式是我们在实际使用中最为常见的插入数据的方式,下面给出的也许会让你眼前一亮。

 

方式四:一条插入语句插入多条数据

 

   INSERT  INTO Fruit

        ( Id, Name, Color, Quantity )

   VALUES ( 4, ‘Apples’, ‘Red’, 10 ),

            ( 5, ‘Peaches’, ‘Green’, 7 ),

        ( 6, ‘Pineapples’, ‘Yellow’, 5 ) ;

 

在这儿,我用一条插入语句同时插入了三条不同的数据到Fruit数据表中。

 

方式五:使用SELECT语句同时插入多条数据。

 

   INSERT  INTO Fruit

        ( Id ,

          Name ,

          Color,

          Quantity

        )

        SELECT  7 +( 6 Id ) ,

                Name,

                ‘White’,

                Quantity

        FROM    Fruit

        WHERE   Id > 3

        ORDER BY Id DESC ;

 

在方式五中,我通过选择Fruit表中 Id 大于 3 记录插入到Fruit表中。这是从水果表中现有的值派生出来的。

 

方式六:使用存储过程插入数据。

 

首先、我们建立一个存储过程如下:

   CREATE PROC HybridFruit

   AS

    SELECT  b.Id + 9 ,

            a.Name + b.name

    FROM    Fruit a

            INNERJOIN Fruit b ON a.Id = 9 b.Id ;

GO  

 

现在我们使用上面建立的存储过程进行数据的插入,方法如下:

   INSERT  INTO Fruit

        ( Id, Name )

        EXECUTE HybridFruit;

 

 

方式七:使用OUTPUT子句输出插入的数据以供外部使用。

  

   INSERT  INTO Fruit

    ( Id, Name )

   OUTPUT  INSERTED.*

VALUES ( 18, ‘PieCherries’ ) ;

 

在这儿,我们通过使用OUTPU子句输出了同步插入的数据。

 

方式八:通过OUTPUT子句把结果返回给客户端。

 

为了方便演示,我先把Fruit表的Id列设为自动增长列。

   ALTER TABLE Fruit

   DROP COLUMN Id ;

   ALTER TABLE Fruit

ADD Id INTIDENTITY ;

现在我们来通过OUTPUT子句插入值并把结果返回给客户端。

DECLARE @INSERTED AS TABLE ( Id INT, Name VARCHAR(100) ) ;

 

使用OUTPUT插入

INSERT  INTO Fruit

       ( Name,Color )

OUTPUT  INSERTED.Id, INSERTED.Name

        INTO @INSERTED

VALUES ( ‘Bing Cherries’, ‘Purple’ ),

       ( ‘Oranges’, ‘Orange’ ) ;

显示插入的值

SELECT  *

FROM    @INSERTED ;

 

 

以上就是八种方式,也许对你有用,仅作参考使用,谢谢~!

转载请注明:RDIFramework.NET » (八大方法、逐层深入,有你一定没见过的)使用INSERT语句向表中插入数据

]]>
http://blog.rdiframework.net/152.html/feed 0