我正在开发一个管理车辆工单的系统。工单的ID由以下组成:OT-001-16
。
OT-
是字符串,001
是计数器,后跟-
字符,最后数字16
是当前年份。
示例:
如果当前年份是2018年,则ID应为OT-001-18
。
问题是当年份发生变化时,计数器必须从001
重新开始。我有一个存储过程来做到这一点,但我认为我做了很多工作。
这是我的存储过程代码:
CREATE PROCEDURE ot (@name varchar(100), @area varchar(100), @idate varchar(100), @edate varchar(100))
AS
BEGIN
SET NOCOUNT ON;
DECLARE @aux varchar(100);
DECLARE @aux2 varchar(100);
DECLARE @aux3 int;
DECLARE @aux4 varchar(100);
SELECT @aux = id_workorder FROM idaux;
IF (@aux IS NULL)
SET @aux = CONCAT('OT-000-', RIGHT(YEAR(GETDATE()), 2));
SET
@aux2 = SUBSTRING(
@aux, CHARINDEX('-', @aux) + 1,
LEN(@aux) - CHARINDEX('-', @aux) - CHARINDEX('-', REVERSE(@aux)));
SET @aux3 = CAST(@aux2 AS int) + 1;
SET @aux4 = @aux3;
IF @aux3 < 1000
IF @aux3 >= 10
SET @aux4 = CONCAT('0', @aux4);
ELSE
SET @aux4 = CONCAT('00', @aux4);
ELSE
SET @aux4 = @aux4;
DECLARE @f varchar(100);
DECLARE @y varchar(50);
SELECT TOP 1
@y = id_workorder
FROM workorder
WHERE (RIGHT(id_workorder, 2)) = (RIGHT(YEAR(GETDATE()), 2))
ORDER BY id_workorder DESC;
DECLARE @yy varchar(10);
SET
@yy = RIGHT(@y, 2);
DECLARE @yn varchar(10);
SET
@yn = RIGHT(YEAR(GETDATE()), 2);
BEGIN
IF @yn = @yy
BEGIN
DECLARE @laux varchar(20)
SET @f = 'OT-' + @aux4 + '-' + RIGHT(YEAR(GETDATE()), 2);
INSERT INTO workorder (id_workorder, name, area, initial_date, end_date)
VALUES (@f, @name, @area, @idate, @edate);
SELECT
@laux = id_workorder
FROM idaux
IF (@laux IS NULL)
BEGIN
INSERT idaux (id_workorder) VALUES (@f);
END
ELSE
BEGIN
UPDATE idaux SET id_workorder = @f;
END
END
ELSE
BEGIN
SET @f = CONCAT('OT-001-', (RIGHT(YEAR(GETDATE()), 2)));
INSERT INTO workorder (id_workorder, name, area, initial_date, end_date)
VALUES (@f, @name, @area, @idate, @edate);
SELECT @laux = id_workorder FROM idaux;
IF (@laux IS NULL)
BEGIN
INSERT idaux (id_workorder) VALUES (@f);
END
ELSE
BEGIN
UPDATE idaux SET id_workorder = @f;
END
END
END
END
基本上,我创建了一个辅助表来保存最后的工作订单ID,然后从这个名为idaux
的表中获取ID,并通过字符串处理将其与新的可能ID进行比较。然后,如果保存的最后一个ID的年份等于当前年份,则计数器增加,但如果不是,计数器重新启动到001,则在辅助表中更新新ID,并将工作订单插入到表{{1 }}
我的存储过程有效,但我需要您的帮助才能优化存储过程。关于评论的任何问题。
答案 0 :(得分:1)
根据您的代码,有许多观察结果可以改变以优化并保证您的结果。
我不知道你的表结构,但似乎你使用自然键作为你的ID。
INT
/ BIGINT
)不仅可以提高表连接的效率(不需要字符串),还可以在当前设计中添加另一层安全性。 OT-001-05
有三个元素:OT
是一种工作订单,001
是ID,15
是年份。目前,OT确定决定年份的ID。 SELECT @aux = id_workorder FROM idaux;
MAX(id_workorder)
,您的结果也无法正常运行。由于这是一个VARCHAR,因此最左边的字符的最大值将不会被返回。 @aux, CHARINDEX('-', @aux) + 1,
LEN(@aux) - CHARINDEX('-', @aux) - CHARINDEX('-', REVERSE(@aux)));
这很好,但总的来说,通过将所有这三个元素分成自己的变量,你可以使代码更清晰,更容易调试。你仍在使用你的方法,但简化了一点(个人而言,CHARINDEX
可能会很痛苦)。
SET @aux = @Type -- 'OT'
SET @aux2 = @ID -- The result of your previous code
SET @aux3 = @Year -- your YY from GETDATE()
-- then join
SET @Work_Order = CONCAT(@aux, '-', @aux2, '-', @aux3)
<强>更新强>
目前,idaux
中的列在您的列的MIDDLE中具有ID。 这将产生灾难性的结果,因为ID的任何比较都会发生在列的中间。这意味着您最多可以使用PATINDEX
,但仍在桌面上执行表扫描。没有索引(保存FULLTEXT
)将被利用得更少优化。
我应该补充一点,如果您将ID元素放入其自己的列中,您可能会发现在列上使用BINARY
排序规则将提高其性能。注意我没有测试过在混合列上尝试BINARY排序规则
答案 1 :(得分:1)
以下是我如何设置存储过程和基础表以跟踪您的工单:
create database tmpWorkOrders;
go
use tmpWorkOrders;
go
/*
The work order ID (as you wish to see it) and the
work order counter (per year) will be separated into
two separate columns (with a unique constraint).
The work order ID (you wish to see) is automatically
generated for you and stored "persisted":
http://stackoverflow.com/questions/916068/sql-server-2005-computed-column-is-persisted
*/
create table WorkOrders
(
SurrogateKey int identity(1, 1) primary key not null,
WorkOrderYear int not null,
WorkOrderCounter int not null,
WorkOrderID as
N'OT-' + right(N'000' + cast(WorkOrderCounter as nvarchar), 3)
+ N'-' + right(cast(WorkOrderYear as nvarchar), 2)persisted,
WorkOrderDescription nvarchar(200),
constraint UQ_WorkOrderIDs
unique nonclustered (WorkOrderYear, WorkOrderCounter)
);
go
create procedure newWorkOrder
(@WorkOrderYear int = null,
@WorkOderCounter int = null,
@WorkOrderDescription nvarchar(200) = null
)
as
begin
/*
If no year is given the the current year is assumed
*/
if @WorkOrderYear is null
begin
set @WorkOrderYear = year(current_timestamp);
end;
/*
If no work order counter (for the above year) is given
then the next available one will be given
*/
if @WorkOderCounter is null
begin
set @WorkOderCounter
= isnull(
(
select max(WorkOrderCounter)
from WorkOrders
where WorkOrderYear = @WorkOrderYear
) + 1,
0
);
end;
else
/*
If a work order counter has been passed to the
stored procedure then it must be validated first
*/
begin
/*
Does the work order counter (for the given year)
already exist?
*/
if exists
(
select 1
from dbo.WorkOrders as wo
where wo.WorkOrderYear = @WorkOrderYear
and wo.WorkOrderCounter = @WorkOderCounter
)
begin
/*
If the given work order counter already exists
then the next available one should be assigned.
*/
while exists
(
select 1
from dbo.WorkOrders as wo
where wo.WorkOrderYear = @WorkOrderYear
and wo.WorkOrderCounter = @WorkOderCounter
)
begin
set @WorkOderCounter = @WorkOderCounter + 1;
end;
end;
end;
/*
The actual insert of the new work order ID
*/
insert into dbo.WorkOrders
(
WorkOrderYear,
WorkOrderCounter,
WorkOrderDescription
)
values
(@WorkOrderYear,
@WorkOderCounter,
@WorkOrderDescription
);
end;
go
/*
Some test runs with the new table and stored procedure...
*/
exec dbo.newWorkOrder @WorkOrderYear = null,
@WorkOderCounter = null,
@WorkOrderDescription = null;
exec dbo.newWorkOrder @WorkOrderYear = null,
@WorkOderCounter = 3,
@WorkOrderDescription = null;
exec dbo.newWorkOrder @WorkOrderYear = null,
@WorkOderCounter = 0,
@WorkOrderDescription = null;
exec dbo.newWorkOrder @WorkOrderYear = null,
@WorkOderCounter = 0,
@WorkOrderDescription = null;
exec dbo.newWorkOrder @WorkOrderYear = null,
@WorkOderCounter = 0,
@WorkOrderDescription = null;
/*
...reviewing the result of the above.
*/
select *
from dbo.WorkOrders as wo;
注意,&#34;下一个可用&#34;工作单计数器一旦给定(1),最大+ 1和一次(2)增加,直到它不再违反表上的唯一键约束。像这样,你有两种不同的可能性。