1

Je développe un système qui gère les ordres de travail pour les véhicules. L'ID des ordres de travail est composé comme suit: OT-001-16.Réinitialiser le compteur d'ID sur une procédure stockée dans SQL Server

OT- est une chaîne, 001 est le compteur, suivi par - caractère et enfin le nombre 16 est l'année en cours.

Exemple:

Si l'année en cours est 2018, l'ID devrait être OT-001-18.

Le problème est lorsque l'année change, le compteur doit redémarrer de 001. J'ai une procédure stockée pour le faire, mais je pense que je fais beaucoup plus de travail.

Ceci est mon code de procédure stockée:

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 

Fondamentalement, je crée une table Auxiliar pour enregistrer le dernier ordre de travail ID, puis de ce tableau appelé idaux i prendre l'ID et i par rapport à la nouvelle ID possible par une manipulation de chaîne. Ensuite, si l'année du dernier ID enregistré est égale à l'année en cours, le compteur augmente, sinon le compteur est remis à 001, le nouvel ID est mis à jour dans la table auxiliaire et le Work Order est inséré dans la table workorder.

Ma procédure stockée fonctionne, mais j'ai besoin de votre aide pour optimiser la procédure stockée. Toute question sur les commentaires.

+0

Puis-je me renseigner sur la configuration de la table où cet 'ID' est stocké? Je comprends que vous voulez montrer l'identification complète comme OT-001-18. Pourtant, ce ne serait pas pratique de stocker comme tel dans un tableau. Je préfère enregistrer dans une colonne l'année comme un «int» et dans une autre colonne le compteur pour cette année comme un «int» et ensuite (éventuellement) créer une colonne calculée (peut-être même «persistante») qui crée automatiquement le plein ID. Bien sûr, une contrainte unique sur les deux colonnes devrait également être ajoutée. En fonction de la configuration de la base de données, la procédure pourrait être mieux optimisée. – Ralph

Répondre

1

Voici comment je configurer la procédure stockée et la table sous-jacente de garder une trace de vos ordres de travail:

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

Notez que le compteur d'ordres de travail "suivant disponible" est donné une fois (1) au maximum + 1 et une fois (2) augmenté jusqu'à ce qu'il ne viole plus la contrainte de clé unique sur la table. Comme cela, vous avez deux possibilités différentes pour y arriver.

+0

Bien fait, semble propre et la colonne persistante est une belle touche. Pas nécessaire, mais utile pour les applications/rapports. –

+1

Merci. La seule partie * sale * dans la solution ci-dessus est la manipulation d'un «compteur de travail» supérieur à 1.000. Il fonctionnera mais ne s'affichera pas correctement dans le 'work order ID'. Mais c'est à l'OP d'examiner. Fondamentalement, je fournis une solution de travail alors que vous analysez l'OP son état actuel du code. Je suppose que ** les deux ** approches/solutions se révéleront utiles. – Ralph

+0

Je suppose que vous pourriez dire que nous avons associé la réponse. :) –

1

Il existe un certain nombre d'observations basées sur votre code que vous pouvez modifier pour optimiser et garantir vos résultats.

Je ne connais pas votre structure de table, mais il semble que vous utilisiez des clés naturelles pour vos ID.

  • Au lieu de cela, utiliser une clé de substitution, comme INT/BIGINT pour ajouter non seulement l'efficacité dans votre table des jointures (pas besoin de cordes), mais potentiellement ajouter une autre couche de sécurité dans votre conception actuelle.
  • Alternativement, normaliser la colonne dans les drapeaux qu'ils sont. Par exemple: OT-001-05 a trois éléments: OT est un type d'ordre de travail, 001 est l'ID, et 15 est l'année. Actuellement, OT détermine l'ID qui détermine l'année.

SELECT @aux = id_workorder FROM idaux;

  • Idaux n'a pas été décrit. Est-ce une valeur unique? S'il est tabulaire, garantissez le résultat ou il pourrait se casser dans le futur.
  • Même si vous ajoutez MAX(id_workorder), votre résultat ne fonctionnera pas comme vous le pensez. Comme il s'agit d'un VARCHAR, la plus grande valeur du caractère le plus à gauche non lié reviendra.

@aux, CHARINDEX('-', @aux) + 1, LEN(@aux) - CHARINDEX('-', @aux) - CHARINDEX('-', REVERSE(@aux)));

  • Cela est bien, mais vous pouvez globalement rendre le code plus clair et plus facile à déboguer en divisant les trois de ces éléments dans leur propre variable.Votre encore en utilisant votre méthode, mais un peu simplifié (personnellement, CHARINDEX peut être une douleur).

    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)

Mise à jour: Actuellement, votre colonne idaux a l'ID au milieu de votre colonne. Cela produira des résultats désastreux puisque toute comparaison d'ID se produira au milieu de la colonne. Cela signifie au mieux que vous pourriez vous en sortir avec PATINDEX mais que vous effectuez toujours une analyse de table sur la table. Aucun index (sauf pour FULLTEXT) ne sera utilisé beaucoup moins optimisé.

Je devrais ajouter, si vous mettez l'élément ID dans sa propre colonne, vous pourriez trouver que l'utilisation de BINARY collations sur la colonne permettra d'améliorer ses performances. Note que je ne l'ai pas testé d'essayer une collation BINARY sur une colonne mixte