Add fields to a clientdataset or access it dynamically? - delphi

What's the point in adding fields to TClientDataset if and can do Cds.FieldByName('field').Value?
Is it faster to have the reference?
Is it 'clearer'?

The problem with
DataSet.FieldByName('field').Value
is a performance one. Each time this is executed, it causes a serial search through the fields collection of the dataset to locate the one with the required name. This search is not optimised in any way, for instance by using a binary search or hashing algorithm. So, if there are many fields and/or you are doing this access while iterating the records in the dataset, it can have a significant impact on performance.
That's one reason, but not the only one, to define "persistent" TFields using the Object Inspector. You can obtain a reference to a particular TField by using the symbolic name known to the compiler and this happens once only, at compile time. So yes, it is faster than FieldByName. up to you whether you find it clearer.
Other reasons to use persistent TFields include the ease which which calculated fields can be set up and, more importantly, the fact that the calculated field(s) do not need to be accessed via FieldByName in the OnCalcFields event. The performance hit of using FieldByName versus persistent fields is, of course, multiplied by the number of fields referenced in the OnCalcField event and OnCalcFields is called at least once for each record in the dataset, even if you do not iterate the dataset records in your own code.
The above is true of all TDataSet descendants, not just TClientDataSets.

Related

Is it safe to share a TDataSource between several controls at the same time?

I have moved all my lookup tables/queries in the main DataModule of my application. Now I wonder if I could also move the associated TDataSource to the Datamodule.
For example, if I have two TLookupCombobox on two different forms (or even on the same form) using the same TDataSource, will this have an impact ? Like when I choose an item in combobox1 will it move to the same item on combobox2 ?
I only want to use these TDataSources read only.
Yes, it is possible for a TDataSource to be placed in a TDataModule and used as the datasource for db-aware components on a number of forms. However, it is generally not a good idea and is one you are likely to come to regret. Doing it creates a maintenance problem for yourself, because it is rather too easy to make some change to its properties which affects how the db-aware components behave and overlook the knock-on consequenes for the various forms (same is true for TDataSet descendants, of course).
Sharing a datasource across forms can impact on performance if instances of the forms exist at the same time, and this is particularly so if you carry out some procedure which iterates the records in the dataset (unless you surround the operation with calls to TDataSet.DisableControls and .EnableControls).
So imo although there are benefits to placing datasets in a datamodule which is used by a number of forms, it is far better to place the datasource(s) on the forms which contain the connected db-aware components.
So far as db-aware compound controls such as TDBComboBox, TDBListBox, etc are concerned, these always display the value from the current record in the related dataset. No amount of coding will permit these components to simultneously display different field values on different forms: if they are fed by the same datasource from the same record field, they will display the same value (though of course, the contents of associated lists in the controls, as in the drop-down list of a DBComboBox, may differ between them). This arises from the way TDataSets are designed to operate: they all implement a dataset "cursor" which makes all data-reading and -writing operations operate on a single, "current" record of the dataset and the value of the dataset field which a db-aware component displays is the value in the record the cursor is on.
You are asking two questions there. - No problem.
On the usage of the Datasource, absolutely. I have all my TTables, TQuerys and DatSources in the one DataModule aand reference them from multiple Forms with multiple components on each.
On the combobox1 v combobox2 interaction, it will depend on the code behind them. Nominally in the OnChange event.
Ian

Assign, memcpy or other method required for TClientDataset or TDataSetProvider (dbexpress)

I'm usign the dbExpress components within the Embarcadero C++Builder XE environment.
I have a relatively large table with something between 20k and 100k records, which I display in a DBGrid.
I am using a DataSetProvider, which is connected to a SQLQuery and a ClientDataSet, which is connected to the DataSetProvider.
I also need to analyze the data and therefore I need to run through the whole table. For smaller tables I always used code, which is basically something like this:
Form1->ClientDataSet1->First();
while(!Form1->ClientDataSet1->Eof){
temp=Form1->ClientDataSet1->FieldByName("FailReason")->AsLargeInt;
//do something with temp
Form1->ClientDataSet1->Next();
}
Of course this works out, but it is very slow, when I need to run through the whole DBGrid. For some 50000 records in can take up to some minutes. My suspicion is that the most perform is lost since the DBGrid needs to be repainted as the actual Dataset increments its address.
Therefore I am looking for a method which allows me to either read the data without manipulating the actual ClientDataSet. Maybe a method which copies the data of a column into a variable, or another way to run through the datasets, which is more efficient. I am sure if I would have a copy in a variable the operation would take less than a few seconds...
I googled now for hours, but didn't find anything useful so far.
Best regards,
Bodo
if your cds is connected to some db-aware control(-s) (via TDataSource) then first of all consider using DisableControls()
another option would be to avoid utilizing FieldByName within the loop

What is the transient, indexed, index spotlight and store in external Record file in core data?

I want to know when to use below properties? What do they do? Why should we use it?
Transient: According to Apple Docs:
Transient attributes are properties that you define as part of the
model, but which are not saved to the persistent store as part of an
entity instance’s data. Core Data does track changes you make to
transient properties, so they are recorded for undo operations. You
use transient properties for a variety of purposes, including keeping
calculated values and derived values.
I do not understand the part that it is not saved to the persistent store as an entity instance's data. Can any one explain this?
indexed: It increase the search speed but at the cost of more space. So basically, if you do a search query using an attribute and you want faster result then make that property as 'indexed'. If the search operation is very rare then it decreases the performance as it take more space for indexing.
I am not sure whether it is correct or not?
index in spotlight
Store in External record file
Consider for instance that you have a navigation app. On your map you have your car at the center, updated a few dozen times a second, and an Entity of the type "GAS STATION". The entity's property 'distance' from your car would be a transient property, as it is a function of real time data thus there's no point to storing it.
An indexed attribute is stored sorted, therefore it can be searched faster. Explanation can be found on Wikipedia. If your frequent searches take noticeable time, you should probably consider indexing.
Consider indexing in Spotlight anything the user is likely to want to search for when not within your application. Documentation is here.
Large binary objects, like images, should be stored externally.

Delphi TClientDataSet, maximum number of fields per index

I have a simple Delphi (2007) procedure that given a TDataSet and a (sub)list of fields returns a new TClientDataSet with the distinct values from the given TDataSet.
This works quite well.
In my proc I used the TClientDataSet index to populate the distinct values.
It was fast and easy.
The problem is that TClientDataSet index support at maximum 16 fields.
If you add more of them they will be silently ignored.
I need more than 16 fields in the dataset (and thus in the index).
Is there any solution? Some hack?
Maybe some open source library to use as workaround?
I'm working offline so I must do it in memory. The size of the dataset is not huge
If you're needing to get distinct occurrences of records across more than 16 fields and you want to use an index to keep things fast you'll need to consider concatenating some of those fields. For example:
Test Field Field 1 Field 2 Field 3 Field 4
Apple~Banana~Carrot~Donut Apple Banana Carrot Donut
Create you index on the Test Field.
You might need to create multiple test fields if the total length of your other fields exceeds the maximum length of a text field.
You could swap out the TClientDataSet for a TjvCsvDataset from JVCL. It can be used as a pure "in memory dataset" replacement for Client Data Sets, without any need to read or write any CSV files on disk.
It is not quite like Client Data Set in design. I am not sure what benefit all these "Indexes" in a client data set offer you, other than that you can't have a field without an index definition, but in the case that this is all you need, you can set the TJvCsvDataSet.FieldDef property = 'Field1,Field2,.....FieldN' and then open the dataset and add as many rows as you like to the dataset. It is practically limited to the amount of memory you can address in a 32 bit process.

How do I determine when a record is inserted in a TDataSet?

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.

Resources