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 UITableView的Section Footer加入button
郝萌主倾心贡献,尊重作者的劳动成果.请勿转载. 假设文章对您有所帮助,欢迎给作者捐赠.支持郝萌主,捐赠数额任意.重在心意^_^ 我要捐赠: 点击捐赠 Cocos2d-X源代码下载:点我传送 在处理UI ...
- swfobject.js的简单配置
因为工作需要在网页中迁入flash,开发过程中,发现直接使用embed自己开发的话需要考虑各种兼容性,也比较麻烦, 网上也找了几个相关的插件,比较使用之下,发现swfobject.js这一款还是蛮不错 ...
- HUNNU11354:Is the Name of This Problem
http://acm.hunnu.edu.cn/online/?action=problem&type=show&id=11354&courseid=0 Problem des ...
- Learning To Rank之LambdaMART前世今生
1. 前言 我们知道排序在非常多应用场景中属于一个非常核心的模块.最直接的应用就是搜索引擎.当用户提交一个query.搜索引擎会召回非常多文档,然后依据文档与query以及用户的相关程度对 ...
- Bootstrap网站模板
根据一篇文章,我再想想写下,无意义,他决定收手. 或者直接做一个简单的基本的模板它 主要知识点包含栅格系统.响应式图片.导航条(固定在顶部和底部).搜索框等等 详细每一个知识点不再赘述,參考Boots ...
- 队列优化和斜率优化的dp
可以用队列优化或斜率优化的dp这一类的问题为 1D/1D一类问题 即状态数是O(n),决策数也是O(n) 单调队列优化 我们来看这样一个问题:一个含有n项的数列(n<=2000000),求出每一 ...
- swift学习笔记(六)析关闭过程和使用分配给属性的默认值
一.通过关闭和功能的默认实现财产值 当存储属性默认值需要定制,能为客户提供通过关闭或全局函数的自定义默认值. 注意:全局函数的结构,和枚举使用keywordstatic大喊 用classkeyw ...
- 【Android开发经验】移动设备的“声波通信/验证”的实现——SinVoice开源项目介绍(一)
转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992 在APP市场上,常常有一些充满新意的应用让我们眼前一亮,比方微信的面对面加好友,支付宝的声波支付等等,都是通 ...
- ACdream 之ACfun 题解
A - ACfun Time Limit: 2000/1000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Others) SubmitSta ...
- C语言程序代写(qq:928900200)
1cs3157 – Advanced ProgrammingSummer 2014, Project 1, 150 pointsJune 17, 2014Follow these step-by-st ...