Trouvez la dernière valeur dans une séquence « enroulée sur » une procédure stockée?
Question
Supposons que j'eu un ensemble d'identifiants alpha-caractères d'une longueur fixe, par exemple toujours cinq lettres, et ils sont attribués de telle sorte qu'ils sont toujours incrémentant (GGggZ -> GGGHA, etc.). Maintenant, si je reçois à ZZZZZ, puisque la longueur est fixée, je dois « rouler » à AAAAA. Je pourrais avoir un bloc contigu de ZZZAA par AAAam. Je veux écrire un sproc qui me donnera l'identifiant « suivant », dans ce cas AAAAN.
Si je n'ai pas eu ce problème « rouler sur », bien sûr, je voudrais juste ORDER BY et de saisir le DESC meilleur résultat. Mais je suis un peu une perte maintenant - et il ne permet pas à tout ce que SQL n'est pas ma langue la plus forte
.Si I Vous Je peux me déplacer à mon C # code d'appel, mais un sproc serait un meilleur ajustement.
ETA: Je voudrais éviter de modifier le schéma (nouvelle colonne ou nouvelle table); Je préférerais simplement être en mesure de « le comprendre ». Je pourrais même préférer le faire la force brute (par exemple commencer à la valeur la plus basse et l'incrément jusqu'à ce que je trouve un « trou »), même si cela pourrait coûter cher. Si vous avez une réponse qui ne modifie pas le schéma, ce serait une meilleure solution pour mes besoins.
La solution 6
Je pense que la solution la moins d'impact pour mes besoins est d'ajouter une colonne d'identité. La seule chose que je peux vous garantir que la commande sera telle que les entrées qui devraient « d'abord » seront ajoutés d'abord - je ne serai jamais un avec identifiant ajouter BBBB, puis revenir en arrière et ajouter BBBA plus tard. Si je n'avais pas cette contrainte, de toute évidence cela ne fonctionnerait pas, mais tel qu'il est, je peux commander par la colonne d'identité et d'obtenir le genre que je veux.
Je vais continuer à penser aux autres suggestions - peut-être si elles « clic » dans ma tête, ils ressemblent à une meilleure option
.Autres conseils
Code est ici que je pense que vous donnera votre valeur suivante. J'ai créé 3 fonctions. Le tableau est juste ma simulation de la table.colonne avec vos alpha ids (je MyTable.AlphaID). Je suppose que c'est que vous avez laissé entendre et il y a un bloc contigu de chaînes alphabétiques majuscules de cinq caractères (AlphaID):
IF OBJECT_ID('dbo.MyTable','U') IS NOT NULL
DROP TABLE dbo.MyTable
GO
CREATE TABLE dbo.MyTable (AlphaID char(5) PRIMARY KEY)
GO
-- Play with different population scenarios for testing
INSERT dbo.MyTable VALUES ('ZZZZY')
INSERT dbo.MyTable VALUES ('ZZZZZ')
INSERT dbo.MyTable VALUES ('AAAAA')
INSERT dbo.MyTable VALUES ('AAAAB')
GO
IF OBJECT_ID('dbo.ConvertAlphaIDToInt','FN') IS NOT NULL
DROP FUNCTION dbo.ConvertAlphaIDToInt
GO
CREATE FUNCTION dbo.ConvertAlphaIDToInt (@AlphaID char(5))
RETURNS int
AS
BEGIN
RETURN 1+ ASCII(SUBSTRING(@AlphaID,5,1))-65
+ ((ASCII(SUBSTRING(@AlphaID,4,1))-65) * 26)
+ ((ASCII(SUBSTRING(@AlphaID,3,1))-65) * POWER(26,2))
+ ((ASCII(SUBSTRING(@AlphaID,2,1))-65) * POWER(26,3))
+ ((ASCII(SUBSTRING(@AlphaID,1,1))-65) * POWER(26,4))
END
GO
IF OBJECT_ID('dbo.ConvertIntToAlphaID','FN') IS NOT NULL
DROP FUNCTION dbo.ConvertIntToAlphaID
GO
CREATE FUNCTION dbo.ConvertIntToAlphaID (@ID int)
RETURNS char(5)
AS
BEGIN
RETURN CHAR((@ID-1) / POWER(26,4) + 65)
+ CHAR ((@ID-1) % POWER(26,4) / POWER(26,3) + 65)
+ CHAR ((@ID-1) % POWER(26,3) / POWER(26,2) + 65)
+ CHAR ((@ID-1) % POWER(26,2) / 26 + 65)
+ CHAR ((@ID-1) % 26 + 65)
END
GO
IF OBJECT_ID('dbo.GetNextAlphaID','FN') IS NOT NULL
DROP FUNCTION dbo.GetNextAlphaID
GO
CREATE FUNCTION dbo.GetNextAlphaID ()
RETURNS char(5)
AS
BEGIN
DECLARE @MaxID char(5), @ReturnVal char(5)
SELECT @MaxID = MAX(AlphaID) FROM dbo.MyTable
IF @MaxID < 'ZZZZZ'
RETURN dbo.ConvertIntToAlphaID(dbo.ConvertAlphaIDToInt(@MaxID)+1)
IF @MaxID IS NULL
RETURN 'AAAAA'
SELECT @MaxID = MAX(AlphaID)
FROM dbo.MyTable
WHERE AlphaID < dbo.ConvertIntToAlphaID((SELECT COUNT(*) FROM dbo.MyTable))
IF @MaxID IS NULL
RETURN 'AAAAA'
RETURN dbo.ConvertIntToAlphaID(dbo.ConvertAlphaIDToInt(@MaxID)+1)
END
GO
SELECT * FROM dbo.MyTable ORDER BY dbo.ConvertAlphaIDToInt(AlphaID)
GO
SELECT dbo.GetNextAlphaID () AS 'NextAlphaID'
Par ailleurs, si vous ne voulez pas assumer contiguïté, vous pouvez faire comme vous le suggérez et (s'il y a une rangée de « ZZZZZ ») utiliser le premier écart dans la séquence. Remplacer la dernière fonction avec ceci:
IF OBJECT_ID('dbo.GetNextAlphaID_2','FN') IS NOT NULL
DROP FUNCTION dbo.GetNextAlphaID_2
GO
CREATE FUNCTION dbo.GetNextAlphaID_2 ()
RETURNS char(5)
AS
BEGIN
DECLARE @MaxID char(5), @ReturnVal char(5)
SELECT @MaxID = MAX(AlphaID) FROM dbo.MyTable
IF @MaxID < 'ZZZZZ'
RETURN dbo.ConvertIntToAlphaID(dbo.ConvertAlphaIDToInt(@MaxID)+1)
IF @MaxID IS NULL
RETURN 'AAAAA'
SELECT TOP 1 @MaxID=M1.AlphaID
FROM dbo.Mytable M1
WHERE NOT EXISTS (SELECT 1 FROM dbo.MyTable M2
WHERE AlphaID = dbo.ConvertIntToAlphaID(dbo.ConvertAlphaIDToInt(M1.AlphaID) + 1 )
)
ORDER BY M1.AlphaID
IF @MaxID IS NULL
RETURN 'AAAAA'
RETURN dbo.ConvertIntToAlphaID(dbo.ConvertAlphaIDToInt(@MaxID)+1)
END
GO
Il faudrait stocker le dernier identifiant attribué dans la séquence.
Par exemple, le stocker dans une autre table qui a une colonne et une ligne.
CREATE TABLE CurrentMaxId (
Id CHAR(6) NOT NULL
);
INSERT INTO CurrentMaxId (Id) VALUES ('AAAAAA');
Chaque fois que vous allouez un nouvel identifiant, vous auriez récupérer la valeur dans ce tableau minuscule, incrémenter et stocker cette valeur dans votre table principale, ainsi que la mise à jour la valeur CurrentMaxId
.
Les réserves d'usage appliquent à l'égard de la concurrence, de table de verrouillage, etc.
Je pense que je l'aurais essayé de stocker la séquence comme un entier, puis le traduire en chaîne. Ou stocker autre colonne de nombre entier qui est incrémenté en parallèle en même temps que la valeur alpha. De toute façon, vous pouvez trier la colonne entière.
Un problème ici est que vous ne pouvez pas vraiment dire à partir des données où la « dernière » entrée est moins qu'il n'y ait plus de détails sur la façon dont les anciennes entrées sont supprimées.
Si je comprends bien, vous êtes enroulant autour à la fin de la séquence, ce qui signifie que vous devez être certains de vos supprimez les anciennes données pour faire de la place. Toutefois, si les données ne sont pas supprimées de manière parfaitement uniforme, vous vous retrouverez avec des fragments, comme ci-dessous:
ABCD HIJKL NOPQRS WXYZ
Vous remarquerez qu'il n'y a pas évidente valeur suivante ... D pourrait être la dernière valeur créée, mais il pourrait aussi être L ou S.
Au mieux, vous pouvez chercher le premier ou le dernier élément manquant (utiliser une procédure stockée pour effectuer un x + 1 chèque comme vous le feriez pour trouver un élément manquant dans une séquence entière), mais il ne va pas fournir tout dommage spécial résultat pour listes enroulés sur.
Depuis que je ne me sens pas comme écrire du code pour incrémenter des lettres, je crée une table de toutes pièces d'identité valides (AAAAAA par ZZZZZZ) avec un nombre entier de 1 à X pour ces ID. Ensuite, vous pouvez utiliser les éléments suivants:
SELECT @max_id = MAX(id) FROM Possible_Silly_IDs
SELECT
COALESCE(MAX(PSI2.silly_id), 'AAAAAA')
FROM
My_Table T1
INNER JOIN Possible_Silly_IDs PSI1 ON
PSI1.silly_id = T1.silly_id
INNER JOIN Possible_Silly_IDs PSI2 ON
PSI2.id = CASE WHEN PSI1.id = @max_id THEN 1 ELSE PSI1.id + 1 END
LEFT OUTER JOIN My_Table T2 ON
T2.silly_id = PSI2.silly_id
WHERE
T2.silly_id IS NULL
Le COALESCE est là au cas où la table est vide. Pour être vraiment robuste, vous devez calculer le « aaaaaa » (SELECT @min_silly_id = id = silly_id OU 1) dans le cas où vos changements d'algorithme « de numérotation ».
Si vous voulez vraiment faire les choses, vous voulez refaire la conception de base de données comme cela a été suggéré.
Pour revenir la prochaine ID
pour un ID
donné (avec retournement), on utilise:
SELECT COALESCE
(
(
SELECT TOP 1 id
FROM mytable
WHERE id > @id
ORDER BY
id
),
(
SELECT TOP 1 id
FROM mytable
ORDER BY
id
)
) AS nextid
Cette requête recherche pour le ID
à côté de la donnée. S'il n'y a pas ID
, elle retourne la première ID
.
Voici les résultats:
WITH mytable AS
(
SELECT 'AAA' AS id
UNION ALL
SELECT 'BBB' AS id
UNION ALL
SELECT 'CCC' AS id
UNION ALL
SELECT 'DDD' AS id
UNION ALL
SELECT 'EEE' AS id
)
SELECT mo.id,
COALESCE
(
(
SELECT TOP 1 id
FROM mytable mi
WHERE mi.id > mo.id
ORDER BY
id
),
(
SELECT TOP 1 id
FROM mytable mi
ORDER BY
id
)
) AS nextid
FROM mytable mo
id nextid
----- ------
AAA BBB
BBB CCC
CCC DDD
DDD EEE
EEE AAA
, i. e. il retourne BBB
pour AAA
, CCC
pour BBB
, etc., et, enfin, pour AAA
EEE
qui est le dernier dans le tableau.