T-SQL开发——ID处理篇
数据库自增ID功能中Identity、Timestamp、Uniqueidentifier的区别:
问题现象:
一般序号的产生,对于一般程序员而言,都是使用T-SQL命令来实现。先读取表中的最大需要,然后累加一,再插回数据库,这样做是相当危险的。因为如果事务机制没有处理好,就会出现同时间内取得同一序号。结果可想而知。为了避免这种情况,SQLServer在内部已经提供了一定的机制来协助处理。
说明:
1、数据表级别识别——Identity:
use tempdb
go
--创建测试用的数据表
create table Employee
(
en int not null identity, --自增ID
ename varchar(50), --员工名称
keyDT datetime --创建日期
); --插入数据,不指定列名
insert into Employee
values('Lewis','2012/6/23'); --插入数据,指定列名,但不指定自增列
insert into Employee(ename,keyDT)
values('Ada','2012/6/24')
go
select * from Employee

针对Identity,还有一些使用技巧:
use tempdb
go
--创建测试用的数据表
CREATE TABLE T1
(
XID INT NOT NULL IDENTITY,
XNAME VARCHAR(10)
);
GO
CREATE TABLE T2
(
YID INT NOT NULL IDENTITY,
YNAME VARCHAR(10)
);
GO --插入3条数据到T2表中
INSERT INTO T2(YNAME) VALUES('name1'),('name2'),('name3');
GO --建立T1的INSERT触发器,用于将T1的数据自动新增到T2的数据表中
CREATE TRIGGER tri_t1 ON t1
after insert
as
insert into t2(YNAME)
select xname from inserted
GO --编写存储过程将数据新增到t1数据表自动返回scope_identity()和@@Identity的值
create PROC uspTest
(
@name varchar(10)
)
as
insert into t1 values(@name)
select @@IDENTITY '@@identity',SCOPE_IDENTITY() 'scope_identity','In Proc'as 'scope'
go --使用存储过程测试:当scope_identity()是1时,@@identity是4
EXEC uspTest 'Ada'
注意:Identity作为自增时,就算在相同事件里面都不会产生相同的序号,所以可以但非强制作为表的主索引键。
2、数据库级别标识——timestamp :
use tempdb
go
--创建南方员工的数据表
CREATE TABLE Employee_S
(
en timestamp not null,--自增二进制ID
ename varchar(50),--员工名
keyDT datetime --创建时间
) --创建中部员工的数据表
CREATE TABLE Employee_C
(
en timestamp not null,--自增二进制ID
ename varchar(50),--员工名
keyDT datetime --创建时间
) --创建北方员工的数据表
CREATE TABLE Employee_N
(
en timestamp not null,--自增二进制ID
ename varchar(50),--员工名
keyDT datetime --创建时间
) --插入数据:
insert into Employee_S(ename,keyDT) values('Sname',GETDATE())
insert into Employee_C(ename,keyDT) values('Cname',GETDATE())
insert into Employee_N(ename,keyDT) values('Nname',GETDATE())
--显示数据
select '南方',* from Employee_S
union all
select '中部',* from Employee_C
union all
select '北方',* from Employee_N

执行脚本后看到数据的日期是一样的,但是en列不一样,而这种效果是identity做不到的。
3、使用NEWID()搭配UniqueIdentifier数据产生全球唯一标识码:
use tempdb
go
--创建南方员工的数据表
CREATE TABLE Employee_GUID
(
en uniqueidentifier not null,--自增二进制ID
ename varchar(50)--员工名 ) --插入数据:
insert into Employee_GUID(en,ename) values(newid(),'Sname'),(newid(),'Cname'),(newid(),'Nname') --显示数据,为了证明不唯一,可以使用GROUP BY来检验:
--源数据
select *
from Employee_GUID
--检验数据
select count(1) 'Total',en
from Employee_GUID
group by en
having count(1)>1
另外,在前面提到过,可以使用NEWID()和NEWSEQUENTIALID()产生, 考虑NEWID()和NEWSEQUENTIALID()两者在使用上的区别:
use tempdb
go
--产生NEWID()和NEWSEQUENTIALID():
SET NOCOUNT ON
DECLARE @T TABLE (newSN uniqueidentifier,seqSN uniqueidentifier default (NEWSEQUENTIALID()))
DECLARE @I INT
SET @I=1
WHILE @I<=10
BEGIN
INSERT INTO @T VALUES(NEWID(),DEFAULT)
SET @I=@I+1
END SELECT * FROM @T
SET NOCOUNT OFF
执行后可以看到下图:注意每台机器值会不一样

注意事项:
通过存储过程实现定制化产生序号方式:
问题现象:
说明:
use tempdb
go
--创建当天序号表
create table tabSN(sn int,sndt datetime)
go
--创建历史序号表
create table tabSNHist(sn INT,sndt datetime)
go --
create proc uspSN
(
@sn char(14) output
)
as
--开始事务
set xact_abort on
begin transaction --判断序号表是否有数据,若没有则新增一条数据
if (select count(1) from tabSN)=0
begin
insert into tabSN values(000000,GETDATE())
end --取出序号表中的日期
DECLARE @sndt datetime
set @sndt=(select sndt from tabSN); --判断是否发生跨天情况,,若是则移动到历史表
if CONVERT(char(10),@sndt,111)<>CONVERT(char(10),getdate(),111)
begin
insert into tabSNHist select * from tabSN;
truncate table tabSN;
insert into tabSN values(000000,getdate())
end
--将号码累加1,作为最后操作时间
update tabsn set sn=sn+1 ,sndt=GETDATE()
--出去序号,转换成YYYYMMDDNNNNNN
SELECT @sn=CONVERT(VARCHAR(10),SNDT,112)+RIGHT('000000'+CONVERT(VARCHAR(6),SN),6)
FROM tabSN;
COMMIT TRANSACTION
GO --使用存储过程产生序号
DECLARE @SN CHAR(14)
EXEC uspSN @SN OUTPUT
SELECT @SN 'SN'
可以做一个简单的压力测试来验证这种写法是否会产生重复:
--压力测试 --创建表存放测试结果
create table test
(
sn char(14),
sdt datetime ,
scomm varchar(100)--谁执行了存储过程
)
以下代码在4个窗口中同时执行:
declare @cnt int
set @cnt=1
while @cnt<=100
begin
--执行存储过程
declare @sn char(14)
exec uspsn @sn output
--将结果新增到测试数据表
insert into test
select @sn,GETDATE(),'SPID'+convert(varchar(5),@@spid)
set @cnt=@cnt+1
waitfor delay '00:00:01'
end
go
可以使用以下语句来测试是否有重复:
select count(1), sn from test group by sn having count(1)>1
当然,结果是没有重复的。
--检查是否发生跳号:
SET NOCOUNT ON
DECLARE @T TABLE (TID INT)
DECLARE @MAX INT ,@MIN INT
SET @MIN=(SELECT CONVERT(INT,RIGHT(MIN(SN),6)) FROM TEST)
SET @MAX=(SELECT CONVERT(INT,RIGHT(MAX(SN),6)) FROM TEST)
WHILE @MIN<=@MAX
BEGIN
INSERT INTO @T VALUES(@MIN)
SET @MIN=@MIN+1
END
SELECT TID '不连续号码' FROM @T EXCEPT SELECT CONVERT(INT,RIGHT(SN,6)) FROM TEST
SET NOCOUNT OFF
通过检查是没有跳号的。
select * from test order by sn

通过INSTEAD OF 触发器,实现定制化序号:
问题现象:
说明:
解决方法:
USE TEMPDB
GO
--创建订单表,订单号是主索引键不可以重复
--创建时间使用GETDATE()值
CREATE TABLE FruitOrderList
(
orderID varchar(20) not null primary key,
prodID int,
qty int,
region varchar(10),
keyinDT datetime default (getdate())
);
GO
--创建INSTEAD OF触发器
CREATE TRIGGER Tri_Int_FruitOrderList ON FruitOrderList
INSTEAD OF INSERT
AS SET NOCOUNT ON
declare @oSN varchar(20) --产生新序号规则=日期+(总笔数+1)
SELECT @oSN=CONVERT(VARCHAR(10),GETDATE(),112)+'.'+RIGHT('000000'+CONVERT(VARCHAR(6),COUNT(1)+1),6)
FROM FruitOrderList
WHERE CONVERT(char(10),keyinDT,111)=CONVERT(CHAR(10),GETDATE(),111) --重新进行数据新增操作
INSERT INTO FruitOrderList
SELECT @oSN,prodID,qty,region,keyinDT
FROM inserted
SET NOCOUNT OFF
GO
然后可以尝试做一下批量插入:
--测试操作:
--新增数据,注意订单编号是自动产生:
INSERT INTO FruitOrderList VALUES(NULL,3,30,'A',GETDATE())
INSERT INTO FruitOrderList VALUES(NULL,6,10,'B',GETDATE())
INSERT INTO FruitOrderList VALUES(NULL,9,20,'C',GETDATE())
INSERT INTO FruitOrderList VALUES(NULL,12,40,'D',GETDATE())
SELECT * FROM FruitOrderList
GO
从结果可以看到:确实达到了想要的效果.

注意事项:
在前端应用程序输出时自动加上序号:
问题:在前端应用程序展现数据时,希望能自动加上序号。
解决方法:
USE AdventureWorks
GO
--使用FirstName进行序号的输出排序
SELECT ROW_NUMBER() OVER(ORDER BY FirstName),FirstName,JobTitle,EmailAddress
FROM HumanResources.vEmployee
WHERE JobTitle LIKE '%Engineer%'
GO

T-SQL开发——ID处理篇的更多相关文章
- SQL开发技巧(二)
本系列文章旨在收集在开发过程中遇到的一些常用的SQL语句,然后整理归档,本系列文章基于SQLServer系列,且版本为SQLServer2005及以上-- 文章系列目录 SQL开发技巧(一) SQL开 ...
- SQL开发技巧(二) 【转】感觉他写的很好
本文转自: http://www.cnblogs.com/marvin/p/DevelopSQLSkill_2.html 本系列文章旨在收集在开发过程中遇到的一些常用的SQL语句,然后整理归档,本系列 ...
- SQL开发中容易忽视的一些小地方(二)
原文:SQL开发中容易忽视的一些小地方(二) 目的:继上一篇:SQL开发中容易忽视的一些小地方(一) 总结SQL中的null用法后,本文我将说说表联接查询. 为了说明问题,我创建了两个表,分别是学生信 ...
- SQL开发中容易忽视的一些小地方(四)
原文:SQL开发中容易忽视的一些小地方(四) 本篇我想针对网上一些对于非聚集索引使用场合的某些说法进行一些更正. 下面引用下MSDN对于非聚集索引结构的描述. 非聚集索引结构: 1:非聚集索引与聚集索 ...
- [No0000195]NoSQL还是SQL?这一篇讲清楚
随着大数据时代的到来,越来越多的网站.应用系统需要支撑海量数据存储,高并发.高可用.高可扩展性等特性要求. 传统的关系型数据库在应付这些已经显得力不从心,并暴露了许多难以克服的问题. 由此,各种各样的 ...
- IOS开发数据存储篇—IOS中的几种数据存储方式
IOS开发数据存储篇—IOS中的几种数据存储方式 发表于2016/4/5 21:02:09 421人阅读 分类: 数据存储 在项目开发当中,我们经常会对一些数据进行本地缓存处理.离线缓存的数据一般都 ...
- 一步一步构建手机WebApp开发——页面布局篇
继上一篇:一步一步构建手机WebApp开发——环境搭建篇过后,我相信很多朋友都想看看实战案例,这一次的教程是页面布局篇,先上图: 如上图所示,此篇教程便是教初学者如何快速布局这样的页面.废话少说,直接 ...
- 从0开始搭建SQL Server AlwaysOn 第一篇(配置域控)
从0开始搭建SQL Server AlwaysOn 第一篇(配置域控) 第一篇http://www.cnblogs.com/lyhabc/p/4678330.html第二篇http://www.cnb ...
- iOS开发——实战OC篇&环境搭建之Xib(玩转UINavigationController与UITabBarController)
iOS开发——实战OC篇&环境搭建之Xib(玩转UINavigationController与UITabBarController) 前面我们介绍了StoryBoard这个新技术,和纯技术 ...
随机推荐
- iOS_ScrollView的自己主动布局
ScrollView的自己主动布局稍显麻烦.但也是有规律可循, 下面就是仅竖向滑动的scrollView加入约束的固定做法 1.在控制器的view加入一个label.取名做anchor 2.给anch ...
- iOS 获取高速随机路径sandbox目录
NSLog(@"%@", NSHomeDirectory());//沙盒主目录 NSLog(@"%@", NSTemporaryDirectory());//砂 ...
- 分布式文件系统FastDFS介绍和配置过程
http://ylw6006.blog.51cto.com/470441/948729/ 由于网站使用nfs共享方式保存用户上传的图片,附件等资料,然后通过apache下载的方式供用户访问,在网站架构 ...
- Ubuntu下使用虚拟机安装Windows XP(sunvirtualbox)
看完这个教程之后,网银?淘宝?CS?魔兽?---一个都不能少!好了,教程开始! 首先下载虚拟机,下载地址:http://download.virtualbox.org/virtualbox/3.1.0 ...
- UML相关工具一览
http://www.cnblogs.com/chehaoj/p/3478003.html TopCoder UML Tool 1.2.6 TopCoder, Inc http://www.topco ...
- Android 反编译(一,apktool+smail2java)
一:解压缩(获取图片等资源) 对于apk中丰富的资源,假设我们在练习的时候须要引用某些apk中的资源文件时,最简单的办法使用解压缩工具对apk进行解压缩,然后在对应的文件夹下查找须要的资源文件. 二: ...
- Vue.js学习与理解
Vue.js(读音 /vjuː/, 类似于 view)是一个构建数据驱动的 web 界面的库.Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件. Vue.js 自身不 ...
- Ising模型(伊辛模型)
Ising模型(伊辛模型)是一个最简单且能够提供非常丰富的物理内容的模型.可用于描写叙述非常多物理现象,如:合金中的有序-无序转变.液氦到超流态的转变.液体的冻结与蒸发.玻璃物质的性质.森林火灾.城市 ...
- 轮播图片 高效图片轮播,两个imageView实现
该轮播框架的优势: 文件少,代码简洁 不依赖任何其他第三方库,耦合度低 同时支持本地图片及网络图片 可修改分页控件位置,显示或隐藏 自定义分页控件的图片,就是这么个性 自带图片缓存,一次加载,永久使用 ...
- Eclipse正确导入第三方project
前言 昨晚,在不同的Android做出最终的在线测试时间,在其他平台上正常升级的提示突然报告出来"java.lang.NoClassDefFoundError"误.拉什adb lo ...