Undvik nummerkonflikter med Microsoft SQL-sekvenser
Obs:Jag kommer att presentera om detta ämne i gruppen Access med SQL Server online. Vänligen gå med mig den 13 september kl. 18.30 CST, gå med i gruppen för att få ett e-postmeddelande med alla mötesdetaljer, det är gratis!
- Behöver du garantera att ett nummer i ett fält bara kommer att användas en gång och aldrig dupliceras av en annan användare?
- Har du haft en situation där du behövt mer än ett autonummer i en tabell?
- Har du någonsin behövt en nedre och en övre gräns för sekventiella nummer, och du kunde inte gå längre än den?
- Har du ibland en lista med nummer som du vill återvinna efter att du kommit förbi det sista?
I SQL Server finns det en funktion som kan hantera detta ganska enkelt, och den kallas en sekvens. Den är tillgänglig från och med SQL Server 2012.
Precis som ett autonumrering kan det säkerställa att ett unikt nummer kommer att delas ut varje gång, såvida det inte återvinns.
Nyligen blev jag ombedd att implementera en sekvens för en klient, där flera användare kommer att skapa nya poster och måste "hämta" nästa nummer i en specifik sekvens. Vi kunde inte använda ett autonummer eftersom kunden var begränsad till ett visst intervall, för att inte överskrida en övre tröskel. När siffrorna var slut, skulle ledningen fylla på sekvensen på nytt.
Varför det inte fungerar att använda en Access-tabell
Innan de uppgraderar till SQL Server, skulle användare dela en tabell som skulle hålla koll på vad som är nästa nummer att använda, problemet med detta tillvägagångssätt är att det inte är idiotsäkert, två användare kan begära samma nummer vid exakt samma tidpunkt, bryter mot affärsregeln.
Skapa och använda en SQL Server-sekvens
Innan du kan använda en sekvens måste den skapas med följande syntax i SQL Server, du behöver bara göra detta en gång:
CREATE SEQUENCE dbo.seqPolicyNumber AS int MIN 50005000 MAX 50005999;
Använd följande sats för att hämta nästa sekvensnummer:
VÄLJ NÄSTA VÄRDE FÖR dbo.seqPolicyNumber som NextValue
Dina användare kommer att behöva uppdateringsbehörigheter för att använda sekvensen, men de bör inte kunna ändra sekvensens intervall. Uppdateringsbehörigheter kan ges med hjälp av denna syntax:
BEHANDLA UPPDATERING PÅ dbo.seqPolicyNumber TILL [MyDatabaseUserOrRole];
För att få nästa värde för en sekvens från ett Microsoft Access VBA-program kan du använda följande sats för att läsa in nästa värde i en ADODB-postuppsättning.
strSQL ="VÄLJ NÄSTA VÄRDE FÖR dbo .seqPolicyNumber as NextValue"
OpenMyRecordset rs, strSQL
NextValue =rs("NextValue")
Det är så vi vanligtvis öppnar en ADODB-postuppsättning i vårt företag. För mer information om hur du kan använda OpenMyRecordset kan du klicka på en annan artikel i vår blogg:
Enkla ADODB-postuppsättningar och kommandon i Access
Det fina med syntaxen för att få nästa sekvensnummer är att den är väldigt enkel att använda i T-SQL. Du ersätter bara NÄSTA VÄRDE FÖR
INSERT dbo.Orders (OrderID, Name, Quty)
VALUES (NEXT VALUE FOR dbo.OrderNumberSequence, 'Tire', 2);
Mer flexibilitet än Autonumber
En sekvens kan erbjuda mer flexibilitet än en autonumrering i Access eller ett IDENTITY-fält i SQL Server. För det första kan du bara ha ett autonummer- eller identitetsfält i en tabell. Även om du kan se om ett IDENTITY-fält, kan du inte återvinna värden. IDENTITY-fält är fortfarande användbara för primärnycklar, när vi vill ha något godtyckligt nummer för att identifiera posten, och det har ingen betydelse. Sekvensintervall kan dock ha inbäddad betydelse.
Du är inte heller begränsad till att använda heltal som en IDENTITET, utan sekvensnummer kan också vara decimala eller numeriska. Du kan också öka nedåt i din sekvens istället för bara uppåt.
En sekvens är inte heller knuten till någon specifik tabell och kan användas över tabeller eftersom nya sekvensnummer behövs för en viss tabell.
Sätt på sekvensen
När du vill ändra intervallet för en sekvens, som när du behöver ett nytt intervall av policynummer, bör det göras med en lagrad procedur. Följande är en lagrad procedur som kan göra detta.
STÄLL IN ANSI_NULLS PÅ
GO
SÄTTA QUOTED_IDENTIFIER PÅ
GO
SKAPA PROCEDUR [dbo].[usp_AlterPolicySequence] (
@SeqName AS sysname,
@InpMin AS int,
@InpMax AS int
) MED UTFÖR SOM ÄGARE SOM
BÖRJA
STÄLL IN NOCOUNT PÅ;
DECLARE @sql nvarchar(MAX),
@err nvarchar(MAX);
OM INTE FINNS (
VÄLJ NULL
FRÅN sys.sequences AS s
WHERE s.name =@SeqName
OCH s.schema_id =SCHEMA_ID('dbo')
)
THROW 50000, 'Sekvensnamnet är inte giltigt.', 1;
OM @InpMin ÄR NULL ELLER @InpMax ÄR NULL
THROW 50000, ‘Värdena kan inte vara null.’, 1;
SET @sql =CONCAT(N'ALTER SEQUENCE [dbo].', QUOTENAME(@SeqName), N' STARTA OM MED ', @InpMin, N' ÖKA MED 1′, N' MINVALUE ', @InpMin, N' MAXVALUE ' , @InpMax, N' NO CYCLE NO CACHE;');
EXEC sys.sp_executesql @sql;
;
SLUT
Det finns några saker som är värda att notera i denna lagrade procedur. Först kör vi det
MED UTFÖR SOM ÄGARE SOM.
Vi vill inte att den dagliga användaren ska kunna ändra en sekvens. Men vi vill ge dem begränsad förmåga att ändra det endast genom en lagrad procedur. (Användare behöver bara rättigheter till den lagrade proceduren.)
GIVNA UTFÖR PÅ dbo.usp_AlterPolicySequence TILL [MyDatabaseUserOrRole];
Denna lagrade procedur kan köras från ett Access-gränssnitt närhelst ett nytt intervall i sekvensen behöver installeras, och det skulle normalt vara av en administratörsanvändare, som kan ha fler SQL Server-privilegier än en normal användare.
Men denna lagrade procedur kan också köras när ett nytt nummer med nummer väntar på att laddas in i sekvensen, direkt efter att den aktuella sekvensen har använts. I detta fall kan den lagrade proceduren anropas av vilken användare som helst som behöver det första policynumret för det nya området. Så vi använder WITH EXECUTE AS OWNER AS för att ge dem fler rättigheter bara för denna begränsade användning.
En annan sak att lägga märke till är att det är nödvändigt att konstruera en SQL-sträng och sedan använda
EXEC sys.sp_executesql
på den strängen, om vi använder parametrar.
Följande sats fungerar om den skrivs in i ett SSMS-frågefönster eller används i en lagrad procedur.
ALTER SEQUENCE dbo.seqPolicyNumber
STARTA OM MED 50005000
ÖKA MED 1
MINVÄRDE 50005000
MAXVÄRDE 50005999
INGEN CYKEL INGEN CACHE
Följande kommer dock inte att fungera med parametrar i en lagrad procedur.
ALTER SEQUENCE dbo.seqPolicyNumber
STARTA OM MED @InpMin
ÖKA MED 1
MINVÄRDE @ InpMin
MAXVALUE @InpMax
INGEN CYKEL
INGEN CACHE
Så du måste konstruera strängsatsen med parametervärdena inklistrade.
SET @sql =CONCAT(N'ALTER SEQUENCE [dbo].', QUOTENAME(@SeqName), N' STARTA OM MED ', @InpMin, N' ÖKA MED 1', N' MINVALUE ', @InpMin, N' MAXVALUE ', @InpMax, N' NO CYCLE NO Cache;');
EXEC sys.sp_executesql @sql;
Denna sträng @sql är konstruerad med funktionerna CONCAT och QUOTENAME. Det kommer också att fungera om du använde plustecken för att göra din sista sträng, men det är bättre att göra det som exemplet som är nollsäkert.
Denna lagrade procedur kommer att producera (kasta) ett fel om du anger saknade eller dåliga värden, och du kommer inte att tillåtas fortsätta. Det kommer automatiskt att generera ett fel om alla sekvensnummer är förbrukade.
Din gränssnittsåtkomstprocedur bör kontrollera att ett fel inte har inträffat, vilket bara bör inträffa om sekvensen tar slut på siffror, om du tillhandahåller korrekta parameterinmatningar. Om ett fel upptäcks måste gränssnittet avbryta sin operation på något sätt.
Det finns några andra funktioner du kan ställa in med argument. CYCLE kommer att tillåta sekvensen att cykla igen efter att den når slutet och sedan gå till MINVÄRDE. Du kan till och med uttryckligen starta om den mitt i en sekvens genom att ge den ett RESTART-värde.
Du kan också ge den en CACHE, till exempel kan du be om 50 sekvensnummer åt gången, och den uppdaterar systemsekvenstabellerna en gång var 50:e nummer, vilket kan vara snabbare, men det ger också en risk om det blir strömavbrott , eftersom dessa nummer inte kan återanvändas
Det sista som är värt att notera i denna lagrade procedur är att du kan hämta information (metadata) om dina sekvenser från en systemvy som kallas sys.sequences. Den innehåller följande information.
Några användbara kolumner som du kanske skulle vilja läsa och förmedla till en användare är minimum_value, maximum_value och aktuellt_värde.
Om du är intresserad har följande sidor på MSDN mycket användbar information om sekvenser.
Sekvensnummer
Beskriver sekvenser och har mycket bra exempel för vanlig användning
SKAPA SEKVENS (Transact-SQL)
ALTER SEQUENCE (Transact-SQL)
NÄSTA VÄRDE FÖR (Transact-SQL)
sys.sequences (Transact-SQL)
Beskriver metadata som du kan fråga efter på dina sekvenser