Data from ListBox to DataSet - delphi

I have DBGrid where I load names from DataSetPeople associated to table_people. I create a Drag and Drop procedure, where the user can drag a name and drop into a ListBox. The user can drop into a listbox_employees or listbox_manager.
MY PROBLEM
I want to create a relationship between people from table_people using the names that user drop in both listbox. I create table_relationship where employeeID match with managerID.
So, how can I get the name from each listbox, associate this name to an ID, and put this ID into a employeeID or managerID from DataSetRelationship associated to table_relationship ?

If your IDs are numeric (integer) values, you can use the TListBox.Items.Objects to store it when you fill it (in your drop event handler on the form):
var
PersonName: string;
PersonID: Integer;
begin
with YourDataModule do // Gives access to the tables in the data module
begin
PersonName := table_people.FieldByName('employeeName').AsString;
PersonID := table_people.FieldByName('employeeID').AsInteger);
end;
listbox_employees.Items.AddObject(PersonName, TObject(PersonID);
end;
For the manager, just change to the listbox for the manager and the field values for the manager.
To get the ID back out to use for an INSERT statement:
// Make sure both listboxes have the same number of items, of course.
var
EmployeeID: Integer;
ManagerID: Integer;
i: Integer;
begin
for i := 0 to ListBox_Employees.Items.Count - 1 do
begin
EmployeeID := Integer(ListBox_Employees.Items.Objects[i]);
ManagerID := Integer(ListBox_Manager.Items.Objects[i]);
if not YourDataModule.MakeRelationship(EmployeeID, ManagerID) then
ShowMessage('Unable to relate this employee and manager!');
end;
end;
// If you're using a SQL query, the `INSERT` should be created like this
// somewhere, like in your datamodule's OnCreate event
// qryRelationShips.SQL.Clear;
// qryRelationShips.SQL.Add('INSERT INTO table_relationship (employeeID, managerID)');
// qryRelationShips.SQL.Add('VALUES (:employeeID, :managerID)';
//
// Or you can type it into the SQL property in the Object Inspector at designtime
// INSERT INTO table_relationship (employeeID, managerID) VALUES (:employeeID, :managerID)
function TYourDataModule.MakeRelationship(const EmpID, MgrID: Integer): Boolean;
begin
Result := False;
// INSERT into your dataset:
qryRelationShips.ParamByName('employeeID').AsInteger := EmpID;
qryRelationShips.ParamByName('managerID').AsInteger := MgrID;
try
qryRelationShips.ExecSQL;
Result := qryRelationShips.RowsAffected;
finally
qryRelationShips.Close;
end;
end;
// If you're using a table instead of a query, getting a result is harder
function TYourDataModule.MakeRelationship(const EmpID, MgrID: Integer): Boolean;
begin
Result := True;
try
tblRelationShips.Insert;
tblRelationShips.FieldByName('employeeID').AsInteger := EmpID;
tblRelationShips.FieldByName('managerID').AsInteger := MgrID;
tblRelationShips.Post;
except
Result := False;
end;
end;

I hope you have made tables using SQLite database. So what you want to do is possible by using a query to insert the rows. Use db.execSQL();
You can keep a counter for incrementong the id values.

Related

ADOQuery Get Result (Delphi)

I Tried to get result from ADOQuery in Delphi. I wrote this function for Get a Name from table according custom ID.
function GetNameByID(Id : Integer) : string;
var query : string;
Begin
ShowMessage(GetDBGridViewIndex().ToString);
query := 'SELECT Name FROM Table1 WHERE ID=' + IntToStr(Id);
With ADOQuery do
Begin
try
SQL.Clear;
SQL.Add(query);
Open;
First;
Result:= // Need Get Result;
finally
Close;
end;
End;
ShowMessage(result);
End;
But I don't know how can return Result from ADOQuery.
TADOQuery is a descendant of TDataset.
You can iterate through the result records with the First, Next, Prior, and Last methods and find out if you've reached the end with Eof.
Within each result record you can access the fields with:
Fields[Index].AsString
Fields[Index].AsInteger
...
or
FieldByName(FieldName).AsString
FieldByName(FieldName).AsInteger
...
In this case you can access to the result using:
Result := Fields[0].AsString;
Result := FieldByName('Name').AsString;
After you Open the query, the cursor is pointing the First record. You don't need to call the First method after Open.

generate a number increases with a specific format ex 'PRT-00000'

I want to create auto numbering to my access database in delphi
example :
I have a database with part names , i want to create an auto id that counts number of these records and generates a name with number as this 'PRT-00000' and increase it with one each time i add a record and keeps this format of five digits , like this 'PRT-00001'
help me please and thanks a lot .
sorry for my poor english
Let's assume your Access table is named 'Parts' and has an AutoNumber
field named 'ID' and a Short Text field named 'PartNumber'. One way of generating the
PartNumber value would be to get Access to calculate it for you, but since you have asked about Delphi, I'm going to explain a way to do it in Delphi.
Please start a new, very simple project with just the following items on the main form:
A TAdoConnection configured to connect to your database;
A TAdoQuery configured to use the TAdoConnection with its SQL.Text property set
to 'select * from Parts'
A TDataSource and TDBGrid configured to display the contents of the TAdoQuery.
A TButton
Then, add the following code to the form's unit:
procedure TForm2.Button1Click(Sender: TObject);
begin
NewPart;
end;
procedure TForm2.NewPart;
const
sSelect = 'select * from Parts';
sPrefix = 'PRT-';
iDigits = 5;
var
PartNumber : String;
ID : Integer;
begin
qryParts.Insert;
try
// First, set the new record's PartNumber field to a temporary value
qryParts.FieldByName('PartNumber').AsString := 'xxxx';
// save the record so that we can then read the ID value Access has allocated to the record
qryParts.Post;
// read the ID value
ID := qryParts.FieldByName('ID').AsInteger;
// next, construct the desired value for the PartNumber field based on the ID
PartNumber := qryParts.FieldByName('ID').AsString;
// left-pad the PartNumber with zeroes
while Length(PartNumber) < iDigits do
PartNumber := '0' + PartNumber;
// pre-pend the PRT- prefix
PartNumber := sPrefix + PartNumber;
// put qryParts into its dsEdit state
qryParts.Edit;
qryParts.FieldByName('PartNumber').AsString := PartNumber;
finally
// post the record back to the Parts table
qryParts.Post;
end;
end;
procedure TForm2.FormCreate(Sender: TObject);
begin
qryParts.Open;
end;
Update I've managed to get the new Part's autonumber ID in advance of the new Part being posted to the db. To use this, add the NewAutoNumber function and modify the NewPart method as shown below.
function TForm2.NewAutoNumber(ATable, AColumn: String): Integer;
var
vCat : OleVariant;
vTable : OleVariant;
vColumn : OleVariant;
begin
vCat := CreateOleObject('ADOX.Catalog');
vCat.ActiveConnection := AdoConnection1.ConnectionString;
vTable := vCat.Tables[ATable];
vColumn := vTable.Columns[AColumn];
Result := vColumn.Properties['Seed'].Value;
end;
procedure TForm2.NewPart;
const
sSelect = 'select * from Parts';
sPrefix = 'PRT-';
iDigits = 5;
var
PrvSql : String;
PartNumber : String;
ID : Integer;
begin
ID := NewAutoNumber('Parts', 'ID');
try
qryParts.Insert;
qryParts.FieldByName('PartNumber').AsString := 'xxxx';
qryParts.Post;
if not qryParts.Locate('ID', ID, []) then begin
raise exception.CreateFmt('Failed to create new Parts record with ID = %d', [ID]);
end;
PartNumber := qryParts.FieldByName('ID').AsString;
while Length(PartNumber) < iDigits do
PartNumber := '0' + PartNumber;
PartNumber := sPrefix + PartNumber;
qryParts.Edit;
qryParts.FieldByName('PartNumber').AsString := PartNumber;
finally
qryParts.Post;
end;
end;
Update #2 As an alternative to getting the ID value for a newly-added Parts record using
either of the methods above, it can be obtained by using the 'select ##identity' method. The simplest
way to do this is to add another TAdoQuery, qryAutoNumber to the form and to add this function to
get the AutoNumber value:
function TForm2.NewAutoNumberFromIdentity : Integer;
begin
if qryAutoNumber.Active then
qryAutoNumber.Close;
qryAutoNumber.SQL.Text := 'select ##identity';
qryAutoNumber.Open;
Result := qryAutoNumber.Fields[0].AsInteger;
end;
Note that to obtain the correct ID value, this function should be called immediately after calling qryParts.Post. However, I have included this
only for completeness but as far as I can see, it is largely pointless
because once the new Parts record has been posted, the ID AutoNumber value
can be read directly from the ID field of qryParts.

Distinct Count in fastreport

Does anyone know how to make a distinct count in fastreport?
Example
I have the report:
Name sex
João m
João m
Maria f
In the normal count, the result would be 3, but I want one that takes only the number of rows that do not repeat the field name .
In this case, the result would be 2.
Can anyone help me? That's just an example.
I can not do a group by in SQL because I have several fields.
I'm not skilled in using FastReport but I've found this page on the FastReport's official forum.
I think you can change the example by adapting it to your scenario (Note that the syntax could require some adjustments).
Bands:
GroupHeader1 <Sex>
MasterData1 [Name, Sex, ...]
GroupFooter1 [GetDistinctCount]
Script (Only working with dataset sorted by the field to count):
var
LastValue : string;
DistinctCount : integer;
//create this event by double-clicking the event from the Object Inspector
procedure OnGroupHeader1.OnBeforePrint;
begin
if LastValue <> (<Datasetname."Sex">) then
Inc(DinstinctCount);
LastValue := <Datasetname."Sex">
end;
function GetDistinctCount: string;
begin
Result := IntToStr(DistinctCount);
end;
The base idea is that the DistinctCount variable is incremented each time the field value changes.
Script (Should works also with unsorted dataset):
var
FoundValues : array of string;
(* !!IMPORTANT!!
You need to initialize FoundValues array before to start counting: *)
SetLength(FoundValues, 0);
function IndexOf(AArray : array of string; const AValue : string) : integer;
begin
Result := 0;
while(Result < Length(AArray)) do
begin
if(AArray[Result] = AValue) then
Exit;
Inc(Result);
end;
Result := -1;
end;
//create this event by double-clicking the event from the Object Inspector
procedure OnGroupHeader1.OnBeforePrint;
begin
if(IndexOf(FoundValues, <Datasetname."Sex">) = -1) then
begin
SetLength(FoundValues, Length(FoundValues) + 1);
FoundValues[Length(FoundValues) - 1] := <Datasetname."Sex">;
end;
end;
function GetDistinctCount: string;
begin
Result := IntToStr(Length(FoundValues));
end;
The base idea is that each different value found is added to the FoundValues array.
You can do it in Firebird without GROUP BY as:
DECLARE #T TABLE (ID INT IDENTITY (1,1), Name NVARCHAR(25) , Sex CHAR(1));
INSERT INTO #T VALUES
('Sami','M'),
('Sami','M'),
('Maria','F');
SELECT DISTINCT Name , Sex FROM #T
You can also create a View , and then use it in your report.
If you really need to do that in FastReport, you have to use a GroupHeader and a GroupFooter to that.
How ?
You have to write your script in OnBeforePrint event.
procedure OnGroupHeader1.OnBeforePrint;
Create this one by double-clicking in the event in the object inspector.

How can I access a TDBGrid column by name instead of by Index?

How can I access a TDBGrid column by name instead of Index?
For example, now I use:
grdInvoiceItems.Columns[2].Visible := False;
but it would be much better to write something like:
grdInvoiceItems.Columns['UnitPrice'].Visible := False;
In the mean time I use a for cycle like in:
for idx := 0 to grdInvoiceItems.Columns.Count - 1 do
begin
if (
(grdInvoiceItems.Columns[idx].FieldName = 'UnitPrice') or
(grdInvoiceItems.Columns[idx].FieldName = 'Discount') or
(grdInvoiceItems.Columns[idx].FieldName = 'SecretCode')
) then
grdInvoiceItems.Columns[idx].Visible := False;
end;
Using colum name is IMO much better tham column index since index is subject to change more often than name.
Any idea on how to encapsulate it better?
You could try something like this:
function ColumnByName(Grid : TDBGrid; const AName : String) : TColumn;
var
i : Integer;
begin
Result := Nil;
for i := 0 to Grid.Columns.Count - 1 do begin
if (Grid.Columns[i].Field <> Nil) and (CompareText(Grid.Columns[i].FieldName, AName) = 0) then begin
Result := Grid.Columns[i];
exit;
end;
end;
end;
Of course, if you are using a version of Delphi which is recent enough to support Class Helpers, you could wrap this function into a Class Helper for TDBGrid, like this
type
TGridHelper = class helper for TDBGrid
function ColumnByName(const AName : String) : TColumn;
end;
[...]
function TGridHelper.ColumnByName(const AName: String): TColumn;
var
i : Integer;
begin
Result := Nil;
for i := 0 to Columns.Count - 1 do begin
if (Columns[i].Field <> Nil) and (CompareText(Columns[i].FieldName, AName) = 0) then begin
Result := Columns[i];
exit;
end;
end;
end;
Then, you could do this
Col := DBGrid1.ColumnByName('SomeName');
Obviously, you could write a similar function which searches by the column's title, rather than the associated Field's FieldName.
You could create a mapping between column name and grid index e.g. as a dictionary and use that. Note that not every column in a dataset is necessarily visible in a dbgrid. In addition there might be calculated fields in the dataset, so don't forget these. The safest way to create the mapping would be to iterate trough the columns of the dbgrid and store their field names together with the column index. This way you won't get any invalid entries and any field that's not in the mapping does not have a dbgrid column.
I wanted to to the similar thing, but i was thinking if the dbgrid doesn't know the colmuns by name (for this case), maybe one else (speaking of components) does know it allready.
In my case i use a fdquery -> Datasource -> DBgrid connection.
The FDQuery knows the fields by name and by id.
So considering you use similar components you can do
dbgrid1.Columns[fdquery1.FieldByName('UnitPrice').Index].visible:=false;

How to Get "_id" field value from kinvey.com

I been using kinvey.com, and every time I try to get Manga._id it returns null. Can you help me figure out why?
TManga = class
strict private
FSite,
FManga,
FID: String;
published
property Site : string read FSite write FSite;
property Manga : string read FManga write FManga;
property _id : string read FID write FID;
///////////////////////////////////////////////////////////////////////////
var
Mangas: TBackendObjectList<TManga>;
Manga : TManga;
QueryStr: TArray<string>;
i: Integer;
begin
with xQuery do
begin
Execute;
Mangas := TBackendObjectList<TManga>.Create;
QueryStr := TArray<string>.Create('');
xStorage.Storage.QueryObjects<TManga>('xxxx' ,QueryStr ,Mangas);
with xListBox do
begin
Items.BeginUpdate;
try
Items.Clear;
for I := 0 to Mangas.Count -1 do
begin
Manga := Mangas.Items[I];
items.add(Manga.Site + ' - ' + Manga._id) // Manga._id this is everytime null
end;
finally
Items.EndUpdate;
end;
end;
end;
http://i.hizliresim.com/M94QPN.png
Did you try using the value 'fields=_id' in one of the array elements of QueryStr?
Your column _id is always null because the Kinvey API doesn't get the _id column as a simple column but as the object ID of the record.
In order to get the object ID of your Manga record, you must to add a variable like this:
oEntity: TBackendEntityValue;
So right under this line in the "for" statement:
Manga := Mangas.Items[I];
You may add these two new lines:
oEntity := FBackendList.EntityValues[Manga]; // Gets the Kinvey object
Manga._id := oEntity.ObjectID; // Sets the _id property of the current TManga instance
One important thing you may take in mind is when you will add new records in your Kinvey collection. You don't have to write in the _id property of your new TManga item before you create your new record. However you will need to get it from Kinvey right after you insert the new record.
This code was adapted from the Embarcadero's ToDo sample:
procedure TDataModule1.AddBackendItem(const AItem: TManga);
var
oEntity: TBackendEntityValue;
begin
// After the execution of this command the new record will be inserted in Kinvey, and the variable oEntity will get the respective object ID
BackendStorage1.Storage.CreateObject<TManga>(
TMangaNames.BackendClassname, AItem, oEntity);
AItem._id := oEntity.ObjectID; // Updates the property _id of the current instance of TManga
FBackendList.Add(AItem, oEntity);
end;
I hope it can help you!

Resources