This may be a newbie question, but I was unable to solve this problem.
I have a TQuery component (Query1) wich returns me a dataset from a database.
I would like to pass the records in it to a QuickReport QRDBText component - with no success so far.
I am creating the Query1 in run-time, and then an event (button press) would come up with the Quickreport. I got this far with it:
QReport.DataSet:=Query1;
QRDBText1.DataSet:=Query1;
QRDBText1.Datafield:='Vnev'; //first field in query
QRDBText2.DataSet:=Query1;
QRDBText2.Datafield:='Knev'; //second field in query
QRDBText3.DataSet:=Query1;
QRDBText3.Datafield:='Idcard'; //third field in query
But this shows me the big grey nothing.
If I operate with a sample database and place a TTable on the QuickReport, and set up the connections via the Object Inspector, it works. But again: I need to display data from my Query.
Any help would come handy! Thanks
The QRdetailband should set to true and fill the QRdbtext on it.
Related
I am trying to insert a data to a field referenced by specific ID of row (or record) in the clientdataset at runtime.
I am using delphi and here's the structure of my case — mysql database > mysqluniprovider > uniquery > dataset provider > clientdataset > datasource > dbgrid.
The data I am trying to insert is generated during runtime by another code in the same procedure. Hence, dbnavigator will not work for me here. On the otherhand, I prefer to do this at the clientdataset level and do not want to direct to sql.
I was able to find the reference id by using the clientdataset.lookup/locate/findkey. But I could not be able to direct the cursor to the cell of the same row of reference id and specific field to insert the data.
I believed there must be a code component for this type of case just like cds.lookup/locate/findkey to update data in an existing table at runtime.
I will greatly appreciate any help on this.
The fact that you are using a TDBGrid to display the data is incidental, you change the data in the clientdataset and the TDBGrid will automatically update its display of the record. What you neeed to do is to use methods of of the clientdataset to move to the record and update its field data. You can use the clientdataset's Locate method to move to the record you want, as in:
ID := 99; // the ID of the record to change
if ClientDataSet1.Locate('ID', ID, []) then begin
ClientDataSet1.Edit; // Put the CDS into dsEdit mode so you can change its field data
ClientDataSet1.FieldByName('SomeField').AsString := 'Whatever';
ClientDataSet1.Post; // save the change(s) to the record
end;
See the online help for TField for its various AsXXX methods, such as AsInteger, AsFloat, etc.
I have seen there is a problem refreshing only current row if the data are changed in another form (and DataSet too) or even if the data are changed on server side (in trigger or from another user).
So i find solution that partially work with DevExpress's cxGrid and have following issue.
frm:=TfrmEdb01.Create(Self); // create edit-form
try
frm.LoadData(0); // do the insert in TAdQuery
frm.ShowModal; // Show edit form
// after closing an Edit-Form, make a new record in cxGrid
row:=tv01.dataController.InsertRecord(tv01.Controller.FocusedRecordIndex);
// for each column in grid...
for col:=0 to tv01.DataController.ItemCount-1 do
begin
// ...get its FieldName
sFld:=tv01.DataController.GetItemFieldName(col);
// and if this field exist in edit-form's Query
if frm.q01.FindField(sFld)<>nil then
//then assign this value to the newly created row in the grid.
tv01.DataController.Values[row,col]:=frm.q01.FieldByName(sFld).Value;
end;
finally
frm.Free;
end;
And realy, when I step out from the edit-form there is a New row created in the cxGrid. Problem is in that:
- if is double-clicked (to open edit-form again) then the previous record is opened. It seems to me the Data acctualy did not updated in Query object, only in GridView... maybe I should use values from Grid instead of Query object.
- if select another record (scroll them up and down), and then want to select again this new record, then it can not be focused at all. Only if I reload whole dataSet, then this new record can be selected same as any other.
Cheers!
p.s. To clearify a problem, I will provide whole test-project. There is also SQL script to create a table in the database, and AnyDac connection must be set to Your database.
There is also an image thate will/should ilustrate a problem.
You have call post to save the changes to the underlying dataset. Do it just before finally.
tv01.DataController.Post();
See the help for TcxCustomDataController.Post for more details.
In Delphi 10.1 I have an ObjectList named DogCollection
and each entry is of the type TDog, a custom class.
thanks to tutorials from malcolm groves I was able to populate a Stringgrid
with my DogCollection.
http://www.malcolmgroves.com/blog/?p=1084
Now I'd like to be able to scroll through the stringgrid and everytime I scroll I want to update the variable "CurrentDog" from the type TDog, with whatever Object is highlighted in the stringgrid.
So I have an Overview about my DogObjects and also a single Object of my Dog
which I can independently view/manipulate.
I am out of ideas at this point.
If it is of any help to you, I can also not get the AfterScroll events of the Adapter to trigger, not even when I add a Navigator with RightClick->Add Navigator.
I thank you for your help and time.
Not sure to understand the question but I think you don't need to have a variable "CurrentDog" to work on the selected object of your list.
You can create all the components (TEdit) you need for your dog (Name, Age...) and bind these components to the same fields (Name, Age...) in your TDataGeneratorAdapter (which is linked to the "Adapter" property of your TAdapterBindSource).
Then, when you select a row in your grid, the corresponding object appears in your edit components. When you modify the "Text" properties, the grid is updated.
EDIT : InternalAdapter
After few searches, you can get your object with the InternalAdapter of your TAdapterBindSource
On the OnClick event :
procedure TForm1.Button1Click(Sender: TObject);
var
Adapter: TBindSourceAdapter;
begin
Adapter:= AdapterBindSource1.InternalAdapter;
CurrentDog:= TDog(Adapter.Current);
end;
I've not been able to find an answer on this anywhere. Using Delphi XE7 with TClientDataSet, DataSnap & SQL Server. I need to insert a record, apply updates and then refresh that record so I can get the Id and assign it to my object. Seems pretty basic requirement, but on the contrary it is proving to be a royal pain.
I've found the obvious stuff on EDN, SO and Dr Bob:
http://edn.embarcadero.com/article/20847
DataSnap and the autoinc field
http://www.drbob42.com/examines/examinC0.htm
However these seem to focus on a "Refresh" of the TClientDataSet to re-fetches the entire table/query. Whilst this does actually resolve the Id field itself (good!), it also moves the cursor off the current record which was just inserted and so I'm not able to get the Id and assign it to my object. Also, for performance over HTTP I don't really want to refetch the entire table every time a record is inserted, if there's 10,000 records this will consume too much bandwidth and be ridiculously slow!
Consider the following code:
function TRepository<I>.Insert(const AEntity: I): I;
begin
FDataSet.DisableControls;
try
FDataSet.Insert;
AssignEntityToDataSet(AEntity); // SET'S ALL THE RELEVANT FIELDS
FDataSet.Post;
FDataSet.ApplyUpdates(-1);
FDataSet.Refresh; // <--- I tried RefreshRecord here but it cannot resolve the record
AEntity.Id := FDataSet.FieldByName('Id').AsInteger; // <----- THIS NOW POINTS TO WRONG ROW
finally
FDataSet.EnableControls;
end;
end;
Does anyone know how to achieve this? I need to be able to refresh and stay on the current record otherwise I do not know the Id of the record just created and the GUI cannot stay focused on the current record.
Hopefully something obvious I'm missing.
Cheers.
Rick.
Assuming you can get hands on the new ID inside the AfterUpdateRecord event of your DataProvider, your event handler then may look like this (the current record of DeltaDS is the one just inserted into SourceDS):
if (UpdateKind = ukInsert) then begin
DeltaDS.FindField('Id').NewValue := <TheNewID>;
end;
Make sure to have the poPropogateChanges option set in the provider. This will transfer the changed Id field back to the ClientDataSet.
Now you can get rid of the FDataSet.Refresh call.
SQL Server does allow you to get the last identity it generated in several ways - there's no need to "refresh" the record/query which means re-issuing a SELECT and can generate undesiderable side-effects. You can use SELECT SCOPE_IDENTITY() or use an OUTPUT clause. If the Delphi database driver supports it, TField.AutogenerateValue should accomplish that task automatically (see http://docwiki.embarcadero.com/Libraries/XE7/en/Data.DB.TField.AutoGenerateValue)
Otherwise you have to put that new data into your delta (see Raabe answer - this has to be done on the datasnap server which actually talks to the database) after reading it, so it's sent back to the client. You also need to set properly and TField.ProviderFlags to ensure data are applied correctly (see http://docwiki.embarcadero.com/RADStudio/XE7/en/Influencing_How_Updates_Are_Applied), usually you don't want those field appear in an UPDATE.
I am not a Delphi programmer, but I I got an old Delphi 7 application that I need to fix and it is using ADO.
The database table (MS Accesss) contains +100,000 rows and when I set the ADOTable.Active=true it starts to load the entire table into RAM and that takes a lot of memory and time.
How can I prevent ADO to load the entire table? I tried to set the MaxRecords but it does not help.
Basically all we do is att program startup:
// Connect to database
DataModule.MyADOConnection.Connected:=true;
DataModule.MeasurementsADOTable.MaxRecords:=1;
// Open datatables
DataModule.MeasurementsADOTable.Active:=true;
After setting Active=true it starts to load the entire measurements into RAM and it takes TIME!
We are using the MSDASQL.1 provider. Perhaps it does not support the MaxRecords property?
How do I add some limiting query into this data object to only "load TOP 1 * from Measurements" ?
You could use TADOQuery to limit the result set with a sql query. Or you could use TADOTable and set the CursorLocation to a Server side cursor to prevent the client loading the complete resultset in memory.
You could use that adoTable with an Server OpenForwardOnly cursor and
an TCLientDataset with PacketRecords set to nonzero value. Worked
wonderfully when I had to write an app to pump data from MSSQL to
Oracle on a customized way with tables with millions of records.
EDIT -> It would be something on the lines of this:
procedure ConfigCDSFromAdoQuery(p_ADOQ: TADOQuery; p_CDS: TClientDataset; p_Prov: TDatasetProvider);
begin
If p_ADOQ.Active then p_ADOQ.Close;
p_ADOQ.CursorLocation := clServer;
p_ADOQ.CursorType := ctOpenForwardOnly;
p_Prov.Dataset := p_ADOQ;
p_CDS.SetProvider(p_Prov);
p_CDS.PacketRecords := 100;
p_CDS.Open;
end ;
I've done this all by code, but most of that you can do in design-time.
This article is BDE specific, but applies to ADO or most client data access libraries.
http://dn.codegear.com/article/28160
I would recommend using TADODataSet (it's "closer" to the ADO layer than TADOQuery) and selecting only the data the client needs by providing a custom search form (date range, list of specific items, etc)
Good luck
On your datamodule where "MeasurementsADOTable" currently resides, drop a TADOQuery and name it "MeasurementsADOQuery"
Set the Connection property of MeasurementsADOQuery to MyADOConnection (assuming this is the case based on the little code snippet provided.)
I'm also assuming that you are displaying a grid or otherwise using a DataSource - change the DataSource component's "DataSet" property from MeasurementsADOTable to MeasurementsADOQuery
Edit the actual query to be executed by setting the SQL property of MeasurementsADOQuery. (In runtime before opening: Measurements.SQL.Text := 'select top 10 * from measurements order by whatever')
Analyze/change all references in code from MeasurementsADOTable to MeasurementsADOQuery
Dont make the adotable active on startup and turning it true later is one way but still not really gonna help....use a adodataset and populate that rather as needed during runtime with your connection text. Only relevant data will be retrieved making it much faster.
use adoquery
If you do not need any row and just want insert new row use sql command like this
'select * from myTable where id=-1'
Since Id is autonumber no rows will return .
or
'select * from myTable where 1=-1'
But I think it is not good way for Insering data. Using adocommand is sure much better.
if you want X rows
'select top X * from myTable '
In furthering Fabrico's answer above, I have legacy application which has a table with 177000 rows and 212 columns. Upon trying to open this table I get the error 'table already open' and no records are available for update. setting Table.CursorLocation := clUseServer;
fixed this issue for me.
I have found ADO + Access w/Delphi to be painfully slow, for lots of things (big table reads like you're describing, but also inserts as well, etc). My answer became "Quit using ADO and Access altogether." Never did understand why it performed so poorly, especially when earlier technologies seemed not to.