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

时间:2016-10-02 16:06:06

标签: sql-server performance stored-procedures query-performance

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

我的存储过程有效,但我需要您的帮助才能优化存储过程。关于评论的任何问题。

2 个答案:

答案 0 :(得分:1)

根据您的代码,有许多观察结果可以改变以优化并保证您的结果。

我不知道你的表结构,但似乎你使用自然键作为你的ID。

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

SELECT @aux = id_workorder FROM idaux;

  • 没有描述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;

enter image description here

注意,&#34;下一个可用&#34;工作单计数器一旦给定(1),最大+ 1和一次(2)增加,直到它不再违反表上的唯一键约束。像这样,你有两种不同的可能性。