delphi mysql query result - mysql

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...

Related

Insert the records of a dbf file into a table in MS Access database using Delphi

I am trying to insert the records of a dbf file into a table in a MS Access database that I have already created.
The dbf file's name is tab1.dbf and it has three columns: cl1, cl2, cl3.
The MS Access database name is db1 and it has one table tb2 with three columns: cl1, cl2, cl3.
I have connected Delphi to the MS Access database using ADOConnection1.
To insert the dbf file's records, I have to click in a Button1 with OpenDialog1
The code I use is this :
procedure TForm1.Button1Click(Sender: TObject);
var importdir,ipo : string;
begin
if form1.OpenDialog1.Execute then
begin
importdir:= extractfiledir(form1.OpenDialog1.FileName);
ipo:= form1.OpenDialog1.FileName ;
end;
form1.Edit1.Text:= importdir;
ADOConnection1.Execute('insert into tab2 SELECT * FROM [ database = '+ipo+' ].tab1' );
end;
but when i execut the form1 i have this error message :
name of the file is incorrect
can you help me guys ?
Here after is one simple solution. It simple because it assume the Access database structure is the same as the dBASE structure. You'll get started with this example that you'll adapt to your needs.
procedure TDbfToAccessWithAdoForm.DbfToAccessButtonClick(Sender: TObject);
var
Fld : Integer;
FldValue : Variant;
InsertSQL : String;
begin
ADOConnectionAccess.Connected := TRUE;
ADOConnectionDbf.Connected := TRUE;
ADOQueryDbf.SQL.Text := 'Select * from Clients';
ADOQueryDbf.Open;
// Build the parametrized INSERT statement
InsertSQL := 'insert into Clients(';
for Fld := 0 to ADOQueryDbf.FieldCount - 1 do
InsertSQL := InsertSQL + ADOQueryDbf.Fields[Fld].FieldName + ',';
// Remove extra coma
Delete(InsertSQL, Length(InsertSQL), 1);
InsertSQL := InsertSQL + ') values (';
for Fld := 0 to ADOQueryDbf.FieldCount - 1 do
InsertSQL := InsertSQL + ':' + ADOQueryDbf.Fields[Fld].FieldName + ',';
// Remove extra coma
Delete(InsertSQL, Length(InsertSQL), 1);
InsertSQL := InsertSQL + ')';
while not ADOQueryDbf.Eof do begin
ADOQueryAccess.SQL.Text := InsertSQL;
for Fld := 0 to ADOQueryDbf.FieldCount - 1 do begin
FldValue := ADOQueryDbf.Fields[Fld].Value;
// Here you can do whatever conversion is required
if FldValue = Null then begin
if ADOQueryDbf.FieldDefList[Fld].DataType = ftDateTime then
FldValue := 0 // My Access table doesn't like empty datetime
else
FldValue := ' '; // My Access table doesn't like empty string
end;
ADOQueryAccess.Parameters.ParamByName(ADOQueryDbf.Fields[Fld].FieldName).Value := FldValue;
end;
ADOQueryAccess.ExecSQL;
ADOQueryDbf.Next;
end;
ADOQueryDbf.Close;
ADOQueryAccess.Close;
end;
You should add error checking and try/finally or try/except. I let you do it as you do usually.

How to get select results using zeoslib

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;

Conditional Statement Not Working With Records

How do i display all customers who have purchased an item on a certain day, my code doesn't seem to work, ive tried implementing the code within the displayByDayPurchased procedure. Sorry if this is a simple question, i'm still new to programming.
type
Tday = (monday, tuesday);
Tcustomer = record
itemPurchased:string;
dayPurchased: Tday;
end;
Tcustomers = array of Tcustomer;
function readDay(prompt:string): Tday;
var
selection:Integer;
begin
writeln('1. Monday');
writeln('2. Tuesday');
selection := ReadIntegerRange('Select day purcased (1 - 3): ', 1, 3);
result := Tday(selection-1);
end;
function readCustomers(prompt:string):TCustomers;
var
numOfCustomers:integer;
i:integer;
begin
numOfCustomers := ReadInteger('Enter number of customers: ');
setLength(result, numOfCustomers);
for i := 0 to high(result) do
begin
writeln(i);
result[i].itemPurchased := ReadString('Item Purchased: ');
result[i].dayPurchased := readDay(prompt);
end;
end;
procedure displayByDayPurchased(customers:TCustomers);
var
specific_day:integer;
begin
specific_day := ReadInteger('Enter day to see items purchased');
if (specific_day = customers.dayPurchased[specific_day])then
begin
end;
end;
procedure Main();
var
customer: Tcustomers;
begin
customer := readCustomers('Read in customers');
end;
begin
Main();
end.
my code doesn't seem to work, ive tried implementing the code within the displayByDayPurchased procedure.
Well, in the code you've posted, your displayByDayPurchased doesn't actually implement anything which would result in matching records being displayed, all you have is an empty begin ... end block:
if (specific_day = customers.dayPurchased[specific_day])then
begin
end;
and a) that doesn't correctly specify the match condition anyway and b) it will not compile because a Tcustomers array does not have a dayPurchased field ( a Tcustomer record does, but not the array).
Your code depends of quite a lot of things whose definitions you have not provide (ReadString, ReadInteger, ReadIntegerRange, etc) so it is difficult to give you a tested solution.
However an implementation of your displayByDayPurchased should probably look something like this:
procedure displayByDayPurchased(customers:TCustomers);
var
specific_day:integer;
i : integer;
ADay : Tday;
begin
specific_day := ReadInteger('Enter day to see items purchased');
ADay := Tday(specific_day); // converts integer to TDay value
for i := Low(customers) to High(Customers) do begin
if customers[i].dayPurchased = ADay then begin
writenln(customers[i].itemPurchased);
end;
end;
end;
I assume your Tcustomer record actually includes the customer's name, andx your code needs modifying to handle that.
Btw, your function readDay(prompt:string): Tday is wrong; Because of your definition of Tday, the allowed values in readDay should be 0 and 1, because the lowest value of the Tday enumeration actually corresponds to zero, not 1.
Also, you did not say which Pascal compiler you are using, but most modern versions allow a call like Tday(integerValue) to convert an integer to an instance value of the enumeration.

Delphi Load Blob from MySql

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;

Lazarus Pascal parameterized query causing segfault! Why?

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.