重置 SQL 服务器中存储过程的 ID 计数器

Reset the ID counter on a stored procedure in SQL Server

我正在开发一个管理车辆工单的系统。工单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

基本上,我创建了一个辅助 table 来保存最后一个工单 ID,然后从这个名为 idaux 的 table 中获取 ID 并与新的可能 ID 进行比较字符串处理。然后,如果最后保存的 ID 的年份等于当前年份,则计数器增加,但如果不是,则计数器重新启动到 001,新 ID 在辅助 table 中更新,并将工单插入到table workorder.

我的存储过程可以正常工作,但我需要你的帮助来优化存储过程。有任何问题 post 评论。

有许多基于您的代码的观察结果,您可以更改这些观察结果以优化和保证您的结果。

我不知道你的 Table 结构,但你的 ID 似乎使用了自然键。

  • 相反,使用代理键,例如 INT/BIGINT 不仅可以提高 table 连接的效率(不需要字符串),还可能添加另一层当前设计中的安全性。
  • 或者,将列规范化为它们的标志。例如:OT-001-05有三个元素:OT是工单类型,001是ID,15是年份。目前,OT确定ID决定年份。

SELECT @aux = id_workorder FROM idaux;

  • idaux 没有被描述。它是一个单一的价值?如果是表格,请保证结果,否则将来可能会崩溃。
  • 即使你加了MAX(id_workorder),你的结果也不会如你所想。由于这是一个 VARCHAR,最左边未绑定的字符的最大值将为 return.

@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 中的列的 ID 位于列的中间。 这将产生灾难性的结果,因为任何 ID 比较都将发生在列的中间。这意味着您充其量可能会逃脱 PATINDEX 但仍在对 table 执行 table 扫描。没有索引(FULLTEXT 除外)的优化程度要低得多。

我应该补充一点,如果将 ID 元素放入其自己的列中,您可能会发现在该列上使用 BINARY 归类会提高其性能。请注意,我尚未测试在混合列上尝试进行 BINARY 归类

以下是我如何设置存储过程和底层 table 以跟踪您的工作订单:

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":
    
*/
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;

请注意,"next available" 工单计数器曾经被给定 (1) 作为最大值 + 1,并且曾经 (2) 增加,直到它不违反 table 上的唯一键约束了。像这样,您有两种不同的可能性。