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这个新技术,和纯技术 ...
随机推荐
- poj 3280 Cheapest Palindrome ---(DP 回文串)
题目链接:http://poj.org/problem?id=3280 思路: dp[i][j] :=第i个字符到第j个字符之间形成回文串的最小费用. dp[i][j]=min(dp[i+1][j]+ ...
- Justinmind教程(3)——管理原型
如已经描述Justinmind概述和Justinmind简单的计算器功能 Justinmind使用教程(1)--概述部分 Justinmind使用教程(2)--计算表达式及条件用法 本章将回到最原始的 ...
- JAVA 保留两位小数的四种方法
import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.NumberFormat; publiccl ...
- linux下安装cmake和mysql遇到的问题总结
首先是在安装cmake的过程中遇到的问题: 1.開始使用yum命令安装时,不知道为什么一直不行,然后就准备wget 来先下载压缩包,再手动编译. 因为网络限制,wget不能下载外网的东西一直显示con ...
- (九)通过几段代码,理清angularJS中的$injector、$rootScope和$scope的概念和关联关系
$injector.$rootScope和$scope是angularJS框架中比較重要的东西,理清它们之间的关系,对我们兴许学习和理解angularJS框架都很实用. 1.$injector事实上是 ...
- Drupal 7 电子邮件的发送设置 SMTP, Mail System, Mime Mail
尽管Drupal自带发送email功能,可是非常多server须要SMTP验证.这个时候就须要安装 SMTP 模块. 激活 SMTP 模块 进入配置 admin/config/system/smtp ...
- crm工作机会实体
using System; using Microsoft.Xrm.Sdk; using Microsoft.Crm.Sdk.Messages; public class Opport ...
- SQL之性能优化
在实际应用中.数据库中的数据会有非常多.若要从这些数据表中检索数据,就须要对系统进行优化,提高数据库系统的响应速度,以下就是日常一些查询优化的方法. 1.创建索引 索引能够提高数据库查询的速度, ...
- 802.11(wi-fi)的PHY层(编码与调制方法)
版本概要: 802.11-2007是目前的基础版本,之前的过时版本不考虑. 2009是较新的版本,就是目前最普及的802.11n.(100Mb/s) 2012就是传说中的802.11ac,工作在5G, ...
- Gradle学习系列之一——Gradle快速入门(转)
这是一个关于Gradle的学习系列,其中包含以下文章: Gradle快速入门 创建Task的多种方法 读懂Gradle语法 增量式构建 自定义Property 使用java Plugin 依赖管理 构 ...