AnyDAC offers TADQuery, TADCommand components and some TADConnection methods to execute SQL commands. The SQL commands are the main way to talk to SQL database.
TADConnection offers ExecSQL methods. These are simple to use overloaded methods useful when:
For example to execute few DDL commands:
ADConnection1.ExecSQL('drop table testtab');
ADConnection1.ExecSQL('create table testtab (id integer, name varchar(10))');
ADConnection1.ExecSQL('insert into testtab values (1, ''anydac'')');
To execute a parameterized query use the other overloaded method:
ADConnection1.ExecSQL('insert into testtab values (:id, :name)', [1, 'anydac']);
Also, TADConnection offers ExecSQLScalar methods different from ExecSQL methods, that they are returning a single result set value:
sName := ADConnection1.ExecSQLScalar('select name from testtab where id = :id', [1]);
In general, the TADQuery may be setup at design-time and / or at run-time.
To setup it at design-time, drop TADQuery on a form. TADQuery.Connection will be automatically set to point to a TADConnection on this form, if any. Then double click on TADQuery to invoke the AnyDAC Query Editor:
There you can specify SQL text, optionally specify parameter and macro values and set options. Press "Query Builder" button to invoke the graphical SQL query builder tool. Note, to enable syntax highlighting check QU4 at GUI Questions.
Press "Execute" button to execute the query. If a command returns a result set, then it will be output on "RecordSet" pane and the result set structure on the "Structure" pane. If DBMS returns any messages or warnings for a command, they will be put to "Messages" pane. The "Next RecordSet" button allows to walk through all result sets returned by command.
When you are testing DML commands, like a UPDATE / INSERT / DELETE, consider to mark "Auto Rollback" check box to automatically rollback actions performed by a command.
Press OK to store changes in the TADQuery.
The parameterized query usage is one of the best practices at SQL database application development. Main benefits are:
To put a parameter marker into the SQL text use :<name> syntax. Then assign corresponding values using Params collection. For example:
ADQuery1.SQL.Text := 'select * from tab where code = :Code';
ADQuery1.ParamByName('code').AsString := '123';
ADQuery1.Open;
Before query will be executed each parameter Name, DataType and ParamType must be filled. Also Position may be need to set.
When you assign SQL property and ResourceOptions.ParamCreate is True, the Params collection will be automatically filled in. Note, that TADParam.Name and TADParam.Position only will be set. The DataType, ParamType and other properties must be set by application. The parameters in Params collection appears in the same order as in the SQL command.
Also you can fill the Params by code. For that you have to turn off automatic parameters creation by setting ResourceOptions.ParamCreate to False. For example:
ADQuery1.ResourceOptions.ParamCreate := False; ADQuery1.SQL.Text := 'select * from tab where code = :Code'; with ADQuery1.Params do begin Clear; with Add do begin Name := 'CODE'; DataType := ftString; Size := 10; ParamType := ptInput; end; end;
The parameters may be bind to the SQL command either by name, either by position. Use the Params.BindMode property to control that:
The DataType may be specified either explicitly, either implicitly by assigning a parameter value to the Value or AsXxxx properties. Otherwise, the FormatOptions.DefaultParamDataType will be used.
When ParamType is not specified, the ResourceOptions.DefaultParamType will be used. By default all parameters are input only. If some of the parameters are output, then this must be specified explicitly.
The input parameter values must be set before query execution. That may be done using one of the ways:
ADQuery1.ParamByName('code').AsString := 'DA1001';
ADQuery1.Open;
ADQuery1.Open('', ['DA1001']);
To set parameter value to Null, specify parameter data type, then call Clear method:
with ADQuery1.ParamByName('name') do begin DataType := ftString; Clear; end; ADQuery1.ExecSQL;
The output parameter values are accessible after query is executed. Some SQL commands and DBMS may require first to process all command result sets, then output parameter values will be received. To force the output parameter values delivery use the TADAdaptedDataSet.GetResults method. Also, read "RETURNING unified support" article about output parameters in Firebird and Oracle RETURNING phrase.
Before a query will be executed it must be prepared. For that AnyDAC automatically performs the following actions:
While the command is prepared the connection must be active and the query will keep server resources. To unprepare query and release all resources use Unprepare or Disconnect methods.
When you need to execute a command only once, you may set ResourceOptions.DirectExecute to True. For some DBMS (SQL Server, SQL Anywhere) that will speed up execution, because will avoid the costly operation of SQL command preparation.
To execute a query, which does not return a result set, use the ExecSQL methods. If a query returns a result set, then exception "[AnyDAC][Phys][MSAcc]-310. Cannot execute command returning result sets" will be raised.
To execute a query, returning a result set and open this result set, use the Open methods. If a query returns no result sets, then exception "[AnyDAC][Phys][MSAcc]-308. Cannot open / define command, which does not return result sets" will be raised.
To execute an add-hock query, use ExecuteOrOpen method. Note, that query may be executed asynchronously. If the query is a command batch, then check "Command Batches" for details.
When the parameter type was changed after the first query Prepare / ExecSQL / Open call, then AnyDAC will raise an exception on a subsequent call:
[AnyDAC][Phys][IB]-338. Parameter [Xxxx] data type is changed. Query must be reprepared.
AnyDAC at first Prepare / ExecSQL / Open call remembers the parameter data type. The data type may be specified either explicitly by setting TADParam.DataType property, either implicitly by assigning value using TADParam.AsXxxx or TADParam.Value properties. When before next ExecSQL / Open call an application changes parameter data type, then on the next ExecSQL / Open call "Parameter [xxxxx] data type is changed" will be raised.
The TADParam.Value property does not change parameter data type on the consequent calls. It casts the assigning value to the current data type. So, to avoid the above exception use:
Use the TADQuery.RowsAffected property to get the number of rows processed by the command. For example, the number of deleted rows by the DELETE command. Note, at MS SQL Server RowsAffected may be unexpectedly equal to -1 when a stored procedure or a table trigger omits
SET NOCOUNT ON. And use the TADQuery.RecordCount property to get the number of fetched rows. Note, AnyDAC does not provide "N rows processed" messages, if needed the application has to build it.
If the command will return an error, an exception will be raised. See the "Handling Errors" topic for more details. The exception may be processed using one of 3 ways:
try ADQuery1.ExecSQL; except on E: EADDBEngineException do ; // do something here end;
procedure TForm1.ADConnection1Error(ASender: TObject; const AInitiator: IADStanObject; var AException: Exception); begin if (AException is EADDBEngineException) and (EADDBEngineException(AException).Kind = ekRecordLocked) then AException.Message := 'Please, try the operation later. At moment, the record is busy'; end; ADConnection1.OnError := ADConnection1Error;
Note, in case of the Array DML, the error handling may be more complex.
Also the command may return warnings, hints and messages, depending on a DBMS. To enable messages processing set ServerOutput to True. To process them use the TADConnection.Messages property. For example:
var i: Integer; begin ADConnection1.ResourceOptions.ServerOutput := True; ADQuery1.ExecSQL; if ADConnection1.Messages <> nil then for i := 0 to ADConnection1.Messages.ErrorCount - 1 do Memo1.Lines.Add(ADConnection1.Messages[i].Message); end;
Some DBMS, like SQL Server, returns messages as an additional result set. So, to process messages the application will need to process multiple result sets. And more complex example, providing status and messages for SQL Server. As you see, we are using TADMemTable to store result set with rows.
var i: Integer; begin ADConnection1.ResourceOptions.ServerOutput := True; ADQuery1.FetchOptions.AutoClose := False; ADQuery1.Open('select * from Region; print ''Hello'''); ADMemTable1.Data := ADQuery1.Data; Memo1.Lines.Add(Format('%d rows processed', [ADMemTable1.RecordCount])); ADQuery1.NextRecordSet; if ADConnection1.Messages <> nil then for i := 0 to ADConnection1.Messages.ErrorCount - 1 do Memo1.Lines.Add(ADConnection1.Messages[i].Message); end;
By default all SQL command executions are performed in auto-commit mode. That means, when right before command execution there was no active transaction, then it will be started. And right after command execution this implicit transaction will be finished:
In case of Firebird and Interbase an implicit transaction will be started right before query preparation. Note, that there is no need to surround a single command execution into explicit transaction, just use the auto-commit mode.
Most database applications have backend administration utilities, which must execute SQL script. These scripts are written using lines appropriate to the DBMS SQL script syntax. To execute SQL scripts use TADScript component.
Sometimes a database application may need to execute heterogeneous queries using tables from different databases or to execute SQL commands on TDataSet descendants instead of the database tables. To execute such queries use Local SQL engine and TADLocalSQL component.
|
What do you think about this topic? Send feedback!
|