Sign in Join
Wiki copytemporary

SqlDBHowto: what changed

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

OldNewLine
11This text is setup as a 'how-to'. I want to answer a number of questions one by one, and explain how you can use the various classes. All those questions are put one after the other and form a sort of tutorial.
22
33I will try to word it in such a way that the text can be used for Lazarus as well as Free Pascal. However, the examples are for FreePascal (i.e. they are console applications.)
44
55## Where can I find official documentation?
66
77Please see the official documentation at [SQLDB documentation](http://www.freepascal.org/docs-html/fcl/sqldb/index.html).
88
99## How to connect to a database server?
1010
1111SqlDB doesn't connect to a database server directly but uses a client that corresponds to the used database server. SqlDB sends the commands to the client library; the client library connects to the database and and transfers the commands. This means that a client library must be installed on the computer to make a connection to a database. Under Windows a client is usually a .dll, under Linux an .so and under OS/X a .dylib.
1212
1313When the client library is installed properly you can connect to a database server using a TSQLConnection component. Various TSQLConnection components are available for different database servers (see [[SQLdb Package]]):
1414
1515- Firebird/Interbase: [[TIBConnection]]
1616- MS SQL Server: [[TMSSQLConnection]] (available since FPC 2.6.1)
1717- MySQL v4.0: [[TMySQL40Connection]]
1818- MySQL v4.1: [[TMySQL41Connection]]
1919- MySQL v5.0: [[TMySQL50Connection]]
2020- MySQL v5.1: [[TMySQL51Connection]] (available since FPC version 2.5.1
2121- MySQL v5.5: [[TMySQL55Connection]] (available since Lazarus 1.0.8/FPC version 2.6.2
2222- MySQL v5.6: [[TMySQL56Connection]] (available in Lazarus 1.2.4/FPC version 2.6.4
2323- ODBC: [[TODBCConnection]] (see [[ODBCConn|ODBCConn#TODBCConnection]])
2424- Oracle: [[TOracleConnection]] (see [[Oracle]])
2525- PostgreSQL: [[TPQConnection]] (see [[postgresql|postgresql#SQLDB]])
2626- Sqlite3: [[TSQLite3Connection]] (available since FPC version 2.2.2, see [[SQLite|SQLite#Built-in_SQLDB]])
2727- Sybase ASE: [[TSybaseConnection]] (available since FPC 2.6.1, see [[Lazarus Database Overview|Lazarus and MSSQL/Sybase]])
2828
2929Note for MySQL - There are many differences between the client versions to the extent that the clients and connections cannot be interchanged. If a MySQL client library version 4.1 is installed, you have to use a TMySQL41Connection. This is not related to the MySQL server; using the MySQL 4.1 client library you can probably connect to a MySQL 5.0 server (see MySQL documentation regarding what combinations are supported).
3030
3131Although details differ for the various databases, in general you need to set four properties to connect to a database server:
3232
3333- the server name or IP address
3434- the name of the database
3535- the username
3636- the password
3737
3838When these properties are set, you can create a connection with the 'open' method. If the connection fails, a EDatabaseError exception is thrown. Use the property 'connected' to test if a connection has been made with the database server. Use the 'close' method to end the connection with the server.
3939
4040``` pascal
4141Program ConnectDB;
4242
4343{$mode objfpc}{$H+}
4444
4545uses
4646 IBConnection;
4747
4848function CreateConnection: TIBConnection;
4949begin
5050 result := TIBConnection.Create(nil);
5151 result.Hostname := 'localhost';
5252 result.DatabaseName := '/opt/firebird/examples/employee.fdb';
5353 result.UserName := 'sysdba';
5454 result.Password := 'masterkey';
5555end;
5656
5757var
5858 AConnection : TIBConnection;
5959
6060begin
6161 AConnection := CreateConnection;
6262 AConnection.Open;
6363 if Aconnection.Connected then
6464 writeln('Successful connect!')
6565 else
6666 writeln('This is not possible, because if the connection failed, ' +
6767 'an exception should be raised, so this code would not ' +
6868 'be executed');
6969 AConnection.Close;
7070 AConnection.Free;
7171end.
7272```
7373
7474If an exception is thrown, read the error message carefully. It may be that the database server is not running, the user name or password are incorrect or the database name or IP address are typed incorrectly. If the error message states that the client library cannot be found, then check if the client is installed correctly. Often the error message states literally the name of the file looked for.
7575
7676## How to execute direct queries/make a table?
7777
7878SqlDB - the name says it all - only works with database server that make use of SQL. SQL stands for 'Structured Query Language' SQL is a language developed to allow working with relational databases. Virtually every database system has its own dialect, but a large number of SQL statements are the same for all database systems.
7979
8080In FPC, there is a difference between:
8181
8282- SQL statements that return information (a dataset). For this, you have to use the TSQLQuery component; see #How to read data from a table?.
8383- statements that do not return information but do something else, e.g. update data. For this, you may also use the 'ExecuteDirect' method of a TSQLConnection. (You can also use this if you get a dataset back but are not interested in the results, e.g. in a selectable stored procedure).
8484
8585Most database system execute SQL statements within a transaction. If you want changes made within a transaction available in other transactions, or have those changes available even after closing the transaction(!), then you have to 'commit' the transaction.
8686
8787To support transactions Sqldb contains the TSQLTransaction component. A SQL statement that is executed by Sqldb must always be executed within a transaction, even if the database system does not support transactions. Also, there are database systems that do support transaction for which TSQLConnection does not (yet) support transaction. Even then, you must use the TSQLTransaction component.
8888
8989To use `TSQLConnection.ExecuteDirect` to execute a SQL statement you must specify which 'Transaction' must be used. In turn, to use TSQLTransaction you must specify which TSQLConnection component must be used.
9090
9191The following example creates a table 'TBLNAMES' with fields 'NAME' and 'ID' and inserts two records. This time using SQLite. The used SQL statements are not explained. For more information about the SQL statements, their use and syntax, please refer to the database system documentation. Note that this example does not attempt to catch any errors, thats a bad thing! Look into [Exceptions](https://www.freepascal.org/docs-html/current/ref/refch17.html#x226-24800017).
9292
9393``` pascal
9494program CreateTable;
9595{$mode objfpc} {$ifdef mswindows}{$apptype console}{$endif}
9696uses
9797 sqldb, sqlite3conn;
9898
9999var
100100 AConnection : TSQLite3Connection;
101101 ATransaction : TSQLTransaction;
102102
103103begin
104104 AConnection := TSQLite3Connection.Create(nil);
105105 AConnection.DatabaseName := 'test_dbase';
106106
107107 ATransaction := TSQLTransaction.Create(AConnection);
108108 AConnection.Transaction := ATransaction;
109109 AConnection.Open;
110110 ATransaction.StartTransaction;
111111 AConnection.ExecuteDirect('create table TBLNAMES (ID integer, NAME varchar(40));');
112112
113113 ATransaction.Commit;
114114
115115 ATransaction.StartTransaction;
116116 AConnection.ExecuteDirect('insert into TBLNAMES (ID,NAME) values (1,''Name1'');');
117117 AConnection.ExecuteDirect('insert into TBLNAMES (ID,NAME) values (2,''Name2'');');
118118 ATransaction.Commit;
119119 AConnection.Close;
120120 ATransaction.Free;
121121 AConnection.Free;
122122end.
123123```
124124
125125## How to read data from a table?
126126
127127Use the TSQLQuery component to read data from a table. A TSQLQuery component must be connected to a TSQLConnection component and a TSQLTransaction component to do its work. Setting the TSQLConnection and TSQLTransaction is discussed in #How to connect to a database server? and #How to execute direct queries/make a table?.
128128
129129When the TSQLConnection, TSQLTransaction and TSQLQuery are connected, then TSQLQuery needs to be further configured to work. TSQLQuery has a 'SQL' property containing a TStrings object. The 'SQL' property contains a SQL statement that must be executed. If all data from a table `tablename` must be read, then set the 'SQL' property to:
130130
131131``` sql
132132'SELECT * FROM tablename;'
133133```
134134
135135.
136136
137137Use 'open' to read the table from the server and put the data in the TSQLQuery dataset. The data can be accessed through TSQLQuery until the query is closed using 'close'.
138138
139139TSQLQuery is a subclass of TDataset. TDataset has a 'Fields' collection that contains all columns of the table. The TDataset also keeps track of the current record. Use 'First', 'Next', 'Prior' and 'Last' to change the current record. 'Bof' returns 'True' if the first record is reached, and 'Eof' returns 'True' if the last record is reached. To read the value of a field in the current record, first find the right 'TField' object and then use 'AsString', 'AsInteger', etc.
140140
141141### Example: reading data from a table
142142
143143Below is an example that displays all values of the table as it was made in CreateTable example above. Copy the test_database file into the ShowData working directory. Note, again, no error checking has taken place !
144144
145145``` pascal
146146Program ShowData;
147147 {$mode objfpc} {$ifdef mswindows}{$apptype console}{$endif}
148148uses
149149 DB, Sysutils, sqldb, sqlite3conn;
150150
151151var
152152 AConnection : TSQLConnection;
153153 ATransaction : TSQLTransaction;
154154 Query : TSQLQuery;
155155
156156begin
157157 AConnection := TSQLite3Connection.Create(nil);
158158 ATransaction := TSQLTransaction.Create(AConnection);
159159 AConnection.Transaction := ATransaction;
160160 AConnection.DatabaseName := 'test_dbase';
161161 Query := TSQLQuery.Create(nil);
162162 Query.SQL.Text := 'select * from tblNames';
163163 Query.Database := AConnection;
164164 Query.Open;
165165 while not Query.Eof do
166166 begin
167167 Writeln('ID: ', Query.FieldByName('ID').AsInteger, 'Name: ' +
168168 Query.FieldByName('Name').AsString);
169169 Query.Next;
170170 end;
171171 Query.Close;
172172 AConnection.Close;
173173 Query.Free;
174174 ATransaction.Free;
175175 AConnection.Free;
176176end.
177177```
178178
179179(The code above of course is not quite finished, it misses 'try...finally' blocks. However, the above code intends to show the database code and thus the finishing touches are left out.) Please note that 'TSQLTransaction.StartTransaction' is not used. This is not necessary. When TSQLQuery is opened, the SQL statement is executed and if no transaction is available then a transaction is automatically started. The programmer does not need to start the transaction explicitly. The same applies for the connection maintained by TSQLConnection. The connection is opened as needed, the line 'Aconnection.Open' is not really required. If a TSQLTransaction is destroyed, an automatic 'rollback' will be executed. **Possible changes to data contained in the transaction will be lost.**
180180
181181### Why does TSQLQuery.RecordCount always return 10?
182182
183183To count the records in a dataset, use '.RecordCount'. However, notice that '.RecordCount' shows the number of records that is already loaded from the server. For performance reasons, SqlDB does not read all records when opening TSQLQuery by default, only the first 10. Only when the eleventh record is accessed will the next set of 10 records be loaded, etc. Using '.Last', all records will be loaded.
184184
185185When you want to know the real number of records on the server you can first call '.Last' and then call '.RecordCount'.
186186
187187An alternative is available. The number of records returned by the server is set by the '.PacketRecords' property. The default value is 10; if you make it -1 then all records will be loaded at once.
188188
189189In current stable FPC, '.RecordCount' does not take filters into account, i.e. it shows the unfiltered total.
190190
191191If you need the exact number of records, it often is a better idea to directly query the number of records in a query using another SQL query, but you would have to do that in the same transaction, as other transactions may have changed the number of records in the meanwhile.
192192
193193### Lazarus
194194
195195Lazarus has various components to show data from a TDataset on a form. Instead of a While-loop and Writeln statements as used above, you can use the components to show the data in a table. Place the right TSQLConnection, TSQLTransaction and TSQLQuery components on a form, then connect them and set them properly. In addition you will need a TDatasource; set the 'TDatasource.Dataset' property to the TSQLQuery component you used. (**Note** do not set the 'TSQLQuery.Datasource' property to the TDatasource compnent you used. The 'TSQLQuery.Datasource' property is used only in master-detail tables - see [[MasterDetail]]) Subsequently you may put a TDBGrid onto the form and set the 'Datasource' property of the grid to the TDatasource component you added before.
196196
197197To see if it all works, set the 'Connected' property of the TSQLConnection to 'True' in the Lazarus IDE. The IDE will try to connect to the database server immediately. If this works you can set the 'TSQLQuery.Active' property to 'True'. If everything is right, you will see - within the IDE - all data from the table immediately on the screen.
198198
199199## How to change data in a table?
200200
201201To change the data in a record (or records), the general process is get TSQLQuery to search for the records you wish to change, make the changes there and then push them back to the database. The [[TDataSet]] (from which TSQLQuery is derived) must be set to edit mode. To enter edit mode call the '.Edit', '.Insert' or '.Append' methods. Use the '.Edit' method to change the current record. Use '.Insert' to insert a new record before the current record. Use '.Append' to insert a new record at the end of the table. In edit mode you can change field values through the 'Fields' property. Use 'Post' to validate the new data, if the data is valid then the edit mode is left. If you move to another record - for example by using '.Next' - and the dataset is in edit mode, then first '.Post' is called. Use '.Cancel' to discard all changes you made since the last '.Post' call and leave the edit mode.
202202
203203``` pascal
204204Query.Edit;
205205Query.FieldByName('NAME').AsString := 'Edited name';
206206Query.Post;
207207```
208208
209209The above is not the complete story yet. TSQLQuery is derived from TBufDataset which makes use of buffered updates. Buffered update means that after you called 'Post' the changes in the dataset are visible immediately, but they are not sent to the database server. What does happen is that the changes are maintained in a change log. When the '.ApplyUpdates' method is called, then all changes in the change log are sent to the database. Only then will database server know of the changes. The changes are sent to the server within a transaction of TSQLTransaction. Make sure to properly set the transaction before 'ApplyUpdates'. After applying the updates, a commit must be executed to save the changes on the database server.
210210
211211The below is an example of changing the data in a table, sending the changes to the server and committing the transaction. Again, no error checking, again, thats bad!
212212
213213``` pascal
214214Program EditData;
215215{$mode objfpc} {$ifdef mswindows}{$apptype console}{$endif}
216216uses
217217 db, sqldb, sqlite3conn;
218218var
219219 AConnection : TSQLConnection;
220220 ATransaction : TSQLTransaction;
221221 Query : TSQLQuery;
222222
223223begin
224224 AConnection := TSQLite3Connection.Create(nil);
225225 ATransaction := TSQLTransaction.Create(AConnection);
226226 AConnection.Transaction := ATransaction;
227227 AConnection.DatabaseName := 'test_dbase';
228228 Query := TSQLQuery.Create(nil);
229229 Query.DataBase := AConnection;
230230 Query.SQL.Text := 'select * from tblNames where ID = 2';
231231 Query.Open;
232232 Query.Edit;
233233 Query.FieldByName('NAME').AsString := 'Name Number 2';
234234 Query.Post;
235235 Query.UpdateMode := upWhereAll; // defined in db
236236 Query.ApplyUpdates;
237237 ATransaction.Commit;
238238 Query.Free;
239239 ATransaction.Free;
240240 AConnection.Free;
241241end.
242242```
243243
244244The actual works starts with the SQL statement "select * from tblNames where ID = 2" identifying the record (or records) you wish to change. If you leave out the "where ID = 2" bit, the TSQLQuery apparently sets ID (and other integer fields?) to 1. And therefore will operate on lines where ID=1 only. For a discussion of 'UpdateMode' continue reading.
245245
246246## How does SqlDB send the changes to the database server?
247247
248248In the code example in #How to change data in a table?, you will find the line
249249
250250``` pascal
251251Query.UpdateMode := upWhereAll;
252252```
253253
254254without explanation of what it does. The best way to find out what that line does is to leave it out. If you leave out the statement and the followed this howto precisely, then you will receive the following error message:
255255
256-`No update query specified and failed to generate one. (No fields for inclusion in where statement found)`
256+```text
257+No update query specified and failed to generate one. (No fields for inclusion in where statement found)
258+```
257259
258260To understand what went wrong, you must understand how changes are sent to the database server. The only way to get data in a SQL server is by executing SQL queries. SQL has three types of queries for three different ways of manupulating a record. To create a new record, change or delete a record insert, update and delete statements are executed respectively. An update statement may be as follows:
259261
260262``` sql
261263update TBLNAMES set NAME='Edited name' where ID=1;
262264```
263265
264266To send a change to the database server, Sqldb must assemble an update query. To assemble the query, three things are needed:
265267
266268The name of the table
267269The table name is retrieved from parsing the select query, although this doesn't always work.
268270
269271`UPDATE` or `INSERT` clause
270272These contain the fields that must be changed.
271273
272274`WHERE` clause
273275This contains the fields that determine which records should be changed.
274276
275277Every field (each *TField* in *Fields*) has a ProviderFlags property. Only fields with **pfInUpdate** in *ProviderFlags* will be used in the update or insert cluase of a query. By default all fields have *pfInUpdate* set in their *ProviderFlags* property.
276278
277279Which fields are used in the `WHERE` clause depends on the *UpdateMode* property of the query and the *ProviderFlags* property of the fields. Fields with *pfInkey* in their *ProviderFlags* are always used in the `WHERE` clause. A field will have the *pfInKey* flag set automatically if the field is part of the primary key of the table and 'TSQLQuery.UsePrimaryKeyAsKey' returns 'True'.
278280
279281The default value for *UpdateMode* of the query is *upWhereKeyOnly*. In this update mode only fields with *pfInkey* in their *ProviderFlags* property are used in the `WHERE` clause. If none of the fields have their *pfInKey* flag set, then no fields are available for the `WHERE` clause and the error message from the beginning of this section will be returned. You can solve the issue by:
280282
281283- Adding a primary key to the table and set *TSQLQuery.UsePrimaryKeyAsKey* to 'True', or
282284- Setting the *pfInkey* flag for one or more fields in code.
283285
284286The **UpdateMode** property knows two more possible values. 'upWhereAll' can be used to add all fields with the 'pfInWhere' flag set to the `WHERE` clause. By default all fields have this flag set. 'upWhereChanged' can be used to add only those fields that have the 'pfInWhere' flag set **and** that are changed in the current record.
285287
286288## How to handle Errors
287289
288290Run time errors are unavoidable, disks may fill up, necessary libraries or helper apps may not be available, things go wrong and we need to allow for that. The FPC detects and handles run time errors quite well. It usually gives you a concise and reasonable explanation of what went wrong. However, you will want to monitor and handle errors yourself for a number of reasons -
289291
290292- You probably don't want the programme to teminate at the first sign of trouble.
291293- If we do keep going, lets make sure any memory allocated in the problem area is recovered, we don't want any memory leaks.
292294- If we are going to go under however, lets give the user a context sensitive explanation.
293295
294296The following bit of code is based on the above examples but this time it DOES check for errors in critical places. Key is the try...finally...end and try...except...end blocks. You can test it by doing things like uninstalling SQLite3, putting a dummy file in place of the test_dbase database and so on.
295297
296298``` pascal
297299program DemoDBaseWithErrors;
298300{$mode objfpc} {$ifdef mswindows}{$apptype console}{$endif}
299301uses
300302 DB, Sysutils, sqldb, sqlite3conn;
301303var
302304 Connect : TSQLite3Connection;
303305 Trans : TSQLTransaction;
304306
305307procedure WriteTable (Command : string);
306308begin
307309 Connect.ExecuteDirect(Command);
308310 Trans.Commit;
309311end;
310312
311313procedure ReadTable ();
312314var
313315 Query : TSQLQuery;
314316 Count : smallint;
315317begin
316318 Count := 0;
317319 try
318320 Query := TSQLQuery.Create(nil);
319321 Query.DataBase := Connect;
320322 Query.SQL.Text:= 'select * from tblNames';
321323 Query.Open; // This will also open Connect
322324 while not Query.EOF do begin
323325 writeln('ID: ', Query.FieldByName('ID').AsInteger, ' Name: ' +
324326 Query.FieldByName('Name').AsString);
325327 Query.Next;
326328 Count := Count + 1;
327329 end;
328330 finally
329331 Query.Close;
330332 Query.Free;
331333 end;
332334 writeln('Found a total of ' + InttoStr(Count) + ' lines.');
333335end;
334336
335337procedure FatalError(ClassName, Message, Suggestion : string);
336338begin
337339 writeln(ClassName);
338340 writeln(Message);
339341 writeln(Suggestion);
340342 Connect.Close; // Its possibly silly worrying about freeing
341343 Trans.free; // if we are going to call halt() but its
342344 Connect.Free; // a demo, alright ?
343345 halt();
344346end;
345347
346348begin
347349 Connect := TSQLite3Connection.Create(nil);
348350 Trans := TSQLTransaction.Create(Connect);
349351 Connect.Transaction := Trans;
350352 Connect.DatabaseName := 'test_dbase';
351353 try
352354 if not fileexists(Connect.DatabaseName) then begin
353355 Connect.Open; // give EInOutError if (eg) SQLite not installed
354356 Trans.StartTransaction;
355357 WriteTable('create table TBLNAMES (ID integer Primary Key, NAME varchar(40));');
356358 Trans.Commit;
357359 end;
358360 Connect.open;
359361 Trans.StartTransaction;
360362 WriteTable('insert into TBLNAMES (NAME) values (''AName1'');');
361363 WriteTable('insert into TBLNAMES (NAME) values (''AName2'');');
362364 except
363365 on E : EDatabaseError do
364366 FatalError(E.ClassName, E.Message, 'Does the file contain the correct database ?');
365367 on E : EInOutError do
366368 FatalError(E.ClassName, E.Message, 'Have you installed SQLite (and dev package)?');
367369 on E : Exception do
368370 FatalError(E.ClassName, E.Message, 'Something really really bad happened.');
369371 end;
370372 ReadTable();
371373 Connect.Close;
372374 Trans.Free;
373375 Connect.Free;
374376end.
375377```
376378
377379## How to execute a query using TSQLQuery?
378380
379381Next to statements that return a dataset (see #How to read data from a table?) SQL has statements that do not return data. For example `INSERT`, `UPDATE` and `DELETE` statements do not return data. These statements can be executed using *TSQLConnection.ExecuteDirect*, but TSQLQuery can also be used. If you do not expect return data use *TSQLQuery.ExecSQL* instead of *TSQLQuery.Open*. As mentioned earlier, use *TSQLQuery.Open* to open the dataset returned by the SQL statement.
380382
381383The following procedure creates a table and inserts two records using TSQLQuery.
382384
383385``` pascal
384386procedure CreateTable;
385387
386388var
387389 Query : TSQLQuery;
388390
389391begin
390392 Query := TSQLQuery.Create(nil);
391393 try
392394 Query.Database := AConnection;
393395
394396 Query.SQL.Text := 'create table TBLNAMES (ID integer, NAME varchar(40));';
395397 Query.ExecSQL;
396398
397399 Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (1,''Name1'');';
398400 Query.ExecSQL;
399401
400402 Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (2,''Name2'');';
401403 Query.ExecSQL;
402404 finally
403405 Query.Free;
404406 end;
405407end;
406408```
407409
408410## How to use parameters in a query?
409411
410412In the code example of #How to execute a query using TSQLQuery? the same query is used twice, only the values to be inserted differ. A better way to do this is by using parameters in the query.
411413
412414The syntax of parameters in queries is different per database system, but the differences are handled by TSQLQuery. Replace the values in the query with a colon followed by the name of the parameter you want to use. For example:
413415
414416``` pascal
415417Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (:ID,:NAME);';
416418```
417419
418420This query will create two parameters: 'ID' and 'NAME'. To determine the parameters, the query is parsed at the moment the text of *TSQLQuery.SQL* is assigned or changed. All existing parameters will be removed and the new parameters will be added to the 'TSQLQuery.Params' property. Assigning a value to a parameter is similar to assigning a value to a field in the dataset:
419421
420422``` pascal
421423Query.Params.ParamByName('Name').AsString := 'Name1';
422424```
423425
424426You can't tell from the query what kind of data must be stored in the parameter. The data type of the parameter is determined at the moment a value is first assigned to the parameter. By assigning a value using '.AsString', the parameter is assigned the data type 'ftString'. You can determine the data type directly by setting the 'DataType' property. If an incorrect datatype is assigned to the parameter, then problems will occur during opening or executing the query. See [[Database field type]] for more information on data types.
425427
426428### Select query
427429
428430An example of a select query with parameters would be to change something like this:
429431
430432``` pascal
431433 Query.SQL.Text := 'select ID,NAME from TBLNAMES where NAME = '''+Edit1.Text+''' ORDER BY NAME ';
432434```
433435
434436to something like this:
435437
436438``` pascal
437439 Query.SQL.Text := 'select ID,NAME from TBLNAMES where NAME = :NAMEPARAM ORDER BY NAME ';
438440 Query.Params.ParamByName('NAMEPARAM').AsString := Edit1.Text;
439441```
440442
441443### Example
442444
443445The following example creates the same table as the previous example, but now parameters are used:
444446
445447``` pascal
446448procedure CreateTableUsingParameters;
447449
448450var
449451 Query : TSQLQuery;
450452
451453begin
452454 Query := TSQLQuery.Create(nil);
453455 try
454456 Query.Database := AConnection;
455457
456458 Query.SQL.Text := 'create table TBLNAMES (ID integer, NAME varchar(40));';
457459 Query.ExecSQL;
458460
459461 Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (:ID,:NAME);';
460462 Query.Prepare;
461463
462464 Query.Params.ParamByName('ID').AsInteger := 1;
463465 Query.Params.ParamByName('NAME').AsString := 'Name1';
464466 Query.ExecSQL;
465467
466468 Query.Params.ParamByName('ID').AsInteger := 2;
467469 Query.Params.ParamByName('NAME').AsString := 'Name2';
468470 Query.ExecSQL;
469471
470472 //Query.UnPrepare; // no need to call this; should be called by Query.Close
471473 Query.Close;
472474 finally
473475 Query.Free;
474476 end;
475477end;
476478```
477479
478480Notice that this example requires more code than the example without the parameters. Then what is the use of using parameters?
479481
480482Speed is one of the reasons. The example with parameters is faster, because the database server parses the query only once (in the .Prepare statement or at first run).
481483
482484Another reason to use prepared statements is prevention of [SQL-injection](http://en.wikipedia.org/wiki/SQL_injection) (see also [[Secure programming]].
483485
484486Finally, in some cases it just simplifies coding.
485487
486488## Troubleshooting: TSQLConnection logging
487489
488490You can let a TSQLConnection log what it is doing. This can be handy to see what your Lazarus program sends to the database exactly, to debug the database components themselves and perhaps to optimize your queries. NB: if you use prepared statements/parametrized queries (see section above), the parameters are often sent in binary by the TSQLConnection descendent (e.g. TIBConnection), so you can't just copy/paste the logged SQL into a database query tool. Regardless, connection logging can give a lot of insight in what your program is doing.
489491
490492Alternatives are:
491493
4924941. you can use the debugger to step through the database code if you have built FPC (and Lazarus) with debugging enabled.
4934952. if you use ODBC drivers (at least on Windows) you could enable tracelog output in the ODBC control panel.
4944963. many databases allow you to monitor all statements sent to it from a certain IP address/connection.
495497
496498If you use TSQLConnection logging, two things are required:
497499
4985001. indicate which event types your TSQLConnection should log
4995012. point TSQLConnection at a function that receives the events and processes them (logs them to file, prints them to screen, etc.).
500502
501503That function must be of type TDBLogNotifyEvent (see sqldb.pp), so it needs this signature:
502504
503505``` pascal
504506TDBLogNotifyEvent = Procedure (Sender : TSQLConnection; EventType : TDBEventType; Const Msg : String) of object;
505507```
506508
507509### FPC (or: the manual way)
508510
509511A code snippet can illustrate this:
510512
511513``` pascal
512514uses
513515...
514516TSQLConnection, //or a child object like TIBConnection, TMSSQLConnection
515517...
516518var
517519type
518520 TMyApplication = class(TCustomApplication); //this is our application that uses the connection
519521...
520522 private
521523 // This example stores the logged events in this stringlist:
522524 FConnectionLog: TStringList;
523525...
524526 protected
525527 // This procedure will receive the events that are logged by the connection:
526528 procedure GetLogEvent(Sender: TSQLConnection; EventType: TDBEventType; Const Msg : String);
527529...
528530 procedure TMyApplication.GetLogEvent(Sender: TSQLConnection;
529531 EventType: TDBEventType; const Msg: String);
530532 // The procedure is called by TSQLConnection and saves the received log messages
531533 // in the FConnectionLog stringlist
532534 var
533535 Source: string;
534536 begin
535537 // Nicely right aligned...
536538 case EventType of
537539 detCustom: Source:='Custom: ';
538540 detPrepare: Source:='Prepare: ';
539541 detExecute: Source:='Execute: ';
540542 detFetch: Source:='Fetch: ';
541543 detCommit: Source:='Commit: ';
542544 detRollBack: Source:='Rollback:';
543545 else Source:='Unknown event. Please fix program code.';
544546 end;
545547 FConnectionLog.Add(Source + ' ' + Msg);
546548 end;
547549
548550...
549551 // We do need to tell our TSQLConnection what to log:
550552 FConnection.LogEvents:=LogAllEvents; //= [detCustom, detPrepare, detExecute, detFetch, detCommit, detRollBack]
551553 // ... and to which procedure the connection should send the events:
552554 FConnection.OnLog:=@Self.GetLogEvent;
553555...
554556 // now we can use the connection and the FConnectionLog stringlist will fill with log messages.
555557```
556558
557559You can also use TSQLConnection's GlobalDBLogHook instead to log everything from multiple connections.
558560
559561### Lazarus (or: the quick way)
560562
561563Finally, the description above is the FPC way of doing things as indicated in the introduction; if using Lazarus, a quicker way is to assign an event handler to the TSQLConnection's OnLog event.
562564
563565## See also
564566
565567- [[Working With TSQLQuery]]
566568
567569---
568570*From the Free Pascal wiki, page "SqlDBHowto", as it stood on 2020-12-10 after a change by Wp, 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