10. november 2006 - 19:49Der er
10 kommentarer og 2 løsninger
Database håndtering
Jeg bruger Delphi 7 og MySQL. Jeg har en ide om hvordan det skal laves og har så lavet det sådan men det kunne være rart at høre andres meninger.
Bruger A henter en post og begynder at rette i den, bruger B henter nu samme post og begynder også at rette i denne. Bruger B gemmer posten uden problemer, nu vil bruger A så gemme og jeg har så lavet det sådan at bruger A får besked på at en anden bruger har rettet i posten så derfor kan bruger A´s rettelser ikke gemmes og sådan er det bare, synd for bruger A. Er det måden at gøre det på eller findes der bedre metoder, det er jo ikke så sandsynligt at 2 eller flere brugere vil rettet i samme post på samme tid. Jeg syntes ikke om metoden med at låse en post da en post så kan risikere at være låst i lang tid hvis brugeren beslutter sig for at hente en kop kaffe eller lign.
Jeg bruger altid denne metode og har brugt det både på pc'ere og på de store mainframes i mange år: På hvert post er der en versionsnummer. (f.eks. kun 1. karakter lang) Når posten læses gemmes dette versionsnummer i hukommelsen. Når der så opdateres, skal den "gamle" post have dette nummer. Hvis ikke, er recorden blevet ændret i mellemtiden. Alt dette kan laves i den sql-streng som bruges til at opdatere databasen med.
Jeg har lavet det sådan at inden ændringerne gemmes henter jeg posten igen for at checke at versionsnummeret er det samme som i den post jeg skal til at gemme. Hvordan gør du det i en sql-streng?
update tabel MinTabel set navn=:navn ... , version = version + 1 where id = :id
En anden løsning er at logge ændringerne i en anden tabel og lade opdateringen tjekke den derfra (tjekke at datoen for sidste opdatering af pågældende record ikke er nyere end datoen for den som brugeren har liggende). Problemet er bare med den slags, at log-filer har det med at blive ret store. Man kan risikere at dræbe ydelsen.
Fordelen er, at man vil kunne se 1-n brugere der har opdateret recorden siden brugeren hentede den:
select l.bruger_id, b.navn, l.dato from log l join by bruger b on (l.bruger_id = b.id) where l.tabel_id >= :tabel_id and l.dato >= :dato order by l.dato
hrc har lige glemt det vigtigste i sql'en til update, nemlig at teste på versionsnummeret: update tabel MinTabel set navn=:navn ... , version = version + 1 where id = :id and version = gammelversion
Jeg bruger en try execcute(sqlstreng), hvor jeg tester på returkoder fra Paradox. Jeg har ud over dette selvfølgelig check på om det er 1. gang recorden skrives. Hvis dette er tilfældet, skal der jo opdateres med en "insert". Selve opdateringen har jeg liggende i en function, som jeg bruger ved alle mine opdateringer i hele programmet. Her logger jeg også fejl, som er alvorlige. Logningen foregår i en almindelig txt fil. Jeg kører forøvrigt med start, commit og bruger rollback i tilfælde af at der kommer en alvorlig fejl under opdateringen. Det er effektivt og har endnu aldrig svigtet på Paradox. Jeg har meget store kørsler, hvor jeg kører opkrævninger ud. Når jeg starter på en kunde, startes en transaction. Når kunden er korrekt afsluttet kører jeg en commit. Herefter tages så fat på næste kunde. Sker der fejl under opdateringerne - og dem er der rigtig mange af- laver jeg en rollback og afslutter programmet. Jeg har derfor altid en database som er ok.
Transaktionsstyring er aldrig at fornægte. For det første så er operationerne atomiske, enten det ene eller det andet. Ydermere får man meget bedre ydelse da databaserne ellers laver skjulte begin/commit for hver record der skrives. Det er der tilsyneladende mange der overser. Jeg bruger følgende skelet (i pseudokode):
procedure DoSQL(aQuery : TQuery); var LocalTransaction : boolean; begin LocalTransaction := not aQuery.Database.InTransaction; // Improviserer her... if LocalTransaction then aQuery.Database.BeginTrans; try aQuery.SQL.Text := '<script>'; aQuery.ParamByName('<paramnavn>).AsInteger := xxx aQuery.ParamByName('<paramnavn>).AsString := yyy
aQuery.ExexSql;
if LocalTransaction then aQuery.Database.CommitTrans; except on e: exception do begin if LocalTransaction then aQuery.Database.RollbackTrans; raise; end; end; end;
... men nu under MS-SQL er jeg gået over til StoredProcs.
Transaktionsstyring lyder jo meget smart og jeg har da også tidligere læst om det og tænkt på at benytte det men indtil nu har jeg aldrig oplevet fejl med MySQL så måske er mine systemer ikke så store og med så mange samtidige brugere at det er nødvendig. Men nu understøtter MySQL jo også StoredProcs så det er måske en bedre løning. Men tak for indput, jeg kan forstå på det at den metode jeg benytter nu ikke er helt hen i vejert. Smider i nogle svar.
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.