Jag vill minnas att man kan lägga in egna id:n med SQL: Har inte testat, men det skulle kunna fungera med: <b>>Det finns även en liten chans att 2 användare kör frågan samtidigt och får samma värde.</b> <b>>>Det finns även en liten chans att 2 användare kör frågan samtidigt och får samma värde. Kanske snurrat till det, men borde inte följande fungera: Vad jag känner till finns det ingen IsloationLevel som hindrar läsning av data som är läst i en annan transaktion... Serializable gör väl just det? Motsvarande blocking calls i annan concurrency? Detta är inget jag testat, men enligt http://msdn2.microsoft.com/en-us/library/system.data.isolationlevel.aspx tycker jag det verkar som att det bara förhindrar förändring av data, inte läsning som det gäller i detta fallet. Men jag kan ha fel här... Jag är inte alls säker, men låt säga att det inte är så, och antag att du har en bokningsapplikation. Antar att vi på något sätt missuppfattar varandra nu... Ok, hur löser vi detta: Nejdå, inte omöjligt, när användare 2 i ditt fall försöker commita sin transaktion kommer detta misslyckas eftersom en annan användare redan har uppdaterat den posten. Men användare 2 kommer inte upptäcka att platsen är bokad förän användare 1 har commitat sin transaktion (iofs beroende på IsolationLevel). Så båda användarna kommer uppdatera samma post, men enbart den första som commitar kommer lyckas. Stämmer ju visserligen att det kommer att smälla, och på så sätt ge en indikation. Men det hade väl varit betydligt snyggare att inte kolla om platsen är ledig förrän man vet att resultatet är och kommer vara sant genom transaktionen. Om användare 2 kör "x = SELECT MAX(ID)" mellen det att användare 1 kör "x = SELECT MAX(ID)" och "UPDATE Tabell SET ID = x+1 WHERE ID = -1" får du dublett. Även om risken är väldigt liten... Hmm, ja om man får ut MAX(...) för tabellens tidigare utseende så. När jag testade med en select * samtidigt, så blev hela tabellen låst under tiden. Tanken var att MAX(...) ska tvingas vänta i och med att det har skett en update. Nå ... det blev lite mer advanced än vad jag räknat med... men det gör inget. Vet inte riktigt om jag förstår vad du menar, men för att göra en "egen räknare" skapar du en kolumn av typen heltal. För att exemplen ska fungera rakt av måste det finnas minst en post med "startvärdet".Startvärde på en räknare i access .
Jag använder en räknare i access för att stega upp postnummer i min tabell. Nu vill jag inte börja mitt postnummer på 1 utan på tex 500. Går det? Sv: Startvärde på en räknare i access .
INSERT INTO tabell (ID) VALUES (499);
Nästa värde borde då bli 500.
Rent generellt borde du inte använda räknar-fält till något som visas för användaren då du kan komma i situationer där du skulle vilja ända id eller liknande. Dessutom blir det dumt om du ska utbyta data mellan olika databaser t.ex. Mitt förslag är därför att du genererar rätt löpnummer i programmet istället och låter räknar-fältet vara dolt om du behöver något.
/JohanSv:Startvärde på en räknare i access .
I ett enda sql insert körning läsa in största nummer i en kolumn och sedan öka med 1.
Kan köra en fråga först som jag sedan ökar med 1 och sedan kör min insert. Sv: Startvärde på en räknare i access .
INSERT INTO tabell (MinRäknare) VALUES ((SELECT MAX(MinRäknare)+1 FROM tabell))
Eller kanske såhär:
INSERT INTO tabell SELECT MAX(tabell.MinRäknare)+1 AS MinRäknare FROM tabell
Men det fungerar inte om tabellen är tom. Det finns även en liten chans att 2 användare kör frågan samtidigt och får samma värde.
EDIT: första exemplet fungerar inte...
/JohanSv:Startvärde på en räknare i access .
...men inte om de kör det i en transaktion...Sv: Startvärde på en räknare i access .
>...men inte om de kör det i en transaktion...</b>
På vilket sätt skulle en transaktion förhindra det? Det snarare ökar risken för att det ska inträffa eftersom det troligtvis tar längre tid innan användare 2 ser posten som användare 1 sparat.
För att förhindra det får man ange att man inte vill ha dubletter i kolumnen, då uppstår ett fel vid dessa tillfällen istället.
/JohanSv:Startvärde på en räknare i access .
1. Användare 1 ansluter, transaktionen startar.
2. Användare 1 tar fram värdet.
3. Användare 1 ökar värdet med 1 och lägger in det i tabellen.
4. Användare 1 avslutar transaktionen.
Oavsett när användare 2 ansluter så får han inte tillgång till något värde förrän efter steg 4?Sv: Startvärde på en räknare i access .
Säg att användare 1 tar fram högsta värdet, säg 523, i punkt 2 ovan. Innan användare 1 hinner till punkt 3 och lägger till 524 i tabellen kommer användare 2 till punkt 2 och läser ut samma högsta värde (523) i tabellen (eftersom användare 1 inte har hunnit commita sin transaktion ännu). Dessa båda användare kommer nu tro att 524 är nästa lediga tal och vi får en dublett.
/JohanSv:Startvärde på en räknare i access .
Sv: Startvärde på en räknare i access .
/JohanSv:Startvärde på en räknare i access .
"Om plats X inte är bokad, ge användaren ett val att boka den eller inte."
Det skulle i så fall vara omöjligt att implementera?Sv: Startvärde på en räknare i access .
Nej, varför skulle det vara omöjligt att implementera? Det är 2 helt skilda problem. Klistrar in ett kodexempel som visar det aktuella problemet (ger dubletter). Tyvärr verkar inte Access stödja Serializable, men vad jag kan läsa mig till spelar det ingen roll (eftersom problemet är att användare 2 hinner läsa ifrån tabellen innan användare 1 har lagt till posten). Aktuellt problem måste lösas genom att användare 2 inte får läsa från tabellen innan användare 1 har lagt till sin post.
(allt som slutar på 1 representerar användare 1 och 2 representerar användare 2)
OleDbConnection con1 = new OleDbConnection(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\db.mdb;Persist Security Info=False");
OleDbConnection con2 = new OleDbConnection(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\db.mdb;Persist Security Info=False");
con1.Open();
con2.Open();
OleDbTransaction trans1 = con1.BeginTransaction(IsolationLevel.ReadCommitted);
OleDbCommand cmdSelect1 = new OleDbCommand("SELECT MAX(Falt1) FROM tabell", con1, trans1);
int Max1 = Convert.ToInt32(cmdSelect1.ExecuteScalar());
OleDbTransaction trans2 = con2.BeginTransaction(IsolationLevel.ReadCommitted);
OleDbCommand cmdSelect2 = new OleDbCommand("SELECT MAX(Falt1) FROM tabell", con2, trans2);
int Max2 = Convert.ToInt32(cmdSelect2.ExecuteScalar());
OleDbCommand cmdInsert1 = new OleDbCommand("INSERT INTO tabell (Falt1) VALUES (@varde)", con1, trans1);
cmdInsert1.Parameters.Add(new OleDbParameter("@varde", Max1+1));
cmdInsert1.ExecuteNonQuery();
OleDbCommand cmdInsert2 = new OleDbCommand("INSERT INTO tabell (Falt1) VALUES (@varde)", con2, trans2);
cmdInsert2.Parameters.Add(new OleDbParameter("@varde", Max2+1));
cmdInsert2.ExecuteNonQuery();
trans1.Commit();
con1.Close();
trans2.Commit();
con2.Close();
Genom att genomföra detta i samma anrop till databasen (som mitt tidigare exempel) minskar risken att detta inträffar, men det är ändå teoretiskt möjligt...
/Johan
Sv:Startvärde på en räknare i access .
1. Vi har ett antal platser för bokning, 1-N. Varje plats har ett fält "bokad".
2. Vi vill nu har en applikation som först tar in ett platsnummer i, kollar om plats i är bokad. "SELECT Booked FROM Bookings WHERE Position = i".
3. Om den redan är bokad: "Message('Seat already booked')".
4. Om den inte är bokad, boka platsen: "UPDATE Bookings SET Booked = 1 WHERE Position = i".
Om en annan användare kommer in någonstans mellan 2 och 4 så går det snett. Användare 2 får inte kunna läsa tabellen tills användare 1 är klar, annars blir det fel. Om det är som du säger är detta alltså omöjligt att lösa?Sv: Startvärde på en räknare i access .
/JohanSv:Startvärde på en räknare i access .
Serializable betyder ju just "möjlighet att göra i serie". Det var alldeles för länge sen jag läste om detaljer rörande transaktioner. Några småtester visar att det du säger stämmer.
Men då skulle ju en lösning som inte är fullt så snygg i SQL-mening, men snyggare i "programmerarmening" kanske kunna vara att börja med en förändring istället:
INSERT INTO Tabell (ID, ...) VALUES (-1, ...)
x = SELECT MAX(ID)
UPDATE Tabell SET ID = x+1 WHERE ID = -1
Då får man väl ändå inga krockar?Sv: Startvärde på en räknare i access .
Samma sak om man gör så som jag först föreslog:
INSERT INTO tabell SELECT MAX(tabell.MinRäknare)+1 AS MinRäknare FROM tabell
Inget som borde inträffa i verkligheten, men det finns en teoretisk risk.
/JohanSv:Startvärde på en räknare i access .
Vad jag är ute efter är ju egentligen bara någon form av operation som låser en hel tabell för alla former av operationer i andra transaktioner tills transaktionen stängs. Det var ett tag sen jag kollade på dylika detaljer, men det måste väl gå på något sätt.
Men hur som helst, problemet med dubbla IDn löses ju på precis lika fult som dubbla bokningar. Det blir ett stenhårt fel i båda fallen. Jag har svårt att acceptera att detta inte funkar med standard sql... =)Sv: Startvärde på en räknare i access .
Hur lägger ni upp en räknare? Sv:Startvärde på en räknare i access .
/Johan