SQL Server中的下一个可用数字/ ID,当不是int并包含字母时

时间:2016-10-28 20:30:56

标签: sql-server sql-server-2014

前言

我目前正在重写一个应用程序,它有一种非常有趣的方式来做某些事情。那些他们想要的东西以同样的方式完成,我对数据或它的结构没有多少控制。主要是因为他们希望它与旧程序以及整个公司使用的其他程序向后兼容。

问题

他们在整个公司使用面向公众的员工ID。它是字母E和4位数字。它以char(5)的形式存储在数据库中。所以示例ID是E1234。分配此ID是一个手动过程。问题是它开始难以维护。他们会定期获取最后使用的ID,并列出几百个可用的ID。然后,他们会在使用时删除ID。洗涤,冲洗,重复。在纸面上,他们每个ID大约有2/3被烧毁。数据库方面,有许多未使用的ID和ID可以退役,并且它们实际上具有使用的可能ID的2/5。幸运的是,该公司不太可能拥有9,999名在职员工。

解决方案

在新程序中,如果我们可以在创建新员工时为他们确定并分配可用ID,则上述问题可能会消失。我找到了很多寻找下一个可用数字的解决方案,但它们似乎只有在你使用int而不是char(5)时才会使用。是否有可能做我想做的事情,如果可以的话我该怎么办?

4 个答案:

答案 0 :(得分:3)

唯一可行的解​​决方案是使用

  • ID INT IDENTITY(1,1)列,以使SQL Server处理数值的自动增量
  • 计算的,持久的列,将该数值转换为您需要的值

所以试试这个:

CREATE TABLE dbo.Employee
  (ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
   EmployeeID AS 'E' + RIGHT('0000' + CAST(ID AS VARCHAR(4)), 4) PERSISTED,
   .... your other columns here....
  )

现在,每次在tblCompany中插入一行而未指定IDEmployeeID的值时:

INSERT INTO dbo.Employee(Col1, Col2, ..., ColN)
VALUES (Val1, Val2, ....., ValN)

然后SQL Server将自动且安全地增加ID值,而EmployeeID将包含E0001E0002等值。 ....等等 - 自动,安全,可靠,无重复。

答案 1 :(得分:1)

如果您通过employeeID列的子字符串进行排序,则可以获得最高价值。从那里,你可以添加1.工作样本:

DECLARE @tbl TABLE (col1 CHAR(5));

INSERT INTO @tbl
        ( col1 )
VALUES  ( 'E0521' ),
( 'E1542' ),
( 'E1543' )

-- prove to yourself that the ordering is correct
SELECT *
FROM @tbl 
ORDER BY CAST(SUBSTRING(col1, 2, 4) AS INT) DESC

DECLARE @max INT;
--get the max value
SELECT @max = MAX(CAST(SUBSTRING(col1, 2, 4) AS INT))
FROM @tbl

-- increment the max value and put E back on the front
SELECT 'E' + cast (@max + 1 AS CHAR(4))

答案 2 :(得分:0)

如果在2012年以上,我更喜欢使用none

SEQUENCE

<强>用法:

create sequence dbo.EmployeeId
as tinyint
start with 0
increment by 1
no maxvalue
no cycle
cache 100;

假设您只能创建序列对象,那么这应该完全符合您的需要。它的工作原理是在序列上调用-- create demo table if Object_Id(N'tempdb..#Employees') is null begin; create table #Employees ( EmployeeId char(5) not null primary key clustered (EmployeeId asc) ); -- create some collisions insert into #Employees (EmployeeId) select a.* from (values (N'E0000') , (N'E0002') , (N'E1234') ) as a (EmployeeId); end; -- insert next id declare @employeeId char(5) = 'E' + Right('0000' + Convert(varchar(4), next value for dbo.EmployeeId), 4); while exists ( select 1 from #Employees as a with (rowlock) where a.EmployeeId = @employeeId ) begin; set @employeeId = 'E' + Right('0000' + Convert(varchar(4), next value for dbo.EmployeeId), 4); end; if (Convert(int, Right(@employeeId, 4)) > 9999) begin; throw 50000, N'All EmployeeIds are currently in use.', 1; end; insert into #Employees (EmployeeId) select @employeeId as EmployeeId; -- check results select * from #Employees; ,直到它遇到一个尚未被表格使用的值。

如果您无法依赖next value for ...对象,则可以选择使用以下查询来提取下一个ID值。

SEQUENCE

答案 3 :(得分:-1)

保持简单。只有10000个可能的ID,所以这可以通过暴力解决:

  • 使用空白biodata
  • 填写所有10K ID
  • 为“已解散”行清空biodata(以允许其重复使用)

每当您需要新ID时:

select min(id) from employees
where firstname == '';

然后更新该ID的biodata,从而使其无法用作新ID:

update employees set
firstname = 'Fred'
-- etc
where id = $chosenId
and firstname == '' -- protection against concurrent first use

此方法绝对不需要对现有架构进行任何更改或添加。

注意:我选择firstname作为“数据信号量”,但您可以为实际人选择必须不为空的列。