How to get each row from a TDBGrid in c++ - c++builder

I am using embarcadero c++ builder and i want to copy the result of a query contained in a DBGrid, is there a way to get each row from the DBGrid?

It depends on what you mean by "copy the result," as well as what type of database you are using. A TDBGrid is typically used to display and edit data visually. It's not the most efficient way to get information from a database and put it into a data structure.
So, if what you want is to (non-visually) do some action for each row of a SQL result set, you should probably use a TSQLQuery. This object has useful methods and properties (i.e., First(), Next(), and Eof) that allow you to iterate through the result set.

You don't get the result from the TDBGrid directly; you get it from the TDataSet attached to the TDataSource that is connected to the TDbGrid. IOW, you read it directly from the TQuery/TADOQuery/TSQLQuery that you use to execute the query. As you've not mentioned which DBMS and data components you're using, it's impossible to be more specific.
You can find the component that is executing the query at design-time (in the IDE) by clicking on the grid and inspecting it's DataSource property in the Object Inspector's Properties page, which will give you the name of the TDataSource component. Clicking on that datasource and inspecting it's DataSet property will give you the name of the query component which is running the SQL statement. You can read from that component to retrieve the data that is returned by executing the statement.

Related

TDateEdit cannot LiveBinding with a datasource bidirection

This is a simple question.
Start a multidevice App, place a TDateEdit and DBTable with a field containing TdateTime data. Then use LiveBinding designer link the data source field to TDateEdit.DateTime property. However, this link is unidirectional , The control DateEdit can accept the data from the Datasource, but cannot update the changes to the datasource. How to change the link to bidreiction???
There may be some things that are not clearly understood.
Normally a LiveBindings link will be bidirectional - so if you setup two TDateEdits and bind them together changing one should change the other, irrespective of which one you change.
It seems that what you are hoping is that changing the TField will update the data in the database table.
That's not typically how the Data Access components in Delphi work.
The TField is part of a TDataSet. The TDataSet has a Post method to write changes to the underlying data store, if that's supported.
The Data Access components are extremely powerrful and can cope with lots of different scenarios, and allow you to extended capabilities throguh exposed events as well as subclassing the components.
If you want to update your underlying data store you need to have a writeable TDataSet and you need to call Post on it to write.
I suggest you start off with some of the videos in Enbarcadero's YouTube channel about how to use the data acecss components. It's not difficult to do but beyond the scope of an answer here.
If you use the LiveBindings Wizard to hook the DateEdit with a field, it will hook it up bi-diretionally to a "virtual" SelectedDateTime property of the DateEdit. That property doesn't really exist in the TDateEdit but is used by the LiveBindings to map the separate Date and Time properties of the DateEdit.

How to fix the DevExpress TcxGrid GridMode using a TSQLDataSet linked to a TSQLQuery?

I'm trying to load about 500k+ data linked to an Oracle DB on my TcxGrid, I want to make the process faster using the GridView "GridMode" property, but I need to do that using a TSQLQuery (DBExpress Component) and it just doesn't work, Gridmode seems inoperable (doesn't load data faster on the Grid, doesn't load custom quantity of records using the "BufferCount" property, etc.)
Here I created a TSQLQuery component and used a query script for my 500k table (for performance purposes I just got 500 values but I need to load 500k+):
TSQLQuery
When I link the TSQLDataSet to the Grid and activate the TSQLQuery it shows all the records from the query, even if the GridMode is TRUE and the GridModeBufferCount is 5
GridWithTSQLQuery
On the other hand, when I use a TQuery, the GridMode just works properly, in this case I had to open SQL Explorer, make the connection and assign that connection to the TQuery DataBase property:
SQLExplorer
Here I show my TQuery with the values mentioned before:
TQuery
And when I activate my TQuery.. voilá:
GridWithTQuery
What I'm doing wrong? or do I need to do more things on my TSQLQuery besides linking it to my dataset and then linking the dataset to the grid?
It's impossible that a very old Tquery can do this and not a newer dbExpress component
Thank you so much guys

Run-time Equivalent to Assign Local Data... for TClientDataSet and TSQLQuery

The TSQLQuery class is unidirectional, so for it to be used as a source for a data-bound TDBGrid, a TClientDataSet needs to be linked between the TSQLQuery and the TDataSource that the TDBGrid is bound to.
I can connect the TSQLConnection and make the TSQLQuery active at design-time, with specified params, and then I can right-click on the CDS and choose the "Assign Local Data..." option to have it grab the data from the TSQLQuery, which then appears in the TDBGrid via the linked TDataSource.
The amount of time between choosing "Assign Local Data..." and the data actually appearing in the grid is very short, so I am looking for the way to replicate this at run-time.
Supposedly, I can set the Data property of the CDS to the Data of the source, but a TSQLQuery has no Data property. There was a post about using a provider, but
DataSetProvider1.DataSet := SQLQuery1;
ClientDataSet1.Data := DataSetProvider1.Data;
throws an access violation,
I did implement the data copy by looping through the TSQLQuery and appending the records to the TClientDataSet, but that was a lot slower than the "Assign Local Data...".
[Edit 1]
All the components I need are hooked up at design-time, and I can make the TSQLConnection active, then the TSQLQuery, then the TClientDataSet and the TDBGrid displays the data from the parameterised query defined in the TSQLQuery.
In the OnChange event of a TComboBox, I need to refresh the query using a different parameter and have the grid show the relevant results, so I Close the TSQLQuery, change the ParamByName value, Open the TSQLQuery and then call ClientDataSet1.Last to highlight the last row in the grid.
That gives me a "Cannot perform this operation on a closed dataset" error, so I use
ClientDataSet1.Active := true;
but that throws an "Access Violation".
All the examples I can find are all about dropping components onto a form, linking them together, and they work. Well, yes they do, but I need to change properties in code at run-time and still have it work, which it just refuses to do.
This is really beginning to frustrate me.
[Edit 2]
So I followed the example on the Embarcadero site for building a VCL Forms dbExpress Database Application, substituting my database connection details for the Interbase one the example uses.
http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Building_a_VCL_Forms_dbExpress_Database_Application
In the designer, everything looked fine and the grid was showing the results from the query, but when I went to run it using F9, I was getting an "Access Violation" thrown from within TCustomClientDataSet.InternalCheck.
Turns out this is a known MIDAS.DLL versioning problem and can be resolved by including MIDASLib in the uses clause of the form. It is just unfortunate that the Datasnap.DBClient code is still throwing Access Violations instead of proper messages, especially since this problem was reported in 2013.
You can use this code. Just change TUniConnection and TUniQuery to what You are using:
Procedure CdsAssignTable(aTableName:String; aCds : TClientDataSet; aCon
:TUniConnection );
Var
aQUery : TUniQuery;
aProvider : TDataSetProvider;
begin
if aCon=Nil then raise Exception.Create('aCon=Nil');
if aCds=Nil then aCds:=TClientDataSet.Create(aCon.Owner);
aQUery:=TUniQuery.Create(Nil);
aQUery.SQL.Text:='select * from '+aTableName;
aQUery.Connection:=aCon;
aQUery.Open;
aProvider:=TDataSetProvider.Create(Nil);
aProvider.DataSet:=aQUery;
aCds.Data:=aProvider.Data;
FreeAndNil(aProvider);
FreeAndNil(aQUery);
End;

How do I add data to a database in Delphi by using edits instead of a DBNavigator?

My interface is very basic. It just includes edits for the user to input data into a database, when they click the button i want it to add the data into my database.
You can easily do this.
Go to the Data Controls tab of the Component palette.
Select a TDBEdit and place it on the same form as your DBNavigator. The IDE will name this DBEdit1
Set the Datasource property of your DBEdit1 to the same datasource as your DBNavigator.
Set the DataField property of DBEdit1 to the name of a field in your dataset.
Compile and run.
That's it. Leave your DBNavigator on your form because you will find that when you make a change to the contents of DBEdit1, its Save and Cancel buttons automatically enable to let you save or cancel the change.
Also, you'll find that if you click your DBNavigator's '+' button, which begins the insertion of a new record into your table, you can then type the field values for the new record into your DBEdits.
Don't use normal non-DB-aware TEdit components and a dynamically-created Sql statement which concatenates the TEdits's contents with other Sql as suggested in the other answer which briefly appeared here and now seems to have been deleted - it is a waste of time, but much more importantly renders your app vulnerable to Sql-Injection - see https://en.wikipedia.org/wiki/SQL_injection. By sending the server an unverified Sql statement which includes what the user has typed into a TEdit, you're effectively providing the user with an opportunity to type additional Sql statements into the TEdit and that is exactly how Sql injection can occur. On the other hand, when you use TDBEdits, the Sql for updating the database record is automatically generated by Delphi's TDataSet framework in a way which does not provide a similar opportunity for Sql Injection.
If some reason you absolutely have to generate your own Sql Update statements, to minimise the risk of Sql Injection, make sure that you use a parameterised Update statement, that is, one where the changed field values are specified as values of parameters in your TDataSet-descendant's Parameters object, rather than in the Update Sql itself. An example of a parameterised Update statement might be:
Update MyTable set FieldA =:FieldA, FieldB=:FieldB where RowID =:RowID
where :FieldA, :FieldB and :RowID are the parameters.

How can I set a TClientDataSet's data in the design view?

I have a TClientDataSet object that I've added to my form.
I've managed to define the fields through the object inspector. However, is there a way to define the data the TClientDataSet contains without doing it in code?
I've managed to do it by adding some AppendRecord statements to the ShowForm event; but I'd rather keep the definition of the TClientDataSet all in one place.
Prior to v10.2 (Seattle, Berlin, etc.), there were no facilities to do this.
You can store and load a file into the ClientDataSet or you can use the Assign Local Data (right click on the ClientDataSet) to load data from an existing source. However, you cannot append or edit the data from inside the IDE directly.
As of v10.2 (Tokyo) has recently added this as a feature.

Resources