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这个新技术,和纯技术 ...
随机推荐
- tab功能菜单——使用tab之间不同的交换机div
需求:在web实现类型的接口tab标签效应 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvem91eXVqaWUxMTI3/font/5a6L5L2T/fo ...
- Android学习之 AChartEngine 图表绘制
Android 开源图表绘制工具AChartEngine地址:http://code.google.com/p/achartengine/ AChartEngine Android实现图表绘制和展示( ...
- 用python输出汉字字库
问题1:如果我们知道汉字编码范围是0x4E00到0x9FA5,怎么从十六进制的编码转成人类可读的字呢? 问题2:怎么把unicode编码的字写入文件呢,假设直接用open()的话,会提示Unicode ...
- 使IIS Express支持其他网络客户端访问
今天尝试利用Android客户端Web浏览器访问VS2012 IIS Express调试中的Web应用,发现这个IIS Express仅支持localhost主机名地址访问. 上网搜索找到解决方案,几 ...
- linux sort,uniq,cut,wc命令详解 (转)
sort sort 命令对 File 参数指定的文件中的行排序,并将结果写到标准输出.如果 File 参数指定多个文件,那么 sort 命令将这些文件连接起来,并当作一个文件进行排序. sort语法 ...
- InputStreamReader 和 OutputStreamWriter类使用方法简单介绍,及演示。
InputStreamReader 和 OutputStreamWriter类使用方法简单介绍. 一.InputStreamReader类 InputStreamReader 将字节流转换为字符流.是 ...
- 【Java基础】异常的简单分类与处理
Java中所有的异常都继承自Throwable类,Throwable类的已知子类有Error和Exception. Error是指系统出现的错误,这种错误出现的时候,我们的程序无能为力,所以不需要进行 ...
- JavaScript向select下拉框中加入和删除元素
JavaScript向select下拉框中加入和删除元素 1.说明 a 利用append()方法向下拉框中加入元素 b 利用remove()方法移除下拉框中最后一个元素 2.设计源代码 < ...
- android 他们定义对话框
创建一个布局文件 my_dialog.xml <?xml version="1.0" encoding="utf-8"?> <Relative ...
- UI —— 计算器
#import <UIKit/UIKit.h> @interface MyViewController :UIViewController { NSInteger _firstName; ...