Avatar billede skindbeni Nybegynder
12. august 2009 - 22:03 Der er 7 kommentarer og
1 løsning

SQL sætning i IBSQL komponent

Hej.

Så har jeg igen brug for at læne mig op af nogle, der er langt dygtigere end mig selv.

Baggrundshistorien er, at når jeg åbner en form, vil jeg indsætte to værdier i en eksisterende post. Dels brugernavnet på vedkommende, der åbner formen og dels en statisk tekst.

Dertil har jeg en IBSQL komponent (valget faldt på den, da det er ren SQL og ikke til brug af noget visuelt, hvorfor jeg ikke tog en IBQuery. Ret mig gerne, hvis jeg tager fejl i korrekt brug der.)

Brugernavnet har jeg fra, da login prompten ved mit programs opstart. I eksemplet har jeg lavet en variabel til det.

I formens onCreate event har jeg nedenstående:

var
  Brugernavn: string;
  KladdeID: integer;
Begin
  IBSQL.SQL.Clear
  IBSQL.SQL.Add('Insert into tabelnavn (Adgang, Bruger) VALUES (''Spærret'', brugernavn) WHERE ID = KladdeID');
  IBSQL.ExecQuery;
end;

Min SQL sætning er gået helt fløjten. JEg kan simpelthen ikke overskue den mere og brugen af 'er (') samt SQL, tekst og variabler m.v. ligger pt. i en rodebutik i min SQL sætning.

Dernæst kunne det nemt tænkes, at min løsning er forkert i forhold til, hvad en ekspert vil gøre eller med fordel kan trimmes / ændres.

Derfor håber jeg på lærerigt input til emnet.

Jeg kører i øvrigt: Delphi 2007 og Firebird 2,1
Avatar billede arne_v Ekspert
13. august 2009 - 01:39 #1
Jeg har ikke mulighed for at teste, men skulle jeg gætte:

IBSQL.SQL.Clear;
IBSQL.SQL.Add('Insert into tabelnavn (Adgang, Bruger) VALUES (''Spærret'', :Brugernavn) WHERE ID = :KladdeID');
IBSQL.Params.ParamByName('Brugernavn').AsString := Brugernavn;
IBSQL.Params.ParamByName('KladdeID').AsInteger := KladdeID;
IBSQL.ExecQuery;
Avatar billede hrc Mester
13. august 2009 - 08:10 #2
Jeg ville ikke bruge et tekstfelt til at gemme en status, eksempelvis "Spærret", ej heller ville jeg gemme brugeren med andet end hans primærnøgle (ID'et) - at det betyder at man aldrig derefter kan slette denne, men må markere brugeren som inaktiv eller slettet, er en anden sag. Jeg vil tage arnes eksempel et lille skridt videre og flyttes status ud som parameter:

IBSQL.SQL.Clear;
IBSQL.SQL.Add('Insert into tabelnavn');
IBSQL.SQL.Add('(Adgang, Bruger)');
IBSQL.SQL.Add('VALUES (:adgang, :bruger)');
IBSQL.SQL.Add('WHERE (ID = :kladdeID)');
IBSQL.Params.ParamByName('adgang').AsString := 'Spærret';
IBSQL.Params.ParamByName('bruger').AsString := Brugernavn;
IBSQL.Params.ParamByName('kladdeID').AsInteger := KladdeID;
IBSQL.ExecQuery;

Hvis jeg skulle prøve at lave det lidt .. anderledes - der er fordele og ulemper ved løsningerne (din er simplest, min er fleksibel). Nedenfor antages at tabellens adgang og bruger bliver til heltals-felter og at bruger omdøbes til brugerID.

IBSQL.SQL.Clear;
type
  TAdgangsTyper = (atyUnknown, atyAaben, atySpaerret);

...

procedure TForm1.GemStatus(const aAdgangsType: TAdgangsTyper; const aBrugerID, KladdeID: integer);
begin
  IBSQL.SQL.Add('Insert into tabelnavn');
  IBSQL.SQL.Add('(adgang, brugerID)');
  IBSQL.SQL.Add('VALUES (:adgang, :brugerID)');
  IBSQL.SQL.Add('WHERE (ID = :kladdeID)');
  IBSQL.Params.ParamByName('adgang').AsInteger := integer(aAdgangsType); // type caster
  IBSQL.Params.ParamByName('brugerID').AsInteger := aBrugerID;
  IBSQL.Params.ParamByName('kladdeID').AsInteger := aKladdeID;
  IBSQL.ExecQuery;
end;


Fordelene er, at hver record fylder meget mindre og at søgninger (queries) bliver meget nemme at lave og endelig bliver de hurtigere da de ikke skal sammenligne strenge.

Endelig ville jeg tage mig tid til at lave constraints og definere relationer mellem fremmednøglerne og tabellerne (brugerID og kladdeID) - på den måde sikre at du ikke kan indsætte et brugerID eller kladdeID der ikke findes, eller at du ikke kan slette et brugerID fordi ovenstående tabel refererer til den. Den slags logik er guld værd og IB kan klare det hele for dig - endelig er der muligheden for at lave cascade constraints eksempelvis på sletning sådan, at hvis du sletter brugeren, så slettes alle records i andre tabeller, der refererer til denne.
Avatar billede skindbeni Nybegynder
13. august 2009 - 23:14 #3
Hej.

Lærerige svar.

Jeg tror, at jeg har forstået 80 af det... Men det har intet med Jer at gøre, tværtimod, men med mig selv :o) Jeg skyder 200 points oveni for videregående hjælp

En uddybning til min baggrundshistorie. Scenariet skulle til sidst gerne være således, at når en bruger åbner en form, så vil hans brugernavn stå på en given post i feltet "bruger" og den statiske tekst "spærret" skal stå i feltet "adgang" således andre kan se, at formen er bruges af "BRUGERNAVNET" og er "SPÆRRET".

I formens onClose event er det så meningen, at jeg trækker det tilbage igen, således feltet "bruger" bliver NULL og værdien i feltet "adgang" bliver "Åben".

(Faktisk tilsvarende Microsoft's C5 under kassekladder)

Tilbage til koderiet.

Jeg forstod ideen med parametrene og jeg fandt ud af, hvordan mine ''er skulle skrives og hvordan det kan gøres overskueligt med og uden parametre.

Det med Type... Hvor skal Type-delen indsættes. Og vil det egentlig hjælpe mig i mit tilfælde, hvor jeg gerne vil have ordet "spærret" eller "åben" til at figuere på min form.

Så er der det med brugere i Firebird. Er der overhovedet noget, der hedder UserID og som er inikt? Jeg havde den tro at det var Brugernavnet, der var unikt. JEg har ikke kunnet finde noget gennem google, hvor man kunne udtrække userID. Men lige netop den del omkring brugere i Firebird synes jeg er mangelfuldt dokumenteret.

Så har jeg lige læst om Prepare og UnPrepare. Er et noget, jeg bør bruge?

Flg. sætning forstod jeg desværre ikke: "...at det betyder at man aldrig derefter kan slette denne, men må markere brugeren som inaktiv eller slettet, er en anden sag..."
Avatar billede hrc Mester
14. august 2009 - 09:20 #4
Hvis du laver en tabel der hedder brugere og der en primærnøgle der hedder BrugerID integer - eller som du lægger op til BrugerNavn varchar(30), så skal databasen nok sørge for at det forbliver unikt. Primærnøgler er vejen frem!

Hvis du refererer til en bruger-record fra en anden tabel, så skal denne relation sættes op som fremmednøgle. Fremmednøgler og også vejen frem! Derved kontrollerer databasen at brugeren findes når du refererer til den i en anden tabel. Den sørger også for du ikke kan slette brugeren (i brugertabellen) hvis der refereres til den fra andre tabeller. Dette kaldes dataintegritet. Derfor bliver du nødt til at have et flag der markerer den som (pseudo)slettet, så den ikke vises i oversigter eller kan bruges.

Til din låsningsteknik kan jeg foreslå du lavede en ny record hver gang (ID, oprettet, formID, brugerID, status) for så kan du se hvor længe brugerene har blokeret formene. Den select hvormed du henter den bliver lidt mere avanceret. Noget i denne retning til at finde sidste status på brugerens adgang til en form (sub-selects, mener ikke IB har "select top 1" ligesom MSSQL):


select id, oprettet, formID, brugerID, status
from adgang
where (ref in (select max(ref)
              from adgang
              where (brugerID = :brugerID)
                and (formID = :formID)))


Prepare og unprepare er mærkelige størrelser. Har læst hjælpen gentagne gange og er stadig ikke sikker på det overhovedet er relevant
Avatar billede hrc Mester
14. august 2009 - 09:33 #5
Grunden til jeg gerne vil erstatte brugernavn og adgangsstatus med tal-værdier er for at undgå redundans. Det er ikke noget man gør i relationelle databaser.
Nu vil du nulstille feltet hver gang og så kan det faktisk accepteres, men hvis du køber ideen med at bruge tabellen som en log, så vil 1000 forekomster af et navn 20 karakterer fylde mere end 20kb. Med talværdier der refererede til en bruger, ville det være 4 bytes i stedet (dvs. 4kb).

En anden ulempe er at du med brugernavnet kan opleve at "Hans Müller" skrives "Hans Møller" eller "Hans Moller". Med en brugertabel vil det kunne styres meget bedre.

P.S. Havde ikke læst hjælpen om Prepare og UnPrepare for IB, for den forstod jeg. Hvis det er en query der ikke skal returnere data (insert, update, delete), og som skal kaldes mange gange, så kan det være en idé at sende den til serveren så der ikke køres så meget frem og tilbage over netværket. Det er i alt fald ikke noget du skal bekymre dig om her.
Avatar billede arne_v Ekspert
16. august 2009 - 17:18 #6
>Dette kaldes dataintegritet.

Dette kaldes referentiel integritet og er en del af data integritet.
Avatar billede arne_v Ekspert
16. august 2009 - 17:31 #7
Prepare er et generelt begreb i SQL, hvor en SQL sætning compiles til en mere effektiv binær form gemt på server, som så kan executes flere gange. Kort sagt i.s.f. at sende en lang streng med SQL så sender man bare et id på den SQL man vil have udført (og de parametre som skal med). For nogle SQL sætninger kan det betyde en pæn performance forbedring.

Med IBSQL er det vist ret ligegyldigt at kalde Prepare. Et hurtigt kig i source code viser:

procedure TIBSQL.ExecQuery;
var
  fetch_res: ISC_STATUS;
begin
  CheckClosed;
  if not Prepared then
    Prepare;
  CheckValidStatement;
  ...
Avatar billede arne_v Ekspert
16. august 2009 - 17:33 #8
Med hensyn til låsning af data mens de editeres, så er der to kendte teknikker kaldet pessimistic og optimistic locking.

Se http://www.eksperten.dk/guide/996 under "long time locking" for mere info.
Avatar billede Ny bruger Nybegynder

Din løsning...

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.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester