1

我正在開發一個管理車輛工單的系統。工單的編號組成如下:OT-001-16重置SQL Server中存儲過程的ID計數器

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,並將工作訂單插入表workorder

我的存儲過程有效,但我需要您的幫助來優化存儲過程。任何關於評論的問題。

+0

我可以詢問儲存該ID的表格設置嗎?我明白,你想顯示完整的ID爲'OT-001-18'。然而,這樣存儲在桌子上並不方便。我寧願把一年中的一列保存爲一個'int',而在另一列中將今年的計數器保存爲一個'int',然後(可能)創建一個計算列(甚至可能是'persistent'),它會自動創建滿ID。當然,還應該添加對兩列的唯一約束。基於數據庫設置,程序可以更好地優化。 – Ralph

回答

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

請注意,「下一個可用的」工單順序一旦被賦予(1)爲最大值+1並且一次(2)增加,直到它不再違反表格上的唯一鍵約束。像這樣,你有兩種不同的可能性去實現它。

+0

做得很好,看起來很乾淨,堅持的柱子很不錯。不必要,但對應用程序/報告有用。 –

+1

謝謝。上述解決方案中唯一的*髒*部分是處理大於1.000的「工單計數器」。它會工作,但沒有正確顯示在「工單ID」中。但是,這是OP的調查。基本上,我提供了一個工作解決方案,而您正在分析OP的代碼當前狀態。我猜** **方法/解決方案將被證明是有幫助的。 – Ralph

+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列有在列中間的ID。 這會產生災難性的結果,因爲ID的任何比較都會發生在列的中間。這意味着最好的情況是你可能會逃脫PATINDEX,但仍然在桌上進行表掃描。沒有索引(除了FULLTEXT)將被利用得更少。

我應該加上,如果你把ID元素放到它自己的列中,你可能會發現在列上使用BINARY排序會改善它的性能。注意我沒有測試過試圖在混合列