Get current cell value in DBGrid - oracle

In my Delphi application, I use lookup fields, but in unusual way. Actually, I wanna update field in underlying data set, just like if it was in the same table.
Existing guides tell that there is no problem, just join the table and voila... I envy if they really succeeded this task with such simple solution. I do not. BTW I think I'm getting close to reach my goal. I have one question left: how the hell I can get value I just entered into DBGrid Cell?
I tried DBGrid[FieldName].EditValue and .DisplayText, but they show the same value as Field.Value, which doesn't change after exiting the column, because it is lookup field. Sender.NewValue is null. I'm using this function to update lookup table:
procedure TKDGridForm.LookupFieldChange(Sender: TField);
begin
if not Assigned(Sender) then
Exit;
Sender.OnChange := nil;
if not Assigned(Sender.LookupDataSet) then
Exit;
if Sender.LookupDataSet.Locate(Sender.LookupKeyFields, Sender.DataSet[Sender.KeyFields], []) then
Sender.LookupDataSet.Edit
else
Sender.LookupDataSet.Append;
// how do I get the value I just entered?
Sender.Value := KDGrid3[Sender.FieldName].DisplayText;
Sender.LookupDataSet.FieldValues[Sender.LookupResultField] := Sender.Value;
Sender.LookupDataSet.Post;
Sender.OnChange := LookupFieldChange;
end;
Here is SQL I used before I ended up with lookup fields:
select det.*,
od1.T_EQ T_SHABLON_EQ,
od1.T_NV T_SHABLON_NV,
od1.T_PRIM T_SHABLON_PRIM,
od2.T_EQ T_PRAVKA_EQ,
od2.T_NV T_PRAVKA_NV,
od2.T_PRIM T_PRAVKA_PRIM,
od3.T_EQ T_VALCOV_EQ,
od3.T_NV T_VALCOV_NV,
od3.T_PRIM T_VALCOV_PRIM,
od4.T_EQ T_REZKA2_EQ,
od4.T_NV T_REZKA2_NV,
od4.T_PRIM T_REZKA2_PRIM
from CMKNEW.details det
left join CMKNEW.OperDetails od1
ON det.nrec = od1.cdetail
and 81 = od1.coper
left join CMKNEW.OperDetails od2
ON det.nrec = od2.cdetail
and 82 = od2.coper
left join CMKNEW.OperDetails od3
ON det.nrec = od3.cdetail
and 83 = od3.coper
left join CMKNEW.OperDetails od4
ON det.nrec = od4.cdetail
and 84 = od4.coper
where det.ckd=:CKD order by det.NREC
Hope it will explain my task clearer. If you wanna mcve, I can extend this, though I think it's not essential.
My database is Oracle, connected through ADO. I'd like the solution to be as simple as possible.

I assume you're talking about a standard TDBGrid and that what you're asking is how to get the text which is displayed in a cell of the grid when you type into it, but before the grid's dataset is updated. At that point, the current row indicator in the LH column will have changed from the default right-pointing triangle to an I-beam
If so, the snippet below shows you how to do get this text value. The point is, in the condition I've described, what's in the cell hasn't yet been posted back to the underlying dataset field. What happens is that when you start editing, an InplaceEditor (TCustomMaskEdit descendant) is dynamically created, and it's this which holds the text value which is being edited.
Add a TTimer and a TMemo to your form and then run the code below to see what I mean.
type
TMyGrid = Class(TDBGrid);
procedure TMyForm.Timer1Timer(Sender: TObject);
var
S : String;
Grid : TmyGrid;
begin
Grid := TmyGrid(DBGrid1);
if Grid.InplaceEditor <> Nil then
S := Grid.InplaceEditor.Text
else
S := IntToStr(Grid.Col) + ':' + IntToStr(Grid.Row);
Grid.Invalidate;
Memo1.Lines.Insert(0, S);
end;

Related

insert a new line of row into grid without recordsource

I've some problem to insert a new row line into record without insert into cursor and then append into recordsource.
That said i've five record already in existing grid list but i need to add a new line into next row under grid list.
Here is my code:
vc_tmpfile = alltrim(sys(3)) + ".dbf"
create table &vc_tmpfile (text c(254))
append from C:\tmp\aaa.out type sdf
dele all for len(ALLTRIM(text)) < 15
pack
with thisform.grid_list
Do while !EOF()
if alltrim(substr(text,1,4)) == "POPL"
.columns(2).text1.value = alltrim(substr(text,6,6))--->>It shows nothing after insert
endif
skip
enddo
endwith
Appreciate thankful to someone could help.
The VFP Grid control displays data from its RecordSource, and that one is always an "alias".
So the easy VFP way is simply to do an SQL Insert Into yourAlias ..., or use the xBase Append and Replace ... commands.
In your scenario, the Grid.RecordSource probably is the temp table you created, where your code would get improved if you'd use the Create Cursor ... command for creating the temporary table, and by avoiding the infamous & operator and using (name) expressions instead wherever you can
Looks like this is what you are trying to do:
thisform.grid_list.RecordSource = ''
vc_tmpfile = Sys(2015)
Create cursor (m.vc_tmpfile) (text c(254))
append from ('C:\tmp\aaa.out') type sdf for len(ALLTRIM(text)) >= 15
Update (m.vc_tmpFile) set text = alltrim(substr(text,6,6)) where text like "POPL%"
locate
thisform.grid_list.RecordSource = m.vc_tmpfile
How did the existing 5 records get into the grid with no recordsource?
You should be updating a record source instead of updating the grid controls...
Maybe take this approach...
1) Stick data into array or temp readwrite cursor
2) Set grid recordsource to the array or cursor
3) Refresh Grid

FastReport Master/Detail in Delphi XE2

I need some help creating a Master/Detail report in Fast Reports for Delphi XE2.
I have a simple form which accepts 2 dates and 2 times from a user. I then have 2 Oracle Datasets on the form with which to retrieve my data. When the user presses the print button, the program accepts the values from the user and sends the values to the first oracle dataset, which then in turn retrieves the first value, and then sends this value along with the user accepted values to the second dataset to print the detail pertaining to the value retrieved.
For each dataset I do have a corresponding frxDBDataset component which is then assigned to the frxReport1 component. With in the report, I have created a Master Band which is assigned to dataset1, and a Detail Band assigned to datset2. When I run my report, dataset 1 brings back all the records, but dataset 2 only brings back the records for the first value and duplicates it for every record in dataset1.
Below is the code I am trying to execute:
opr_operator_ods.Close;
opr_operator_ods.SetVariable('DATEFROM', opr_datefrom_dtp.Date);
opr_operator_ods.SetVariable('DATETO', opr_dateto_dtp.Date);
opr_operator_ods.SetVariable('TIMEFROM', opr_timefrom_dtp.Text);
opr_operator_ods.SetVariable('TIMETO', opr_timeto_dtp.Text);
opr_operator_ods.Open;
if opr_operator_ods.RecordCount > 0 then
begin
while not opr_operator_ods.Eof do
begin
opr_operatorcount_ods.Close;
opr_operatorcount_ods.SetVariable('DATEFROM', opr_datefrom_dtp.Date);
opr_operatorcount_ods.SetVariable('DATETO', opr_dateto_dtp.Date);
opr_operatorcount_ods.SetVariable('TIMEFROM', opr_timefrom_dtp.Text);
opr_operatorcount_ods.SetVariable('TIMETO', opr_timeto_dtp.Text);
opr_operatorcount_ods.SetVariable('OPERATOR',
opr_operator_ods.FieldByName('opr_code').AsString);
opr_operatorcount_ods.Open;
while not opr_operatorcount_ods.Eof do
begin
frxReport1.PrepareReport(false);
opr_operatorcount_ods.Next;
end;
frxReport1.PrepareReport(true);
opr_operator_ods.Next;
end;
DecodeDate(opr_datefrom_dtp.Date, tyear, tmonth, tday);
StartDate := '''' + IntToStr(tday) + '/' + IntToStr(tmonth) + '/' + IntToStr(tyear) + '''';
DecodeDate(opr_dateto_dtp.Date, tyear, tmonth, tday);
EndDate := '''' + IntToStr(tday) + '/' + IntToStr(tmonth) + '/' + IntToStr(tyear) + '''';
frxReport1.Variables['StartDate'] := StartDate;
frxReport1.Variables['EndDate'] := EndDate;
//frxReport1.PrepareReport(True);
frxReport1.ShowPreparedReport;
How do I get the second dataset to move on to the next record's values?
This report used to work perfectly in Delphi 2005 with RaveReports6, but there we used code based form development which was easier to manipulate with a 'writeln' and not visual like with Fast Reports.
When creating the preview FastReport does something like this code:
while not MasterBand.DataSet.Eof do
begin
...Do special FastReport's work :)
while not DetailBand.DataSet.eof do
begin
...Do special FastReport's work :)
DetailBand.DataSet.Next;
end;
MasterBand.DataSet.Next;
end;
In your code:
while not opr_operatorcount_ods.Eof do
begin
frxReport1.PrepareReport(false);
opr_operatorcount_ods.Next; <-- here opr_operatorcount_ods is in the last position from PrepareReport
end;
Data bands may be a master or detail type, but they only control the positioning of data of the output page
(order and number of times displayed).
Data displayed by the objects in the bands depends on relationship between the two (or more) datasets.
So you should made relation
Relationship can be done in several ways.
If you want to use parameters you can do this as follows:
Place DataSource component.
Connect it to dataset1(opr_operator_ods) using DataSet property DataSet = opr_operator_ods;
in DataSource.OnDataChange event write :
opr_operatorcount_ods.Close;
......
//Set parameter(relation between opr_operator(Master) and opr_operatorcount(Detail)
opr_operatorcount_ods.Params.ParamByName('opr_code').asString := opr_operator_ods.FieldByName('opr_code').AsString);
opr_operatorcount_ods.Open;
And then prepare and print report as:
procedure Print;
begin
//Prepare Master dataset ( parameters, close open etc.) like :
opr_operator_ods.Close;
opr_operator_ods.SetVariable('DATEFROM', opr_datefrom_dtp.Date);
opr_operator_ods.SetVariable('DATETO', opr_dateto_dtp.Date);
opr_operator_ods.SetVariable('TIMEFROM', opr_timefrom_dtp.Text);
opr_operator_ods.SetVariable('TIMETO', opr_timeto_dtp.Text);
opr_operator_ods.Open;
...
frxReport1.PrepareReport;
frxReport1.ShowPreparedReport;
end;

Tables got over-written

I want to loop thru a dbf and create word table for each record meeting the condition, and I got a one-page report with only the last rec in a single table. Look like all records are written to the same table. I tried to use n = n + 1 to place the variable as an element to the table
oTable = oDoc.tables[n]
But seems it only support numerical rather than variable ?
You have to add each table as you go, making sure to leave space in between them (because Word likes to combine tables).
You'll need something like this inside your loop:
* Assumes you start with oDoc pointing to the document,
* oRange set to an empty range at the beginning of the area where you want to add the tables,
* and that nRows and nCols give you the size of the table.
oTable = oDoc.Tables.Add(m.oRange, m.nRows, m.nCols)
oRange = oTable.Range()
oRange.Collapse(0)
oRange.InsertParagraphAfter()
oRange.Collapse(0)
After this code, you can use oTable to add the data you want to add. Then, on the next time through the loop, you're ready to add another table below the one you just filled.

Delphi ADO dataset Filter

In an application I am building at work, I have a large database with a table say "People" with 100,000 + rows. Furthermore the entries in this table contain two types of data :
Parent type and Child type, where each Child type entry has the database id of its parent in a special "Child_OF" column.
In memory, both db entry types are represented by corresponding classes "TParent" and "TChild", where each parent class has the field "children : TList".
Which is the fastest way, using ADO, to:
- create a list of Parents and correctly assign to them their children...
The way I see it... one can go about the problem by
1) retrieve in a bulk( by one sql query) all parents from the table and create the parents list with empty children lists.
2) retrieve in a bulk all children and for each parent try to find his/her children from the corresponding dataset.
Here is an example of what I have in mind for the assignment stage of the program...
procedure assignParentsTheirChildren(parentList: TList<TParent>;
ma_people: TADOTable);
var
i: Integer;
qry: TADOQuery;
aChild: TChild;
aParent: TParent;
begin
// create the query
qry := TADOQuery.Create(nil);
qry.Connection := ma_people.Connection;
// set the sql statement to fetch all children ...
qry.SQL.Clear;
qry.SQL.Add('Select * from ' + ma_people.TableName + ' WHERE ChildOF <> ' +
QuotedStr(''));
// supposedly do some optimization---
qry.CursorLocation := clUseClient; // load whole recordset in memory
qry.DisableControls;
// disable controls ensures that no dataset bound control will be updated while iterating the recordset
qry.CursorType := ctStatic; // set cursor to static
// open dataset
qry.Open;
// ***EDIT*** for completeness I add the suggestion made by Agustin Seifert below
qry.RecordSet.Fields['ChildOf'].Properties.Item['Optimize'].value := true;
for i := 0 to parentList.count - 1 do
begin
// get daddy
aParent := parentList[i];
qry.Filter := 'ChildOF = ' + QuotedStr(IntToStr(aParent.parentID));
qry.Filtered := true;
while (not qry.EOF) do
begin
aChild := TChild.Create;
getChildFromQuery(aChild, qry); // fills in the fields of TChild class...
aParent.children.Add(aChild);
qry.Next;
end;
end;
qry.Free;
end;
I guess the biggest bottleneck of the above code is that I am filtering the data for every new parent. Is there a faster rework using seek() or locate/find...? Basically one can assume that my dataset is static (during the time of creating the parents list) and network latency infinite:) (that is, I first want to do the child to parent assignment from memory).
Many thanks!
btw I am using Microsoft SQL Server 2012.
If you don't wanna change your code/logic, there's a way to optimize filter, find, sort operations in ADO.
Access the recordset an optimize the involved fields:
var
qry: TADOQuery;
rs: _Recordset;
...
begin
...
//after qry.Open;
rs := qry.Recordset;
rs.Fields['YourField'].Properties.Item['Optimize'].Value := True; //YourField = ChildOF in your case
This will create an index for the field. It takes a small amount of time vs the time it takes to filter lot of times without an index.
msdn: Optimize Property-Dynamic (ADO)

PL/SQL: UPDATE inside CURSOR, but some data is NULL

I'm still learning some of the PL/SQL differences, so this may be an easy question, but... here goes.
I have a cursor which grabs a bunch of records with multiple fields. I then run two separate SELECT statements in a LOOP from the cursor results to grab some distances and calculate those distances. These work perfectly.
When I go to update the table with the new values, my problem is that there are four pieces of specific criteria.
update work
set kilometers = calc_kilo,
kilo_test = test_kilo
where lc = rm.lc
AND ld = rm.ld
AND le = rm.le
AND lf = rm.lf
AND code = rm.code
AND lcode = rm.lcode
and user_id = username;
My problem is that this rarely updating because rm.lf and rm.le have NULL values in the database. How can I combat this, and create the correct update.
If I'm understanding you correctly, you want to match lf with rm.lf, including when they're both null? If that's what you want, then this will do it:
...
AND (lf = rm.lf
OR (lf IS NULL AND rm.lf IS NULL)
)
...
It's comparing the values of lf and rm.lf, which will return false if either is null, so the OR condition returns true if they're both null.
I have a cursor which grabs a bunch of records with multiple fields. I then run two separate SELECT statements in a LOOP from the cursor results to grab some distances and calculate those distances. These work perfectly.
When I go to update the table with the new values, my problem is that there are four pieces of specific criteria.
The first thing I'd look at is not using a cursor to read data, then make calculations, then perform updates. In 99% of cases it's faster and easier to just run updates that do all of this in a single step
update work
set kilometers = calc_kilo,
kilo_test = test_kilo
where lc = rm.lc
AND ld = rm.ld
AND NVL(le,'x') = NVL(rm.le,'x')
AND NVL(lf,'x') = NVL(rm.lf,'x')
AND code = rm.code
AND lcode = rm.lcode
and user_id = username;

Resources