Sign in Join
Wiki copytemporary

SqlDBHowto/nl: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## Introductie
22
33Omdat er heel veel vragen over SqlDb zijn, en zo weinig documentatie beschikbaar is, heb ik besloten toch het een en ander op te gaan schrijven. Ik doe het in het Nederlands, omdat dat me nu eenmaal veel beter afgaat. Later kunnen anderen het altijd nog naar het Engels vertalen.
44
55Ik heb gekozen voor een soort 'howto'-opzet. Ik wil één voor één vragen beantwoorden, uitleggen hoe je iets doet. Al die vragen achter elkaar moeten wel ook een verhaal vormen, die als een soort tutorial doorlopen kan worden.
66
77Ik zal proberen het zo te verwoorden dat het duidelijk is voor zowel Lazarus als Freepascal, maar de voorbeelden zijn voor Freepascal. (Daarmee bedoel ik dus dat het console-applicaties zijn.)
88
99## Hoe maak ik een verbinding met een database-server?
1010
1111SqlDB maakt niet direct verbinding met de database server, maar maakt gebruik van de client die bij de besbetreffende database-server hoort. SqlDB stuurt de commando's naar de client-library, die vervolgens de daadwerkelijke verbinding maakt. Dat betekent dus dat deze library altijd op de computer geinstalleerd moet zijn, wil je een verbinding kunnen maken. In Windows is dat meestal een dll, in Linux een .so en voor OS/X een .dylib-bestand.
1212
1313Als de client-library goed geinstalleerd is, kan je een verbinding maken met een database-server met een TSQLConnection-component. Er zijn verschillende TSQLConnection componenten voor de verschillende database-servers. Voorbeelden zijn TIBConnection om met een Firebird/Interbase client-library te verbinden. TPQConnection voor PostgreSQL en TMySQL40Connection, TMySQL41Connection en TMySQL50connection voor MySQL-clients met versienummer 4.0, 4.1 of 5.0. De verschillen tussen deze MySQL versies zijn zo groot, dat ze niet door elkaar te gebruiken zijn. Als de MySQL-client library versie 4.1 is geinstalleerd, moet een TMySQL41Connection gebruikt worden, onafhankelijk van de versie van de MySQL-server.
1414
1515Het kan per database verschillen, maar over het algemeen moeten er vier properties ingesteld worden om met een database-server te kunnen verbinden:
1616
1717- de server-naam of ip-adres
1818- de naam van de database
1919- een gebruikersnaam
2020- een wachtwoord.
2121
2222Als deze zijn ingesteld kan er een verbinding gemaakt worden met de 'open' methode. Als de verbinding mislukt, komt er een EDatabaseError. Met de property 'connected' kan gekeken worden of er al een verbinding met de database-server is. Met 'close' kan de verbinding weer afgesloten worden.
2323
2424``` pascal
2525 Program ConnectDB
2626
2727 var AConnection : TSQLConnection;
2828
2929 Procedure CreateConnection;
3030 begin
3131 AConnection := TIBConnection.Create(nil);
3232 AConnection.Hostname := 'localhost';
3333 AConnection.DatabaseName := '/opt/firebird/examples/employee.fdb';
3434 AConnection.UserName := 'sysdba';
3535 AConnection.Password := 'masterkey';
3636 end;
3737
3838 begin
3939 CreateConnection;
4040 AConnection.Open;
4141 if Aconnection.Connected then
4242 writeln('Succesful connect!')
4343 else
4444 writeln('This is not possible, because if the connection failed, an exception should be raised, so this code would not be executed');
4545 AConnection.Close;
4646 AConnection.Free;
4747 end.
4848```
4949
5050Als er een exceptie optreedt, lees de foutmelding dan zorgvuldig. Het kan zijn dat de databaseserver niet draait, dat de gebruikersnaam of wachtwoord niet goed zijn, of bijvoorbeeld het ip-adres. Als in de foutmelding staat dat de client-library niet gevonden kan worden, controleer dan of die client juist geinstalleerd is. Vaak staat in de foutmelding de letterlijke naam van het bestand waarnaar gezocht wordt.
5151
5252## Hoe voer ik direct queries uit/maak ik een tabel?
5353
5454SqlDB- de naam zegt het al, werkt alleen met database-servers die van SQL gebruik maken. SQL staat voor 'Structured Query Language' en is een taal speciaal ontwikkeld om met relationele databases te werken. Vrijwel iedere database-server heeft zijn eigen dialect, maar een aantal SQL-commando's zijn voor alle database types hetzelfde.
5555
5656FPC maakt een verschil tussen:
5757
5858- SQL-commando's die gegevens (een dataset) terug geven. Hierbij moet je altijd een TSQLQuery-component gebruiken (zie hier)
5959- commando's die geen gegevens terugsturen maar bijvoorbeeld gegevens bijwerken. Hiervoor mag je ook de 'ExecuteDirect' methode van een TSQLConnection gebruiken. (Je kan dit ook doen als je een dataset terugkrijgt maar niet geinteresseerd bent in de gegevens, bijv. bij het aanroepen van een selectable stored procedure).
6060
6161De meeste typen database-servers kunnen SQL-commando's uitvoeren binnen een transactie. Wil je dat de wijzigingen die je binnen een transactie gemaakt hebt ook beschikbaar komen in de andere transacties, of nog steeds beschikbaar zijn nadat je de transactie hebt afgesloten(!), dan moet de de transactie 'committen'.
6262
6363Om daar ondersteuning voor te bieden, heeft SqlDB het TSQLTransaction-component. Een SQL-commando dat door SqlDB wordt uitgevoerd, moet altijd binnen een transactie uitgevoerd worden, zelfs als de database-server geen transacties ondersteunt. Er zijn ook typen database-servers die wel transacties ondersteunen, maar waarvan de TSQLConnection (nog) geen transacties ondersteunt. Maar ook dan moet dus een TSQLTransaction-component gebruikt worden.
6464
6565Wil je dus met `TSQLConnection.ExecuteDirect` een SQL-commando kunnen uitvoeren, moet je voor de TSQLConnection eerst opgeven welke 'Transaction' hij moet gebruiken. Voor de TSQLTransaction op zijn beurt, moet je opgeven welke TSQLConnection component gebruikt moet worden.
6666
6767Het volgende voorbeeld maakt een tabel 'TBLNAMES' met de velden 'NAME' en 'ID' aan, en voegt twee records toe. Er worden twee SQL-commando's gebruikt die ik hier verder niet uitleg. Voor meer informatie over SQL-queries kan je de documentatie van de database-server raadplegen. De procedure 'CreateConnection' staat hierboven gedefinieerd.
6868
69-`program CreateTable;`\
69+``` pascal
70+program CreateTable;
7071
71-`var AConnection : TSQLConnection;`\
72-`    ATransaction : TSQLTransaction;`\
72+var AConnection : TSQLConnection;
73+ ATransaction : TSQLTransaction;
7374
74-`procedure CreateTransaction;`\
75-`begin`\
76-`  ATransaction := TSQLTransaction.Create;`\
77-`  ATransaction.Database := AConnection;`\
78-`end;`\
75+procedure CreateTransaction;
76+begin
77+ ATransaction := TSQLTransaction.Create;
78+ ATransaction.Database := AConnection;
79+end;
7980
80-`begin`\
81-`  CreateConnection;`\
82-`  CreateTransaction;`\
83-`  AConnection.Transaction := ATransaction;`\
84-`  AConnection.Open;`\
85-`  ATransaction.StartTransaction;`\
86-`  AConnection.ExecuteDirect('create table TBLNAMES (ID integer, NAME varchar(40));'); `\
87-`  `\
88-`  // Some database-server types need a commit before you can use a newly created table. (Firebird)`\
89-`  // With .Commit you also close the transaction`\
90-`  ATransaction.Commit; `\
81+begin
82+ CreateConnection;
83+ CreateTransaction;
84+ AConnection.Transaction := ATransaction;
85+ AConnection.Open;
86+ ATransaction.StartTransaction;
87+ AConnection.ExecuteDirect('create table TBLNAMES (ID integer, NAME varchar(40));');
88+
89+ // Some database-server types need a commit before you can use a newly created table. (Firebird)
90+ // With .Commit you also close the transaction
91+ ATransaction.Commit;
9192
92-`  ATransaction.StartTransaction;`\
93-`  AConnection.ExecuteDirect('insert into TBLNAMES (ID,NAME) values (1,'Name1');'); `\
94-`  AConnection.ExecuteDirect('insert into TBLNAMES (ID,NAME) values (2,'Name2');'); `\
95-`  ATransaction.Commit; `\
96-`  AConnection.Close;`\
97-`  AConnection.Free;`\
98-`  ATransaction.Free;`\
99-`end.`
93+ ATransaction.StartTransaction;
94+ AConnection.ExecuteDirect('insert into TBLNAMES (ID,NAME) values (1,'Name1');');
95+ AConnection.ExecuteDirect('insert into TBLNAMES (ID,NAME) values (2,'Name2');');
96+ ATransaction.Commit;
97+ AConnection.Close;
98+ AConnection.Free;
99+ ATransaction.Free;
100+end.
101+```
100102
101103## Hoe lees ik data in uit een tabel?
102104
103105Om data uit een tabel in te lezen maak je gebruik van een TSQLQuery component. Een TSQLQuery moet verbonden worden met een TSQLConnection en een TSQLTransaction om te kunnen werken. Het instellen van de TSQLConnection en TSQLTransaction worden hier en hier besproken.
104106
105107Als de TSQLConnection, TSQLQuery en TSQLTransaction met elkaar zijn verbonden, moet TSQLQuery nog worden ingesteld. TSQLQuery heeft een 'SQL'-property die een TStrings-object bevat met daarin een SQL-statement dat uitgevoerd moet worden. Als alle gegevens uit een tabel `tablename` ingelezen moeten worden, stel de query dan in op
106108
107109``` sql
108110'select * from tablename;'
109111```
110112
111113. Met 'open' worden de gegevens in de tabel uitgelezen van de server en in de TSQLQuery opgeslagen. De gegevens zijn nu via TSQLQuery te benaderen totdat de query met 'close' weer wordt afgesloten.
112114
113115TSQLQuery is een afgeleide van TDataset en een TDataset heeft een 'Fields' collectie, waarin alle kolommen van de tabel staan. En een TDataset houdt het huidige record bij. Met 'First', 'Next', 'Prior' en 'Last' kan het huidige record gewijzigd worden. 'Eof' en 'Bof' geven aan of het eerste of laatste record bereikt zijn. Om de waarde van een veld in het huidige record op te vragen, moet je eerst het juiste 'TField'-object zoeken en dan de 'AsString', 'AsInteger',... property uitlezen.
114116
115117Hieronder staat een voorbeeld dat van een tabel zoals die hierboven gemaakt is alle waarden weergeeft.
116118
117119``` pascal
118120 Program ShowData;
119121
120122 var AConnection : TSQLConnection;
121123 ATransaction : TSQLTransaction;
122124
123125 procedure GetQuery : TSQLQuery;
124126 var AQuery : TSQLQuery;
125127 begin
126128 AQuery := TSQLQuery.Create;
127129 AQuery.Database := FConnection;
128130 AQuery.Transaction := FTransaction;
129131 Result := AQuery;
130132 end;
131133
132134 var Query : TSQLQuery;
133135
134136 begin
135137 CreateConnection;
136138 CreateTransaction;
137139 Query := GetQuery;
138140 Query.SQL.Text := 'select * from tblNames';
139141 AConnection.Open;
140142 Query.Open;
141143 while not Query.Eof do
142144 begin
143145 Writeln('ID: ', Query.FieldByName('Name').AsInteger, 'Name: '+Query.FieldByName('Name').AsString);
144146 Query.Next;
145147 end;
146148 Query.Close;
147149 AConnection.Close;
148150 Query.Free;
149151 ATransaction.Free;
150152 AConnection.Free;
151153 end.
152154```
153155
154156(De code hierboven is natuurlijk niet helemaal netjes, zo missen er 'try..finally' blokken, maar daar gaat het hier niet om, dus heb ik ze weggelaten.) Als je goed oplet zie je dat ik hier geen gebruik maak van 'TSQLTransaction.StartTransaction'. Dat is ook niet nodig. Als een TSQLQuery uitgevoerd wordt terwijl er geen transactie gestart is, dan wordt er automatisch een transactie gestart, dat hoeft de programmeur niet expliciet te doen. Hetzelfde geldt trouwens voor de verbinding van een TSQLConnection. Die wordt ook automatisch geopend als dat nodig is. De regel 'AConnection.Open;' zoals die hierboven staat is dus niet nodig. Als een TSQLTransaction wordt vernietigd, wordt eerst automatisch een 'rollback' uitgevoerd. **Eventuele wijzigingen aan de data in die transactie worden dan dus teniet gedaan!**
155157
156158### Waarom geeft TSQLQuery.RecordCount altijd 10 terug?
157159
158160Als je wilt weten hoeveel records er in een dataset zijn, kan je .RecordCount gebruiken. Maar houd er rekening mee dat .RecordCount het aantal records laat zien dat al geladen is van de server. SqlDB leest bij het openen van een TSQLQuery standaard niet alle records in, maar alleen de eerste 10. Pas als er geprobeerd word om naar het 11e record te gaan worden de volgende 10 records geladen, enzovoorts. Als .Last wordt aangeroepen, worden alle records ingelezen.
159161
160162Wil je dus weten hoeveel records er echt op de server staan, kan je eerst .last aanroepen en daarna .recordcount uitlezen. Er is echter ook een andere mogelijkheid. Het aantal records dat per keer wordt opgehaald van de server is in te stellen met .Packetrecords (standaard dus 10). Als hier -1 wordt ingevuld wordt de dataset altijd in een keer ingelezen.
161163
162164Je kan natuurlijk ook middels een expliciete SQL query het aantal records opvragen van de database. Dat moet dan wel in dezelfde transactie gebruiken, want andere transacties kunnen inmiddels het aantal records beïnvloed hebben.
163165
164166### Lazarus
165167
166168Lazarus heeft verschillende componenten om gegevens uit een [[TDataSet]] op een formulier te tonen. In plaats van de while-loop en WriteLn zoals in het voorbeeld hierboven, kan je van deze componenten gebruik maken om de data in de tabel te laten zien. Plaats daarvoor de juiste TSQLConnection, TSQLTransaction en TSQLQuery op een formulier, stel ze goed in en verbind ze met elkaar zoals eerder besproken. Verder heb je een [[TDatasource]] nodig; geef als TDatasource.Dataset de TSQLQuery op. (Doe dit niet andersom, TSQLQuery heeft een 'Datasource' property, maar gebruik die alleen als je met master-detail tabellen werkt) Vervolgens kan je bijvoorbeeld een TDBGrid op het formulier zetten en de datasource daarvan instellen op de TDatasource die je net gemaakt hebt.
167169
168170Om te kijken of het werkt, kan je in de Lazarus-IDE de property 'Connected' van de TSQLConnection op 'True' zetten. Dan wordt er meteen geprobeerd om een verbinding met de database-server te maken. Als dat gelukt is kan je TSQLQuery.Active op 'True' zetten. Als alles goed is gedaan, zie je - in de IDE - meteen alle gegevens in de tabel op je scherm verschijnen.
169171
170172## Hoe wijzig ik gegevens in een tabel?
171173
172174Om de gegevens in een record te wijzigen, moet de TDataset (waarvan TSQLQuery een afgeleide is) in edit-modus gezet worden. Dat gebeurt met de 'Edit', 'Insert' of 'Append' methodes. Met 'Edit' is het huidige record te wijzigen, 'Insert' en 'Append' maken een nieuw record aan voor het huidige record of helemaal aan het eind van de dataset. Daarna zijn de gegevens via de 'Fields' property te wijzigen. Met 'Post' wordt er eerst gecontroleerd of de nieuwe gegevens wel geldig zijn en als dat zo is dan verlaat de dataset de edit-modus. Als je naar een ander record gaat (bijvoorbeeld met 'Next') en de dataset is in de edit-modus, dan wordt altijd eerst 'Post' aangeroepen. Met de methode 'Cancel' kan je de wijzigingen die je sinds de aanroep van 'Post' in de dataset hebt gemaakt weer ongedaan maken en wordt de edit-modus ook weer verlaten.
173175
174-`Query.Edit;`\
175-`Query.FieldByName('NAME').AsString := 'Edited name';`\
176-`Query.Post;`
176+``` pascal
177+Query.Edit;
178+Query.FieldByName('NAME').AsString := 'Edited name';
179+Query.Post;
180+```
177181
178182Dit is echter nog niet het hele verhaal. TSQLQuery is afgeleid van TBufDataset, dat gebruik maakt van gebufferde updates. Dat houdt in dat nadat 'Post' is aangeroepen, de wijzigingen in de dataset in het geheugen direct zichtbaar zijn, maar dat ze nog niet naar de database-server worden gestuurd. Wat er wel gebeurt is dat er een changelog wordt bijgehouden met alle wijzigingen. Als de methode 'ApplyUpdates' wordt aangeroepen, worden alle wijzigingen in het changelog één voor één naar de database gestuurd. Pas daarna weet de database-server van de wijzigingen. De wijzigingen worden naar de database verstuurd binnen de transactie van de TSQLConnection. Vergeet dus niet om die in te stellen voordat 'ApplyUpdates' aangeroepen wordt. Daarna moet er natuurlijk ook nog een commit uitgevoerd worden om de wijzigingen op te slaan.
179183
180184Hieronder een voorbeeld van het wijzigen van gegevens in de database, het versturen van de wijzigingen naar de server en het committen van de transactie.
181185
182186``` pascal
183187 Program EditData;
184188
185189 var AConnection : TSQLConnection;
186190 ATransaction : TSQLTransaction;
187191 Query : TSQLQuery;
188192
189193 begin
190194 CreateConnection;
191195 CreateTransaction;
192196 AConnection.Transaction := ATransaction;
193197 Query := GetQuery;
194198 Query.SQL.Text := 'select * from tblNames';
195199 Query.Open;
196200 Query.Edit;
197201 Query.FieldByName('NAME').AsString := 'Edited name';
198202 Query.Post;
199203 Query.UpdateMode := upWhereAll;
200204 Query.ApplyUpdates;
201205 AConnection.Transaction.Commit;
202206 Query.Free;
203207 ATransaction.Free;
204208 AConnection.Free;
205209 end.
206210```
207211
208212Voor een uitleg van UpdateMode, lees hieronder verder.
209213
210214## Hoe stuurt SqlDB de wijzigingen naar de Database-server?
211215
212216In het voorbeeld voor het wijzigen van gegevens in een tabel, staat de regel
213217
214-`  Query.UpdateMode := upWhereAll;`
218+``` pascal
219+ Query.UpdateMode := upWhereAll;
220+```
215221
216222waarvan niet uitgelegd is wat de regel doet. De beste manier om daar achter te komen is door de regel weg te laten. Als je dat doet en je hebt deze howto precies hebt gevolgd, dan krijg je de volgende foutmelding:
217223
218-`No update query specified and failed to generate one. (No fields for inclusion in where statement found)`
224+```text
225+No update query specified and failed to generate one. (No fields for inclusion in where statement found)
226+```
219227
220228Om te begrijpen wat er hier fout gaat is het nodig om precies te begrijpen hoe wijzigingen naar de database-server gestuurd worden. De enige manier om een gegevens op een SQL-server te wijzigen is door een SQL-query uit te voeren. Er zijn drie type queries voor de drie verschillende manieren waarop een record gewijzigd kan worden: Een record kan nieuw zijn, verwijderd of gewijzigd waarvoor respectievelijk een insert-, delete- of update-query voor uitgevoerd moet worden. Een update-query kan er als volg uitzien:
221229
222-`update TBLNAMES set NAME='Edited name' where ID=1;`
230+```text
231+update TBLNAMES set NAME='Edited name' where ID=1;
232+```
223233
224234Om een wijziging naar de database-server te sturen, moet SqlDB dus zo'n update-query samenstellen. Daarvoor zijn drie dingen nodig:
225235
226236De tabelnaam
227237Deze wordt achterhaald door de opgegeven select-query te parsen (dit lukt niet altijd.)
228238
229239Update- of insert-deel
230240Dit zijn de velden die gewijzigd worden.
231241
232242Where-deel
233243Dit zijn de velden die aangeven welk record er nou eigenlijk gewijzigd moet worden.
234244
235245Ieder veld (TField) heeft een property ProviderFlags. Alleen de velden met pfInUpdate in de ProviderFlags worden in het update- of insert-deel van de query opgenomen. Standaard hebben alle velden dat.
236246
237247Welke velden in het where-gedeelte worden opgenomen hangt af van de UpdateMode van de query en de ProviderFlags van de betreffende velden. De velden met pfInKey in hun ProviderFlags komen altijd in het where-gedeelte terecht. Een veld krijgt de pfInKey vlag automatisch als het veld deel uitmaakt van de primary key van de tabel en TSQLQuery.UsePrimaryKeyAsKey waar is.
238248
239249De standaard waarde voor de UpdateMode van de query is upWhereKeyOnly. In deze UpdateMode zijn de velden met pfInKey in de ProviderFlags de enige velden die gebruikt worden in het where-gedeelte. Als er geen enkel veld de pfInKey vlag heeft en de UpdateMode is upWhereKeyOnly dan zijn er geen velden beschikbaar zijn voor het where-deel van de query en treedt de foutmelding op van het begin van deze sectie. Dit kan dan opgelost worden door een primary key aan de tabel toe te voegen en TSQLQuery.UsePrimaryKeyAsKey op 'true' in te stellen of door handmatig de pfInKey vlag in te stellen op voor of meer van de velden van de tabel.
240250
241251Er zijn nog twee andere UpdateMode's. Met UpWhereAll worden naast de velden met pfInKey alle velden met de pfInWhere flag opgenomen in het where-deel van de query. Standaard heeft ieder veld deze vlag ingesteld staan. Als de UpdateMode UpWhereChanged is dan worden alleen die velden met pfInWhere opgenomen die in het huidige record gewijzigd zijn.
242252
243253## Hoe voer je een query uit met een TSQLQuery?
244254
245255Naast queries die een dataset teruggeven zijn er ook queries die dat niet doen. Bijvoorbeeld de 'update', 'insert' en 'delete' queries. Deze queries kunnen niet alleen uitegvoerd worden met TSQLConnection.ExecuteDirect maar ook door een TSQLQuery te gebruiken. Dit doe je door TSQLQuery.ExecSQL aan te roepen in plaats van TSQLQuery.Open (wat je gebruikt om een tabel die je met een query opvraagt te openen).
246256
247257De volgende procedure maakt een tabel en voegt twee records toe door gebruik te maken van TSQLQuery.
248258
249259``` pascal
250260 procedure CreateTable;
251261
252262 var Query : TSQLQuery;
253263
254264 begin
255265 Query := GetQuery;
256266 Query.SQL.Text := 'create table TBLNAMES (ID integer, NAME varchar(40));';
257267 Query.ExecSQL;
258268
259269 Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (1,'Name1');';
260270 Query.ExecSQL;
261271
262272 Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (2,'Name2');';
263273 Query.ExecSQL;
264274
265275 Query.Close;
266276 Query.Free;
267277 end;
268278```
269279
270280## Hoe maak je gebruik van parameters in een query?
271281
272282In dit voorbeeld wordt twee keer dezelfde query uitgevoerd met alleen verschillende parameters. Een betere manier om dit te doen is door een query met parameters te gebruiken.
273283
274284De syntax van parameters in queries is per database server verschillend maar dat wordt door TSQLQuery afgehandeld. Vervang de parameter in de query met een dubbele punt, gevolgd door de naam die je de parameter wilt geven. Bijvoorbeeld
275285
276286``` pascal
277287 Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (:ID,:NAME);';
278288```
279289
280290maakt twee parameters aan, 'ID' en 'NAME'. Om te bepalen wat de parameters zijn wordt de query geparsed als hij aan TSQLQuery.SQL wordt toegewezen of gewijzigd. Alle bestaande parameters worden dan verwijderd en de nieuwe worden toegevoegd aan de TSQLQuery.Params property. Om een waarde aan een parameter toe te wijzen lijkt op de manier waarop de waarde van een veld wordt gewijzigd:
281291
282292``` pascal
283293 Query.Params.ParamByName('Name').AsString := 'Name1';
284294```
285295
286296Het is aan de query zelf niet te zien wat voor soort data de parameter moet opslaan. Daarom wordt het datatype pas bepaald op het moment dat er voor het eerst een waarde aan de parameter wordt toegewezen. Door .AsString te gebruiken bijvoorbeeld, krijgt de parameter het datatype 'ftString'. Het datatype is ook direct in te stellen door de 'DataType' property te gebruiken. Als het verkeerde datatype aan een parameter toegewezen wordt, kan dat tot problemen leiden tijdens het openen of uitvoeren van de query.
287297
288298Het volgende voorbeeld maakt dezelfde tabel aan als in het vorige voorbeeld maar dan door gebruik te maken van parameters:
289299
290300``` pascal
291301 procedure CreateTableUsingParameters;
292302
293303 var Query : TSQLQuery;
294304
295305 begin
296306 Query := GetQuery;
297307 Query.SQL.Text := 'create table TBLNAMES (ID integer, NAME varchar(40));';
298308 Query.ExecSQL;
299309
300310 Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (:ID,:NAME);';
301311
302312 Query.Params.ParamByName('ID').AsInteger := 1;
303313 Query.Params.ParamByName('NAME').AsString := 'Name1';
304314 Query.ExecSQL;
305315
306316 Query.Params.ParamByName('ID').AsInteger := 2;
307317 Query.Params.ParamByName('NAME').AsString := 'Name2;
308318 Query.ExecSQL;
309319
310320 Query.Close;
311321 Query.Free;
312322 end;
313323```
314324
315325Wat meteen opvalt is dat dit voorbeeld meer code heeft dan het voorbeeld dat geen gebruik maakt van parameters. Waarom zou je dan parameters gebruiken? Eén van de redenen is dat het voorbeeld met parameters sneller is. Dat komt omdat de query niet twee keer geparsed hoeft te worden. Niet alleen door TSQLQuery, maar ook door de database server. De meeste database servers ondersteunen parameters. Als eenzelfde query meerdere keren wordt aangeroepen met steeds verschillende waarden als parameter, dan hoeft de database server maar één keer een plan te maken voor het uitvoeren van de query. Dat maakt het sneller. (Met TSQLQuery.Prepare kan je zelf bepalen wanneer de query door de database server geparsed en gepland moet worden. Met .UnPrepare zorg je ervoor dat de query de volgende keer dat hij uitgevoerd wordt, altijd eerst opnieuw wordt voorbereid door de database-server)
316326
317327Een andere reden kan het voorkomen van SQL-injectie [1](http://en.wikipedia.org/wiki/SQL_injection) zijn (zie ook [[Secure programming]]).
318328
319329Tenslotte: in sommige gevallen kan het de code ook een stuk vergemakkelijken.
320330
321331## Troubleshooting: TSQLConnection logging
322332
323333Je kan een TSQLConnection laten loggen wat hij allemaal uitvoert. Dit kan handit zijn om te kijken wat je Lazarus-programma precies verstuurt naar de database, om de database-componenten zelf te debuggen en wellicht je queries te optimaliseren. Let wel: als je parameters in queries gebruikt, worden die vaak door de database TSQLConnection afstammeling (zoals TIBConnection) binair verstuurd; je kan dus niet zomaar altijd de gelogde SQL knippen en plakken in een databasequerytool. Hoe het ook zij, loggen van de connectie kan veel inzicht geven in wat je programma doet.
324334
325335Hiervoor moet je twee dingen doen:
326336
3273371. bij de TSQLConnection instellen welke event types/soorten gebeurtenissen hij moet loggen
3283382. een functie aanwijzen die de loggegevens ontvangt van de TSQLConnection en op de een of andere manier verwerkt.
329339
330340De functie moet er een zijn van het type TDBLogNotifyEvent (zie sqldb.pp), dus met deze signatuur:
331341
332342``` pascal
333343TDBLogNotifyEvent = Procedure (Sender : TSQLConnection; EventType : TDBEventType; Const Msg : String) of object;
334344```
335345
336346Een stukje voorbeeldcode kan dit verduidelijken:
337347
338348``` pascal
339349uses
340350...
341351TSQLConnection, //of een kindobject zoals TIBConnection, TMSSQLConnection
342352...
343353var
344354type
345355 TMyApplication = class(TCustomApplication); //dit is onze applicatie die de connectie gebruikt
346356...
347357 private
348358 // We laten bijvoorbeeld het log wegschrijven naar deze variabelen in onze class:
349359 FConnectionLog: TStringList;
350360...
351361 protected
352362 // Deze procedure gaat de te loggen events opvangen:
353363 procedure GetLogEvent(Sender: TSQLConnection; EventType: TDBEventType; Const Msg : String);
354364...
355365 procedure TMyApplication.GetLogEvent(Sender: TSQLConnection;
356366 EventType: TDBEventType; const Msg: String);
357367 // De procedure wordt aangeroepen door TSQLConnection en slaat de ontvangen logberichten
358368 // op in de FConnectionLog stringlist
359369 var
360370 Source: string;
361371 begin
362372 // Nicely right aligned...
363373 case EventType of
364374 detCustom: Source:='Custom: ';
365375 detPrepare: Source:='Prepare: ';
366376 detExecute: Source:='Execute: ';
367377 detFetch: Source:='Fetch: ';
368378 detCommit: Source:='Commit: ';
369379 detRollBack: Source:='Rollback:';
370380 else Source:='Unknown event. Please fix program code.';
371381 end;
372382 FConnectionLog.Add(Source + ' ' + Msg);
373383 end;
374384
375385...
376386 // We moeten de TSQLConnection wel vertellen wat te loggen...
377387 FConnection.LogEvents:=LogAllEvents; //= [detCustom, detPrepare, detExecute, detFetch, detCommit, detRollBack]
378388 // ... en waar de info heen moet:
379389 FConnection.OnLog:=@Self.GetLogEvent;
380390...
381391 // nu kunnen we de connectie gebruiken en zal de FConnectionLog stringlist zich vullen met logberichten.
382392```
383393
384394Je kan ook de GlobalDBLogHook functie in TSQLConnection gebruiken wanneer je voor meerdere connecties tegelijk wil loggen. Tenslotte: zoals in de introductie vermeld beschrijft dit stuk de FPC manier van doen; als je met Lazarus werkt kan je sneller werken door aan de TSQLConnection OnLog gebeurtenis je gebeurtenisprocedure/event handler te koppelen.
385395
386396---
387397*From the Free Pascal wiki, page "SqlDBHowto/nl", as it stood on 2021-11-16 after a change by Slawek, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history