Clé primaire personnalisée sur SQL Server 2008 R2

J'ai plusieurs jours à essayer de résoudre ce problème, mais mon manque de connaissance m'arrête, je ne sais pas si c'est possible ce que j'essaye d'accomplir.

J'ai besoin d'une table comme celle-ci:

Le premier champ doit être un ID de key primaire personnalisé (incrémenté automatiquement): AAAAMMJJ-99

Où YYYMMDD est le jour actuel et "99" est un countur qui devrait être incrémenté automatiquement de 01 à 99 dans chaque nouvelle ligne ajoutée et doit être automatiquement redémarré à 01 le jour suivant.

Le deuxième champ est un champ de text NVARCHAR (40) standard appelé: Nom

Par exemple, j'ajoute trois lignes, en introduisant simplement le "Nom" de la personne, l'identifiant est automatiquement ajouté:

ID Name --------------------------- 20160629-01 John 20160629-02 Katie 20160629-03 Mark 

Puis, le lendemain, j'ajoute deux nouvelles lignes:

  ID Name ------------------------- 20160630-01 Bob 20160630-02 Dave 

Les deux derniers numbers doivent être redémarrés, après le jour change.

Et, de quoi s'agit-il? Réponse: Besoin du client.

S'il est possible de le faire dans une procédure stockée, cela fonctionnera pour moi aussi.

Merci d'avance!!

C'est assez facile à réaliser, mais un peu compliqué à faire, c'est sûr avec plusieurs clients.

Ce dont vous avez besoin est une nouvelle table (par exemple IndexHelper ) qui stocke les parties de l'index comme il se doit en utilisant deux colonnes: Une a la date actuelle correctement formatée comme vous le souhaitez dans votre index et l'autre est l'index actuel entier. Exemple:

 DateSsortingng CurrentIndex ------------------------------- 20160629 13 

Vous avez maintenant besoin d'un code qui vous aide à get la valeur d'index suivante de manière atomique, c'est-à-dire d'une manière qui fonctionne également lorsque plusieurs clients essayent d'insert en même time sans avoir le même index plus d'une fois.

T-SQL vient à la rescousse avec sa clause UPDATE ... OUTPUT , qui vous permet de mettre à jour une table, en produisant en même time les nouvelles valeurs comme une opération atomique, qui ne peut pas être interrompue.

Dans votre cas, cette déclaration pourrait ressembler à ceci:

 DECLARE @curDay NVARCHAR(10) DELCARE @curIndex INT DECLARE @tempTable TABLE (theDay NVARCHAR(10), theIndex INT) UPDATE IndexHelper SET CurrentIndex = CurrentIndex + 1 OUTPUT INSERTED.DateSsortingng, INSERTED.CurrentIndex INTO @temptable WHERE CurrentDate = <code that converts CURRENT_TIMESTAMP into the ssortingng format you want> SELECT @curDay = theDay, @curIndex = theIndex FROM @tempTable 

Malheureusement, vous devez utiliser la table temporaire, comme l'exige la clause OUTPUT .

Cela incrémente CurrentIndex champ CurrentIndex dans IndexHelper pour la date actuelle . Vous pouvez combiner les deux en une valeur comme celle-ci:

 DECLARE @newIndexValue NVARCHAR(15) SET @newIndexValue = @curDay + '-' + RIGHT('00' + CONVERT(NVARCHAR, @curIndex), 2) 

Maintenant, la question est: Comment gérez-vous l'exigence «revenir à 01 pour le lendemain»? Aussi simple: Ajoutez des inputs dans IndexHelper 2 jours à l'avance avec la date et l'index 0 correspondants. Vous pouvez le faire en toute security chaque fois que votre code est appelé si vous vérifiez qu'une input d'un jour est réellement manquante. Donc pour aujourd'hui votre table pourrait ressembler à ceci:

 DateSsortingng CurrentIndex ------------------------------- 20160629 13 20160630 0 20160701 0 

Le premier appel demain ressemblerait à ceci:

 DateSsortingng CurrentIndex ------------------------------- 20160629 13 20160630 1 20160701 0 20160702 0 

Emballez ceci dans une procédure stockée qui fait tout le process INSERT dans votre table d'origine, ce que vous obtenez est:

  1. Ajoutez les inputs manquantes pour les deux prochains jours à la table IndexHelper .
  2. Obtenez l'ID suivant atomiquement comme décrit ci-dessus
  3. Combiner la string de date et l'ID de la command UPDATE en une seule string
  4. Utilisez ceci dans la command INSERT pour vos données réelles

Cela entraîne la procédure stockée suivante que vous pouvez utiliser pour insert vos données:

 -- This is our "work date" DECLARE @now DATETIME = CURRENT_DATETIME -- These are the date ssortingngs that we need DECLARE @today NVARCHAR(10) = CONVERT(NVARCHAR, @now, 112) DECLARE @tomorrow NVARCHAR(10) = CONVERT(NVARCHAR, DATEADD(dd, 1, @now), 112) DECLARE @datomorrow NVARCHAR(10) = CONVERT(NVARCHAR, DATEADD(dd, 2, @now), 112) -- We will need these later DECLARE @curDay NVARCHAR(10) DELCARE @curIndex INT DECLARE @tempTable TABLE (theDay NVARCHAR(10), theIndex INT) DECLARE @newIndexValue NVARCHAR(15) -- Add ensortinges for next two days into table -- NOTE: THIS IS NOT ATOMIC! SUPPOSED YOU HAVE A PK ON DATESTRING, THIS -- MAY EVEN FAIL! THAT'S WHY IS USE BEGIN TRY BEGIN TRY IF NOT EXISTS (SELECT 1 FROM IndexHelper WHERE DateSsortingng = @tomorrow) INSERT INTO IndexHelper (@tomorrow, 0) END TRY BEGIN CATCH PRINT 'hmpf' END CATCH BEGIN TRY IF NOT EXISTS (SELECT 1 FROM IndexHelper WHERE DateSsortingng = @datomorrow) INSERT INTO IndexHelper (@datomorrow, 0) END TRY BEGIN CATCH PRINT 'hmpf again' END CATCH -- Now perform the atomic update UPDATE IndexHelper SET CurrentIndex = CurrentIndex + 1 OUTPUT INSERTED.DateSsortingng, INSERTED.CurrentIndex INTO @temptable WHERE CurrentDate = @today -- Get the values after the update SELECT @curDay = theDay, @curIndex = theIndex FROM @tempTable -- Combine these into the new index value SET @newIndexValue = @curDay + '-' + RIGHT('00' + CONVERT(NVARCHAR, @curIndex), 2) -- PERFORM THE INSERT HERE!! ... 

Une façon d'get un incrément automatique personnalisé consiste à utiliser le triggersur INSTEAD OF dans SQL Server. https://msdn.microsoft.com/en-IN/library/ms189799.aspx

J'ai testé cela en utilisant le code ci-dessous. Cela pourrait être utile.

Il est écrit avec l'hypothèse que 99 loggings maximum seront insérés dans un jour donné. Vous devrez le modifier pour gérer plus de 99 loggings.

 CREATE TABLE dbo.CustomerTb( ID VARCHAR(50), Name VARCHAR(50) ) GO CREATE TRIGGER dbo.InsertCustomerTrigger ON dbo.CustomerTb INSTEAD OF INSERT AS BEGIN DECLARE @MaxID SMALLINT=0; SELECT @MaxID=ISNULL(MAX(RIGHT(ID,2)),0) FROM dbo.CustomerTb WHERE LEFT(ID,8)=FORMAT(GETDATE(),'yyyyMMdd'); INSERT INTO dbo.CustomerTb( ID, Name ) SELECT FORMAT(GETDATE(),'yyyyMMdd')+'-'+RIGHT('00'+CONVERT(VARCHAR(5),ROW_NUMBER() OVER(ORDER BY Name)+@MaxID),2), Name FROM inserted; END GO 

TEST CAS 1

 INSERT INTO dbo.CustomerTb(NAME) VALUES('A'),('B'); SELECT * FROM dbo.CustomerTb; 

TEST CAS 2

 INSERT INTO dbo.CustomerTb(NAME) VALUES('P'),('Q'); SELECT * FROM dbo.CustomerTb;