Could you please explain to me the purpose of the following lines, especially for the First method ?
TestQ.First;
while not TestQ.Eof do
TDataSet and its descendants like TAdoAquery and TAdoTable have a standard set of navigation methods including First, Last, Next, Prior and MoveBy which move the dataset's "cursor" in the manner the method name describes.
Unless the dataset is empty, the dataset's cursor always points to exactly one record in the dataset, known as the "active" record, which is the one on which data-reading and data-writing operations take place. When db-aware components are used to display fields from the dataset, it is the values of the fields of the active record which are displayed. (In a TDBGrid, the current record is highlighted).
First simply moves the cursor to the first record in the dataset, according to the dataset's current order and taking into account any active filter on the dataset.
Btw, the standard behaviour when opening a non-empty TDataSet places the cursor on the first record in it, so has the same effect at calling First. Generally, First is called before traversing a dataset so that the traversal starts at a known position.
the idea , iterate a query with the following code
AnyQuery.first;
while (not AnyQuery.EOF) do
begin
/// enter your query code here
....
...
.
/// Move to the next query record
AnyQuery.Next ;
end;
Related
What should be the code for inserting after the last record using the ClientDataSet?
I tried the following:
cdsSomething.Last;
cdsSomething.Insert:
But it appears it replaces the last record instead. I am sure there must be a quick code for this.
The method to append a record to the end of the Dataset (let alone any index) is Append. You don't even need to call Last before.
cdsSomething.Append;
Insert inserts a row before the selected record, so with your code, the new record should become the second to last record.
In general, where an added record (or, in fact, any record) appears in the DBGrid does not depend on the dataset operations which were used to insert it.
In fact, the DBGrid is irrelevant to this question, because it simply displays the added row in the ClientDataSet in the position the added row occurs in the CDS according to its current index order.
So, for example, if the CDS contains an integer ID field, and its current index is this ID field (e.g. because the CDS's IndexFieldNames property is set to 'ID'), to make the added row appear at the end, all you need to do is to set its ID value to something higher than any existing record in the CDS. If the field is of type ftAutoInc, this will, of course, happen automatically.
Uwe Raabe has answered this q a bit differently. What he says is correct if the CDS is not using any index, so the records are displayed in the physical order they appear in the CDS's datafile. However, relying on the physical order to determine the display order is not necessarily a good idea if the display order is important. If it is, then use an indexed field (or fields) to determine the order.
D5, ZEOS 6.6, SQLite.
I have srcAccount.AutoEdit = False;
I have All Edit functions set to False in the TDBGrid Options. Only options are set to true are Indicator, grind lines and Titles.
I have a form with a few TDBEdits and a TDBGrid on it showing all the current accounts.
When the user clicks the "New" button for a new account I have
dbedAcct.SetFocus;
tblAccounts.Insert;
If, after clicking the New button, the user wants to scroll to check Account names OR happens to click in the grid, it saves the new data and drops out of Insert mode.
How can I stop this happening? I need for them to be able to check account names.
Or, is this a bug with D5? If so, how do I work around it?
I also tried using SMDBGrid and it does exactly the same thing.
http://www.scalabium.com/smdbgrid.htm
I need for them to be able to check account names.
You can't do that using the same grid + dataset if you're allowing the user to do data entry to the grid. You are creating this problem for yourself by trying to use the grid for data entry and look-up at the same time. A simple solution is to use the grid for look-up and have a separate form (or panel on the same form as the grid) for doing the inserts, and these need to be connected to different dataset instances.
The thing is, you can't scroll around a dataset (as you need to do you look up other records) while it's in the middle of inserting a record. The dsBrowse state needed for a dataset to allow scrolling around and the dsInsert state needed to insert are mutually exclusive. Attempting to scroll the dataset will automatically post the pending insert, as you've found.
So, you actually need two dataset instances, one for lookup and one for inserts. If you use two client-side ClientDataSet instances, it can be quite straightforward because of the ease with which you can copy the data from one to the other using the CDS's Data property (cdsLookup.Data := cdsLive.Data), so making a local copy for look-up is trivial. Or, if you prefer you can use a cloned cursor - see http://edn.embarcadero.com/article/29416
I need to implement a combobox, which is bound to a TpFIBDataSet (descendant of TDataSet). I've done this several times before. It's not a big thing if it contains only predefined values.
This time, I'd like to have a combobox that accepts custom values entered by the user, also giving the ability to the user to select some predefined value. Newly entered values shall be inserted into some table of the database just before the record the combobox's field belongs to is posted.
The main problem seems to me, that predefined values are internally represented as integer IDs (the combobox I use is TwwDBComboBox from Roy Woll's InfoPower package, as it implements maplist functionality) because the field is a foreign key, while custom values may be nearly everything (only restricted by a mask).
How can I distinguish between an integer ID and integer user-input, for example?
See the set properties of the combobox:
AComboBox.Style := csDropDown;
AComboBox.MapList := True;
I don't request a solution as take this piece of code and be happy. I'm rather looking for some advice by others who might have or had a similar problem.
How can I distinguish between an integer ID and integer user-input, for example?
You go back to the database. Either query directly select count(*) from table where id = ComboBoxId.
Or use the Locate method of the dataset.
Or keep a cache handy in a MyList: TList<Integer> and do a MyList.BinarySearch to see if the item is already in the DB.
Obviously the cache will only work if the DB is single-user, because otherwise you will not be able to keep it up-to-date.
If it is not in the DB, you run the insert query.
After it's inserted you run the default combobox behavior, because now the values is sure to be in the DB.
I am writing a grid control that will display the contents of either a TDataSet or a TObjectList. When you only need to support TDataSet, things are quite simple:
Link to the dataset via a TDataLink descendant.
When painting the contents of the grid, you can use the records buffered in that TDataLink to paint what you need to.
There is no need to have individual objects somewhere to represent rows in the TDataSet, because you always just paint the rows in the buffer.
In my case, I need to accept data from a few other sources as well, which meant that I needed to have an object representing each row (also because the control required quite a bit of row state).
But this causes problems with the model described above. Because I have an object representing each row, I need to be informed when records are added or deleted from the TDataSet. And I just cannot see how to do that.
Clearly, I don't want to be hooking to the dataset events; they may already be in use and the TDataLink is meant to be the mediator between my control and the dataset. And my attempts at using the DataEvent virtual method failed, because it simply doesn't tell you if a record is being added/deleted.
Any ideas?
If you hook your TDataLink descendant to a TDataSource that is connected to the TDataSet you get a call in the RecordChanged procedure when data changes.
You can use the events OnDataChange and OnUpdateData of a TDataSource connected to the TDataSet.
It seems, you have to derive your own class from the base dataset class you are going to use. There you will need override InternalAddRecord, InternalPost, InternalDelete methods and handle records addition / deletion.
Using TDataSet.FindKey you can locate records. When it results in True the datasets cursor will be positioned on the found record.
I use this when I select items in a list on the left, corresponding data should appear on the right.
When it results in False the cursor is not moved. This results in the record data prior to FindKey being displayed in data aware components on the right.
How can I code the result of FindKey to return an empty record?
if Not tblSomeTable.FindKey([SomeSearchData]) then
begin
< code to return empty or move data cursor to neutral position >
end;
Update: (Waited a few days before selecting right answer as I believe that is the custom and didn't wan to discourage further feedback.) There were several suggestions on tackling this situation although I believe the correct answer was from Marcelo in that it is not possible to have a cursor not be on a record. Several workarounds were suggested. I chose one of my own. It went something like:
If Not tblSomeTable.FindKey([SomeSearchData]) then
begin
tblSomeTable.FindKey([-1,2010]);
end
What I did is create a dummy, blank record with an index that the actual data can never be, ie: The first index value will never be -1. If the initial search comes up empty then the FindKey will position the cursor on this empty record. This will provide the visual effect I was after.
TDataSet does not have a "neutral position". But as always you have few options:
Set dataset into insert / append record mode. So, the controls and code will see empty record. Be careful, as something may incidentally assign data to a field and then the new record may be posted to DB.
Depending on the data access components, you are using, you can set dataset to Cached Updates mode, insert and post new empty record into dataset, and mark all changes as applied. Then assign a filter, normally rejecting this empty record. Then in your code you have to switch the filter over, so it will reject all records, excluding this empty one.
Consider to disconnect the TDataSource from the dataset and later connect it again.
Note sure, but probably there may be invented some other approaches :)
Hallo,
use SetRange instead of FindKey.
tblSomeTable.SetRange([SomeSearchData],[SomeSearchData]);
try
while not tblSomeTable.Eof do begin
<do something with the Record>
tblSomeTable.Next;
end;
finally
tblSomeTable.CanelRange;
end;
when you criteria ensures that the maximum of matching records is one you retrieve with the statement above zero or one record.
This is not possible as far as I know. The cursor must always be on a record unless Bof and Eof are both true (empty data set).