webForumDet fria alternativet

Förhindra konflikt om två inserts vill använda samma id?

Databaser & SQL

5 svar · 294 visningar · startad av CokeLight

Medlem sedan juni 2000504 inlägg
Frågan#1

Hejsan :)

(funderar hur jag ska förklara.. :) Säg att jag har en tabell, t1, som fungerar som en slags pool med en massa UniqueID som annan en tabell, t2, kan använda. När man gör en insert i t2 ska då automatiskt ett UniqueID från t1 hämtas, stoppas in i en kolumn i t2, och sen ska detta UniqueID raderas från t1 för att man inte ska använda det igen. Hur gör man detta?

Vilket UniqueID man använder spelar inte så stor roll, bara det är unikt:
alt 1: Hämta alltid det första ID't som finns i t1 (ASC)
alt 2: Hämta alltid det sista ID't som finns i t1 (ORDER BY UniqueID DESC)
alt 3: Hämta random ID från t1 (detta minskar risken avsevärt att konflikt skulle inträffa men den finns fortfarande : )

alt 1 implementation:

CREATE PROCEDURE [B]dbo.coke_GetTopId[/B]
	[B]@UniqueID[/B] char (9) OUTPUT
AS
SET NOCOUNT ON
SET [B]@UniqueID[/B] = (SELECT TOP 1 [B]UniqueID[/B]FROM t1)
RETURN
GO

Själva applikationen anropar denna SP när man klickar på en knapp som tillåter användaren att göra en förhandsgranskning innan de beslutar sig för att skapa posten. När man klickar på en "Godkänn" knapp så utförs själva INSERT'en

Problemet:
Men båda alternativen är ju behäftade med samma konfliktpotential såsom jag ser det; flera användare kan vid ungefär samma tid granska olika poster - varje gång de granskar en post så anropas dbo.coke_GetTopId som då hämtar det första tillgängliga ID't från t1.

När den första användaren beslutar sig för att Godkänna förhandsgranskningen så kommer det UniqueID som dbo.coke_GetTopId hämtade att raderas (mha av en trigger eller nåt) men problemet är att de andra användarna som vid ungefär samma tid granskade andra poster också kommer att utnyttja samma UniqueID som ju nu har blivit raderat från t1 ! Så, det är detta som är mitt problem; hur ska jag undvika detta?

Så, hur gör man då...? Är det sånt här ROLLBACK TRANSACTION är till för, eller finns nåt annat, t.ex. att man har en IF sats, typ:

IF ID EXISTS
-- USE IT
ELSE
-- USE THE NEXT AVAILABLE

(alltså, jag kan inte syntaxen men nåt sånt...? :)

Vet inte om det är relevant men koden som gör INSERT'en som jag nämnde ovan ser ut ungefär så här (utan ROLLBACK etc).

CREATE procedure dbo.coke_InsertConfirmedRequest
(
	@col1 xxx,
	@col2 xxx,
	..
)
AS

INSERT INTO [B]sub_tabell_till_t2[/B]
(
	col1,
	col2,
	..
)
VALUES
(
	@col1,
	@col2,
	..
)

DECLARE [B]@LastId[/B] nvarchar(9)
SET @LastId = Scope_Identity()

INSERT INTO [B]t2[/B]
(
	[B]LastId[/B],
	[B]UniqueID[/B],
	..
)
VALUES
(
	[B]@LastId[/B], -- [I]Detta förväntas vara det ID som gavs från sub_tabell_till_t2 (har inget att göra med t1 tabellen).[/I]
	[B]@UniqueID[/B], -- [I]Det här ID't skickas med från applikationen men kom ursprungligen från [B]dbo.coke_GetTopId[/B][/I]
	...
)
GO

(Hoppas att jag inte skrev för mycket så att det blev rörigt istället för tydligare.. säg till i så fall, för jag skulle gärna ta emot lite hjälp.. :)

Medlem sedan juni 2000504 inlägg
#2

Ska prova lägga in ROLLBACK etc så gott det går (plus trigger som ska utföra DELETE på det ID som användes vid INSERT'en efter att INSERT har utförts) och sen skriva vilka eventuella fel jag fick... kanske lättare besvara en sån fråga... inser att mitt första inlägg var lite rörigt... återkommer senare :)

Medlem sedan dec. 200012 464 inlägg
#3

Varför detta krångliga upplägg? Kan du inte använda en räknare eller GUID?

begin transaction
  select @g = UniqueID from t2 holdlock
   delete from t2 where UniqueID = @g
commit transaction
Medlem sedan juni 2000504 inlägg
#4

tackar :) har inte hunnit prova än... hmm.. du menar upplägget med att använda trigger? tja, såsom du skrev ser jag inte heller varför man måste ha trigger.. fick bara för mig att det var det som var bra vid just det här tillfället och reflekterade inte så mycket över det.. hmm.. så när är det motiverat med att använda trigger då...?

(Eller om du menade upplägget med att ha en tabell med UniqueID så är det för att det är någon annan som skickar dessa UniqueID när de som finns tar slut, så detta är bortom min kontroll... det är nån special algoritm iaf...)

Sen tänkte jag passa på att fråga.. såg att du använde holdlock och läste lite om det, verkar ju vara smidigt... :) dock undrar jag vad som händer med den användare som då inte fick det ID som han/hon begärde...? Gör man raise error eller if error > 0 (eller nåt sånt) och gör om allt själv utan att användaren märker, eller ska man skicka ett meddelande till användaren att nåt blev fel o be de trycka på "Godkänn" igen...? (har inte min dator just nu men ska testa så snart som möjligt o kan ju prova själv o se vad som händer : )

tack igen, uppskattar hjälpen! :)

Medlem sedan dec. 200012 464 inlägg
#5

Krångligt avsåg i första hand upplägget med två tabeller.

Det fel som kan uppstå när du använder min kod är att det tar slut på unika värden vilket du måste ha någon felhantering för.

Det är också bra att lägga till en begränsning med hjälp av top i min kod.

begin transaction
  set @g = (
  select top 1 UniqueID 
   from t2 holdlock 
  order by UniqueID)
   delete from t2 where UniqueID = @g
commit transaction
Medlem sedan juni 2000504 inlägg
#6

jo, precis.. måste fixa felhantering... tänkte att det skulle gå ut ett mail automatiskt vid en viss gräns men det måste ju finnas i vilket fall som helst.. :)

.. och TOP 1 gör att man är säker på alltid få första.. låter bra.. hmm.. ska prova o sen skriva hur det blev.. tack :)

270 ms totalt · 4 externa anrop · v20260731065814-full.a51de22e
134 ms — deklarationer (db)
0 ms — hämta statistik (cache)
132 ms — hämta tråd, inlägg och bilagor (db)
135 ms — ändringar (db)