I am working on a project with mysql, and for this I am doing a class using zeoslib, I can make the connection, I can execute querys like insert, update, etc. But my problem is with select, I run it quietly, but how would I do to get the return? I would like something like PHP in that I inform the column name and it returns me the value of the row that is in that column.
It's important what you want to select, here is an example that uses TZQuery to get an integer value;
function TAccess.getProgramNo(aProgramName:WideString):Integer;
var
q:TZQuery;
begin
Result := -1;
q := TZQuery.Create(Self);
try
q.Connection := conn;
q.SQL.Text := ' SELECT progno FROM programs WHERE name = :name ORDER BY progno ASC ';
q.ParamByName('name').Value := aProgramName;
q.Open;
if q.RecordCount > 0 then
Result := q.FieldByName('progno').AsInteger;
finally
q.Free();
end;
end;
If you want to return a list of objects (i did not compile this);
function TAccess.getPrograms(aProgramName:WideString):TList;
var
q:TZQuery;
begin
Result := TList.Create;
q := TZQuery.Create(Self);
try
q.Connection := conn;
q.SQL.Text := ' SELECT progno FROM programs WHERE name = :name ORDER BY progno ASC ';
q.ParamByName('name').Value := aProgramName;
q.Open;
While not q.EOF do
begin
result.Add(TZoo.Create(....));
q.Next;
end;
finally
q.Free();
end;
end;
Related
for read data from database server im using mysql query like this:
FDQuery1.SQL.Text := 'select * from `table` WHERE dcid=3;';
FDQuery1.Open;
memo1.Lines.Add( FDQuery1.FieldByName('value').AsString );
but when i have to use a loop for read data from mysql
i need a array to read and store data on it
i know in php its like this
$arr = array();
while($obj = $q->fetch_object() ){
$arr[] = $obj;
}
but how can i do this syntax in Delphi ?
You don't need to do FDQuery1.First as that is implicit in the FDQuery1.Open. However if you want to know how many records you have it's important to do a FDQuery1.Last; before using FDQuery1.RecordCount to get the true record count otherwise you might get strange results.
All you need to do get your data into a memo is this
FDQuery1.SQL.Text := 'select * from table WHERE dcid=3;';
memo1.Lines.clear;
FDQuery1.Open;
While not FDQuery1.Eof do
begin
memo1.Lines.Add( FDQuery1.FieldByName('value').AsString );
FDQuery1.next;
end;
end;
FDQuery1.Close;
although a better solution with minimal exception handling is
FDQuery1.SQL.Text := 'select * from table WHERE dcid=3;';
memo1.Lines.clear;
try
try
begin
FDQuery1.Open;
While not FDQuery1.Eof do
begin
memo1.Lines.Add( FDQuery1.FieldByName('value').AsString );
FDQuery1.next;
end;
end;
end;
except
on E : Exception do
begin
showmessage ('Exception class name = '+E.ClassName+ slinebreak
+ 'Exception message = '+E.Message);
end //on E
end; //try-except
finally
FDQuery1.Close;
end; //try-finally
You mention an array. This is not needed if you only want to put the data in a memo but if you did want to put the data in an array (a dynamic array of variants as you don't know at design time how many record elements you need, how many field elements you need or what type each field is) then you would use the following code.
(Note this is deliberately not optimised code as I was trying to make the process clear)
Const
FirstRecordIndex = 0;
FirstFieldIndex = 0;
Var
DataArray : Variant;
TheRecordCount, TheFieldCount,
RecordNumber, FieldNumber : integer;
Data : variant;
begin
FDQuery1.SQL.Text := 'select * from table WHERE dcid=3;';
FDQuery1.Open;
FDQuery1.Last; //to get correct recordcount
TheRecordCount := FDQuery1.RecordCount;
TheFieldCount := FDQuery1.FieldCount;
FDQuery1.First; //go back to the beginning of the dataset
//set the dimensions of the 2D array of variants to hold data
DataArray := VarArrayCreate([FirstRecordIndex, TheRecordCount, FirstFieldIndex, TheFieldCount], varVariant ); //element can be of any type
//fill it
RecordNumber := -1; //initialise record indexe to just before the start
While not FDQuery1.Eof do
begin
inc(RecordNumber); //point to next record element in the array
for FieldNumber := FirstFieldIndex to TheFieldCount -1 do //load all the fields of this record
begin
Data := FDQuery1.Fields[FieldNumber].asVariant; //get the data
DataArray[RecordNumber, FieldNumber] := Data; //put into array
end;
FDQuery1.next; //get next record
end; //while
end;
FDQuery1.Close;
end;
To get the data back again use
For RecordNumber := FirstRecordIndex to TheRecordCount -1 do
For FieldNumber := FirstFieldIndex to TheFieldCount -1 do
begin
Data := DataArray[RecordNumber, FieldNumber] ;
//do something with the data ie put into a memo
end;
You can use this, i hope that help u:
FDQuery1.SQL.Text := 'select * from `table` WHERE dcid=3;';
FDQuery1.Open;
if FDQuery1.Active and (FDQuery1.RecordCount > 0) then
begin
FDQuery1.First;
While not FDQuery1.Eof do
begin
memo1.Lines.Add( FDQuery1.FieldByName('value').AsString );
FDQuery1.next;
end;
end;
you can do this with array of array consept
for this first you must create your new type
type
myArray = array [1..10] of integer;
myData= array of myArray;
and then you must define your variable
var
Records: myData;
i:integer;
on,now we have aarray of array to save database records on it so
begin
FDQuery1.SQL.Text := 'select * from table WHERE type=3;';
FDQuery1.Open;
FDQuery1.First;
SetLength(Records ,FDQuery1.recordcount);//or maybe you need a do a select count(*) query first
i:=0;
While not FDQuery1.Eof do
begin
Records[i][1]:= FDQuery1.FieldByName('value1').AsString;
Records[i][2]:= FDQuery1.FieldByName('value2').AsString;
Records[i][3]:= FDQuery1.FieldByName('value3').AsString;
Records[i][4]:= FDQuery1.FieldByName('value4').AsString;
Records[i][5]:= FDQuery1.FieldByName('value5').AsString;
Records[i][6]:= FDQuery1.FieldByName('value6').AsString;
i:=i+1;
FDQuery1.next;
end;
FDQuery1.Close;
end;
now all of your selected database records are in your local variable
you can go over the row and make new queries with FDQuery1...
in my project i use mysql.pas to deal with MySql database , so i've this Table structure :
Table Name :CarsTbl
ID int 10
CarName varchar 100
Car_Img longblob 0
i use this table to save each Car with its name and its picture .
The save routine works very well .
But the problem is when getting the Car_Img in which i use the following procedure :
procedure GetCarImage(const MyCarName:String;TrgStream:TMemoryStream);
var
query_string: String;
rCount: Integer;
mySQL_Res: PMYSQL_RES;
LibH: PMYSQL;
Row: PMYSQL_ROW;
iLen:PMYSQL_LENGTHS;
begin
mySQL_Res := nil;
Try
Try
query_string := 'SELECT CarName,Car_Img FROM CarsTbl WHERE CarName="'+MyCarName+'"';
mysql_real_query(LibH, PAnsiChar(query_string), Length(query_string));
mySQL_Res := mysql_store_result(LibH);
Try
rCount := mysql_num_rows(mySQL_Res);
If rCount > 0 Then Begin
Repeat
Row := mysql_fetch_row(mySQL_Res);
iLen:= mysql_fetch_lengths(mySQL_Res);
If Row <> nil Then Begin
TrgStream.position :=0;
//Row^[1] ==== Car_Img Blob Field
TrgStream.WriteBuffer(Row^[1],iLen[1]);
End;
Until Row = nil;
end;
Finally
mysql_free_result(mySQL_Res);
mySQL_Res := nil;
Row := nil;
End;
Finally
mysql_close(LibH);
End;
Finally
LibH := nil;
End;
end;
I get the Car image but with Malformed file Header let me explain :
Audi_Car image is saved as a Png image , but when i Load its image i always get it like this :
So please how could correct this ? is there any error in my Sql query ?
P.S : i create TrgStream in an other place.
And here's my LoadCarImage Procedure :
Procedure LoadCarImage();
var
CarStrm:TMemoryStream;
begin
CarStrm:=TMemoryStream.Create;
Try
GetCarImage('Audi',CarStrm);
CarStrm.SaveToFile('audi.png');
finally
CarStrm.Free;
end;
end;
many thanks
you should not use
Repeat ... until
CarName is not unique. So if you have more than one Audi in your table you create also a not valid image file.
Try it with
Delphi5
EDIT:
With
TrgStream.WriteBuffer(Row^[1],iLen[1]);
WriteBuffer takes the first pointer and writing the content of the whole Row.
In your .png file now you have
ID00CarName00Car_Img
9Wî00fî00Audi00‰PNG.......
I prefer the long way via the array only to see what I get.
Now that you have tested it. we have found no error in the writing of the present stream.
we use instead of
TrgStream.WriteBuffer(Row^[1],iLen[1]);
this
TrgStream.WriteBuffer(Row^[1]^,iLen[1]);
procedure GetCarImage(const MyCarName:String;TrgStream:TMemoryStream);
var
[...]
begin
[...]
query_string := 'SELECT CarName,Car_Img FROM cars WHERE CarName="Audi"';
mysql_real_query(LibH, PAnsiChar(query_string), Length(query_string));
mySQL_Res := mysql_store_result(LibH);
Try
rCount := mysql_num_rows(mySQL_Res);
If rCount > 0 Then Begin
Row := mysql_fetch_row(mySQL_Res);
iLen:= mysql_fetch_lengths(mySQL_Res);
If Row <> nil Then Begin
TrgStream.position :=0;
//Row^[1] ==== Car_Img Blob Field
TrgStream.WriteBuffer(Row^[1]^,iLen[1]);
[...]
I had the allmost same problem.
I wanted read emf (picture metafile) from mysql glob field.
And I solved it like this.
var
text_t1:string;
Stream: TStream;
begin
text_t1:='';
text_t1:=' select emf_glob from table ';
ADOQuery1.Close;
ADOQuery1.SQL.Clear;
ADOQuery1.SQL.Add(text_t1);
ADOQuery1.Open;
ADOQuery1.first;
Stream:= TMemoryStream.Create;
Stream := ADOQuery1.CreateBlobStream(ADOQuery1.FieldByName('emf_glob'), bmRead);
Stream.Position:=0;
DBImage1.Picture.LoadFromStream(Stream);
Stream.Free;
I've only just started learning Pascal so please excuse my ineptitude if I've missed something glaringly obvious.
I have a program that connects to a DB, retrieves a list of accounts and displays them in a StringGrid. I am now trying to extend the program so that selecting a row in the grid will perform a further search using parameters returned from the grid.
I have the grid setup correctly (I think!), I've managed to write the accountID value into a variable and am using it to construct a query.
When the time comes to run the query a segfault is issued and I don't understand why, the error message is
Project SQLConnect raised exception class 'External: SIGSEGV' at address 583166
The Assembler window shows the following gobbledygook:
00583166 8b09 mov (%ecx),%ecx
Here are the procedures I'm using to return data
Initial return before selecting row, this works - on its own:
procedure TForm1.sendQueryClick(Sender: TObject);
begin
CreateConnection;
CreateTransaction;
try
Query := GetQuery;
Query.SQL.Text := 'SELECT * FROM tbl_accounts LEFT JOIN tbl_properties ON tbl_accounts.ClientID = tbl_properties.PropertyID LEFT JOIN tbl_clients ON tbl_accounts.ClientID = tbl_clients.ClientID ORDER BY tbl_accounts.AccountID DESC';
AConnection.Open;
Query.Open;
while not Query.Eof do
begin
accID := Query.FieldByName('AccountID').AsString;
accountNo := Query.FieldByName('AccountNumber').AsString;
mortgagors := Query.FieldByName('Mortgagors').AsString;
address := Query.FieldByName('Address').AsString;
accResults.RowCount := accResults.RowCount + 1;
accResults.Cells[0, accResults.RowCount - 1] := accID;
accResults.Cells[1, accResults.RowCount - 1] := accountNo;
accResults.Cells[2, accResults.RowCount - 1] := mortgagors;
accResults.Cells[3, accResults.RowCount - 1] := address;
Query.Next;
end;
finally
Query.Close;
AConnection.Close;
Query.Free;
ATransaction.Free;
AConnection.Free;
end;
end;
This is the row select query, which causes the segfault:
procedure TForm1.accResultsSelectCell(Sender: TObject; aCol, aRow: Integer; var CanSelect: Boolean);
begin
// Hide Account ID Column
accResults.ColWidths[0] := 0;
CurrCol := 0;
CurrRow := aRow;
// Grab Account ID value from cell
CellValue := accResults.Cells[CurrCol, CurrRow];
selectedRow.Text := CellValue;
// Setup Query
try
TestQuery := GetQuery;
TestQuery.SQL.Text := 'SELECT * FROM tbl_accounts LEFT JOIN tbl_properties ON tbl_accounts.ClientID = tbl_properties.PropertyID LEFT JOIN tbl_clients ON tbl_accounts.ClientID = tbl_clients.ClientID WHERE AccountID = :AccID';
TestQuery.Params.ParamByName('AccID').AsString := CellValue;
// Open Database connection
AConnection.Open;
TestQuery.Open;
begin
end;
finally
TestQuery.Close;
AConnection.Close;
TestQuery.Free;
ATransaction.Free;
AConnection.Free;
end;
end;
IIRC: Make sure that the type of the parameter is not ftunknown.
I have a user registration process which stores user info to my database using MYDAC components. Currently it allow duplicate users, which is not my intention. My code is below, but I don't know where the problem is.
procedure TForm1.Button1Click(Sender: TObject);
begin
if (edit1.Text <> '') and (edit2.Text <> '') and (edit3.Text <> '') and
(edit4.Text <> '') then
begin
MyQuery1.Close;
MyQuery1.SQL.Text := 'select * from uyeler '+
'where nick=:0 and mail=:0 and site=:0';
MyQuery1.Params[0].AsString:=edit1.text;
MyQuery1.Params[0].AsString:=edit2.text;
MyQuery1.Params[0].AsString:=edit3.text;
MyQuery1.open;
if MyQuery1.RecordCount = 0 then
MessageDlg('The same information! Try again.', mtError, [mbOK], 0)
else
MyQuery1.Close;
MyQuery1.SQL.Text := 'INSERT INTO uyeler (nick, mail, site, sifre) VALUES '+
'(:nick, :mail, :site, :sifre)';
MyQuery1.ParamByName('nick').AsString := Edit1.text;
MyQuery1.ParamByName('mail').AsString := Edit2.text;
MyQuery1.ParamByName('site').AsString := Edit3.text;
MyQuery1.ParamByName('sifre').AsString := Edit4.text;
MyQuery1.Execute;
Button1.Enabled := False;
MessageDlg('Mission complate!', mtInformation, [mbOK], 0);
Edit1.Clear;
Edit2.Clear;
Edit3.clear;
Edit4.Clear;
PageControl2.Visible := False;
PageControl1.Visible := True;
end
else
begin
MessageDlg('Information is missing! Try again.', mtWarning,[mbOK],0);
end;
end;
How can I prevent signing up with the same? What should I do in this case?
I would typically use unique indexes on the underlying MySQL table to enforce this.
You're checking the wrong result. You need to change your test to
if MyQuery1.RecordCount > 0 then // At least one match found already
Better yet, if MyDac supports it, is to use
if not MyQuery1.IsEmpty then // row already exists.
Actually, you have more issues than that, though. You have a mismatched begin and end block, because right now you're always running the insert part of the method. As #TLama says, you're also using the same pameter multiple times, assigning nick, mail, and site the all the same value. Use named parameters instead (shown below in both the SQL and the parameter assignments).
procedure TForm1.Button1Click(Sender: TObject);
var
UserExists: Boolean;
begin
Button1.Enabled:=false;
if (edit1.Text <> '') and (edit2.Text <> '') and
(edit3.Text <> '') and (edit4.Text <> '') then
begin
MyQuery1.Close;
MyQuery1.SQL.Text :=' select* from uyeler '+
'where nick=:nick and mail=:mail and site=:site';
MyQuery1.ParamByName('nick').AsString:=edit1.text;
MyQuery1.ParamByName('mail').AsString:=edit2.text;
MyQuery1.ParamByName('site').AsString:=edit3.text;
MyQuery1.open;
try
UserExists := not MyQuery1.IsEmpty;
finally
MyQuery1.Close;
end;
if UserExists then
MessageDlg('The same information! Try again.', mtError,[mbOK],0)
else
begin // <<--- Added begin
MyQuery1.SQL.Text :=' INSERT INTO uyeler (nick, mail, site, sifre) VALUES '+
'(:nick, :mail, :site, :sifre)';
MyQuery1.ParamByName('nick').AsString := Edit1.text;
MyQuery1.ParamByName('mail').AsString := Edit2.text;
MyQuery1.ParamByName('site').AsString := Edit3.text;
MyQuery1.ParamByName('sifre').AsString := Edit4.text;
try
MyQuery1.Execute;
finally
MyQuery1.Close;
end;
end; // <------------ Moved end from below where marked
MessageDlg('Mission complate!', mtInformation,[mbOK],0);
Edit1.Clear;
Edit2.Clear;
Edit3.clear;
Edit4.Clear;
PageControl2.Visible:=false;
PageControl1.Visible:=true;
end // <------------- removed extra end caused by addition above
else
MessageDlg('Information is missing! Try again.', mtWarning,[mbOK],0);
end;
IMO the answer posted by #Ken White should work fine, but since you are finding troubles. I suggest you to try with this code. Its just the difference in the executing the queries.
I am considering the field datatypes to be Char or VarChar, hence " " while entering the data values
procedure TForm1.Button1Click(Sender: TObject);
begin
Button1.Enabled:=false;
if (edit1.Text <> '') and (edit2.Text <> '') and
(edit3.Text <> '') and (edit4.Text <> '') then
begin
MyQuery1.SQL.Clear;
MyQuery1.SQL.Add(' select* from uyeler where nick="'+edit1.text+'"' +
'and mail="'+edit2.text+'" and site="'+edit3.text+'"');
MyQuery1.Execute;
if not MyQuery1.IsEmpty then //--- can also use MyQuery1.RecordCount >0
MessageDlg('The same information! Try again.', mtError,[mbOK],0)
else
begin //--- no duplicates present
MyQuery1.SQL.Clear;
MyQuery1.SQL.Add(' INSERT INTO uyeler (nick, mail, site, sifre) VALUES '+
'("'+edit1.text+'", "'+edit2.text+'","'+edit3.text+'", "'+edit4.text+'")');
try
MyQuery1.Execute;
finally
MyQuery1.SQL.Clear;
end;
MessageDlg('Mission complate!', mtInformation,[mbOK],0);
Edit1.Clear;
Edit2.Clear;
Edit3.clear;
Edit4.Clear;
PageControl2.Visible:=false;
PageControl1.Visible:=true;
end;
end;
else
MessageDlg('Information is missing! Try again.', mtWarning,[mbOK],0);
end;
I am working with Devart's MyDac and MySQL Server 5.0.41. Here is a section from the documentation on executing stored procedures with TMyConnection.ExecProc:
Note: Stored functions unlike stored procedures return result values that are obtained internally through the RESULT parameter. You will no longer have to provide anonymous value in the Params array to describe the result of the function. The stored function result is obtained from the Params[0] indexed property or with the ParamByName('RESULT') method call.
They also give an example on how to execute a stored function:
aStringVariable1 := TMyConnection.ExecProc('StoredFunctionName',['Param1','Param2']);
aStringVariable2 := TMyConnection.ParamByName('Result').AsString;
By Following these examples, my execution of the stored functions are returning Param1 in the variable aStringVariable2.The execution of the functions in the Query Browser returns the right results. Any pointers on the right way to execute stored functions in MyDAC with TMyConnection or TMyStoredProc will be appreciated.
Thanks in advance.
Here is the code we use to call stored procedures - hope it helps
function TDbControl.DatabaseStoredProc(FConnectionsAddr: integer; SpName: string;var Params: TDAParams): boolean;
var
MyStoredProc: TMyStoredProc;
PramsTxt: String;
Idx, Idx2: Integer;
begin
result := False;
MyStoredProc := nil;
try
try
MyStoredProc := TMyStoredProc.Create(nil);
MyStoredProc.Connection := TMyConnection(FConnectionsAddr);
MyStoredProc.StoredProcName := SpName;
MyStoredProc.ParamCheck := False;
if assigned(Params) then
begin
for Idx := 0 to Params.Count - 1 do
begin
MyStoredProc.ParamByName(Params[Idx].Name).DataType := Params[Idx].DataType;
MyStoredProc.ParamByName(Params[Idx].Name).Value := Params[Idx].Value;
end;
end;
MyStoredProc.Execute;
if assigned(Params) then
begin
for Idx := 0 to Params.Count - 1 do
begin
if (Params[Idx].ParamType = ptOutput ) then
Params[Idx].Value := MyStoredProc.ParamByName(Params[Idx].Name).Value;
end;
end;
result := True;
except
on E: Exception do
begin
PramsTxt := '';
if assigned(Params) then
begin
for Idx2 := 0 to Params.Count - 1 do
begin
PramsTxt := PramsTxt + Params.Items[Idx2].Name + '=' + Params[Idx2].AsString + ',';
end;
end;
LogText(FConnectionsAddr, 'DatabaseStoredProc Err:' + E.Message + ' SpName:' + SpName + ' Prams:' + PramsTxt);
raise ;
end;
end;
finally
FreeAndNil(MyStoredProc);
end;
end;