Reputation: 2033
I run various SQL statements using MSSQL and ADO.
The code sequence looks like this:
aADOQuery.Active := False;
aADOQuery.SQL.Text := ' MY SQL STATEMENT ';
aADOQuery.ExecSQL;
aADOQuery.Active := True;
The last statement fails if the SQL return result is empty. How to check for this case to avoid run time errors?
Note: The SQL statement comes from a memo where the user is typing the SQL.
Upvotes: 2
Views: 4707
Reputation: 6477
Whilst this isn't a direct answer to your question, I wrote an SQL command interpreter tool which allows various commands to be sent to the database. If the first word of the query is not 'select' then 'execsql' is the command. For 'select', I determined whether the query was what Borland calls a live query, in which interactive editing is possible. The 'query' variable in my program is a TClientDataSet which is connected via a TDataSetProvider to a TSQLDataSet, but this should also work with AdoQuery.
procedure TForm1.Button1Click(Sender: TObject); // this is the 'execute query' button
var
i: integer;
cmd: string[6];
tmp: string;
begin
tmp:= mem.lines[0];
i:= 1;
while tmp[i] = ' ' do inc (i);
dec (i);
if i > 0 then tmp:= copy (tmp, i + 1, length (tmp) - i);
cmd:= '';
for i:= 1 to 6 do cmd:= cmd + upcase (tmp[i]);
query.close;
sdsquery.commandtext:= mem.text;
if cmd = 'SELECT' then
begin
if livequery then
begin
dbgrid.options:= dbgrid.Options + [dgEditing];
dbNavigator1.visiblebuttons:= [nbfirst, nbprior, nbNext, nbLast, nbInsert,
nbDelete, nbedit, nbpost, nbcancel,
nbrefresh];
end
else
begin
dbgrid.options:= dbgrid.Options - [dgEditing];
dbNavigator1.visiblebuttons:= [nbfirst, nbprior, nbNext, nbLast];
end;
query.open
end
else sdsquery.ExecSQL;
end;
function TForm1.LiveQuery: boolean;
// check what the second token after 'from' is
const
EOI = #26;
var
cmdlen, curpos: integer;
ch: char;
tmp: string;
Procedure GetChar;
begin
inc (curpos);
if curpos <= cmdlen
then ch:= mem.text[curpos]
else ch:= EOI;
end;
Function Token: string;
const
punct: set of char = [' ', ',', ';', EOI, #10, #13];
begin
result:= '';
while ch in punct do getchar;
while not (ch in punct) do
begin
result:= result + upcase (ch);
getchar
end
end;
begin
ch:= ' ';
cmdlen:= length (mem.text);
curpos:= 0;
tmp:= token;
while tmp <> 'FROM' do tmp:= token;
tmp:= token; // this should be the first table name
if ch = ',' then result:= false // select ... from table1, table2
else if ch = EOI then result:= true // select * from table1
else
begin
tmp:= token;
result:= (tmp = 'WHERE') or (tmp = 'ORDER')
end;
end;
Upvotes: 2
Reputation: 21232
If your query returns a record set (SELECT
statements) you should not use ExecSQL
but simply aADOQuery.Open
or Active := True
.
For queries that do not return a record set e.g. INSERT
/UPDATE
/DELETE
, use ExecSQL
. in most cases you will get back aADOQuery.RowsAffected
by your query.
Other SQL statements that you should use ExecSQL
are CREATE
/ALTER
/DROP
/EXEC
etc... (no RowsAffected
return in this case)
If the query does not return a cursor to data (such as INSERT
statement), trying to Open
or setting such TDataSet
to Active
will fail.
You could use ADOConnection.Execute
instead of TADOQuery
to execute your command-text, and then inspect if there is a valid Recordset
returning from ADOConnection
.
In ADOConnection.OnExecuteComplete
your could do something like this:
procedure TForm1.ADOConnection1ExecuteComplete(Connection: TADOConnection;
RecordsAffected: Integer; const Error: Error;
var EventStatus: TEventStatus; const Command: _Command;
const Recordset: _Recordset);
begin
// check for errors
if Assigned(Error) then
begin
Memo1.Lines.Add('Error: ' + Error.Description);
end;
// check for a valid recordset
if Assigned(Recordset) then
begin
MyDataSet.Recordset := Recordset; // MyDataSet is TADODataSet
end;
// check for affected rows
if RecordsAffected >= 0 then
Memo1.Lines.Add('Records affected: ' + IntToStr(RecordsAffected))
else
Memo1.Lines.Add('Record count: ' + IntToStr(MyDataSet.RecordCount));
end;
Upvotes: 8