What should I use between FDQuery, FDMemtable, and ClientDataset in Delphi FireDac? - delphi

New to Delphi. I'm developing an application that needs to access a MSSQL database, to do this I've used an FDConnection, FDQuery and a DataSource component connected to a grid. With these I can access/modify/delete data just fine. Now if for example I want to filter the grid, I can do this by changing the FDQuery component at run-time, but I'm not sure if this is the right approach.
I've thought about using something that stores tables in memory like ClientDatasets because I'm not sure if FDQuery does this, so that I can manage data I've already retrieved without accessing database more than needed. My problem is I don't have a fundamental understanding of any of these components, so my question is:
Do I need to use anything else other than FDQuery?
A little more context on what I'm building: UniGUI web application, with the MSSQL server in the same LAN as the Web Server, and multiple user access to DB.

Now that I understand these components better, I found this FAQ from Embarcadero's doc that explains what I wanted to know.
Q1: Can I use TFDQuery and connect it to a dataset provider and retrieve the data in an Embarcadero client dataset?
A: TFDQuery is a mix of TFDMemTable, TFDTableAdapter and several
TFDCommand's. So, TFDQuery has everything inside to execute SQL
commands, send parameter data, receive and store result sets, browse
result sets and post changes back to a database. There is no reason to
use TFDQuery + DSP + CDS.
You can use TFDMemTable, TFDTableAdapter and TFDCommand directly,
instead of TFDQuery. They give more flexibility, but also require more
coding. Take for example synchronized cached updates across datasets.
In other words, TFDQuery is an optimal "shortcut" for every day data
application programming.

Related

Disconnected Recordset / Dataset for Delphi

I have a 3-tier application written in Delphi 7 that uses a WebBroker-based SOAP server (standalone exe) with the SOAP client program communicating to via a HTTPRIO component.
This is not a DataSnap application. Instead the server sends data to the client as a disconnected ADO recordset in ADTG format that corresponds to the SQL string passed into the SOAP request. (The server first compresses it and converts it to Base64 for easy transmission back to the client as a "string" result.)
I use the returned recordset directly to iterate through its rows and fields (ie without needing to use a DataSet component) as this is something I am comfortable doing due to many years programming in Microsoft Access with VBA.
Luckily it is also possible to assign it directly to the Recordset property of a TADODataset component so that I can bind it to a QuickReport.
I now want to upgrade my client program to FireMonkey so that I can compile it for both Windows and OSX and suppose that I won't be able to use ADO Recordsets anymore, at least not in the Mac enviroment.
In preparation for conversion to FireMonkey, what would be the best data format that I can
a) disconnect and transmit "by hand",
b) load into a dataset component so that I can base reports on it, and
c) if possible, work through the data in code without having to first load it into a component?
This last item is important to me not just because I am used to working with data without using components, but also because I frequently request data in a thread in order to retrieve it in the background without locking up the user interface. As I understand it, components and threads don't go together.
Any advice about the best way forward?
I would go into transmitting some per-representation value, e.g. real XML or JSON, instead of some binary base-64 encoded content. And switch to a REST approach, which is much easier to implement on both sides than SOAP.
You can then fill a TDataSet from a JSON array of objects, or a JSON array of values (if you don't want to transmit the column names as object field names over the wire).
The advantage of JSON/REST is that you may be able to reuse the very same server logic for publishing its content to a HTML5 javascript client (mobile or web), or a third-party application or service.

Auto refresh a TDataSet / DBGrid

I'm developing a software that displays information in a DBGrid via a TSimpleDataSet (dbExpress components)
The software in question is used on 2 different computers by 2 different people.
They both view and edit the same information at different times.
I'm trying to figure out a way to automatically update the DBGrid (or rather, the DataSet, right?) on Computer B once Computer A makes a change to a row (edits something/whatever) and vice-versa.
Currently I've set up a TButton named Refresh that once clicked executes the following code:
procedure TForm2.actRefreshDataExecute(Sender: TObject);
begin
dbmodule.somenameDataSet.MergeChangeLog;
dbmodule.somenameDataSet.ApplyUpdates(-1);
dbmodule.somenameDataSet.Refresh;
dbmodule.somename1DataSet.MergeChangeLog;
dbmodule.somename1DataSet.ApplyUpdates(-1);
dbmodule.somename1DataSet.Refresh;
dbmodule.somename2DataSet.MergeChangeLog;
dbmodule.somename2DataSet.ApplyUpdates(-1);
dbmodule.somename2DataSet.Refresh;
dbmodule.somename3DataSet.MergeChangeLog;
dbmodule.somename3DataSet.ApplyUpdates(-1);
dbmodule.somename3DataSet.Refresh;
end;
This is fine and works as intended, once clicked.
I'd like an auto update feature for this, for example when Computer A edits information in a row, Computer B's DBGrid should update it's display accordingly, without the need to click the refresh button.
I figured I would use a TTimer and set it at a specific interval, on both software on both PC's.
My actual question is:
Is there a better way than a TTimer for this? If so, please elaborate.
Also, if the TTimer route is the way to go any further info you might find useful to state would be appreciated (pro's and con's and so on)
I'm using Rad Studio 10 Seattle and dbExpress components, the datasets connect to a MySQL database on my hosting where my website is.
Thanks!
Well, Ken White and Sertac Akyuz are certainly correct that using a server-originated notification to determine when to refresh your local dataset is preferable to continually re-reading all the data you are using from the server.
The problem AFAIK is that there is no Emba-supplied notification system which works with MySql. See this list of databases supported by FireDAC's Database Alerts:
http://docwiki.embarcadero.com/RADStudio/XE8/en/Database_Alerts_(FireDAC)
and note that it does not list MySql.
Luckily, I think there is a work-around which should be viable for a v. small system like yours currently is. As I understand it, you and your colleague's PCs are on a LAN and the MySql Server is outside your LAN and on the internet. In that situation, it doesn't need a round trip to the server for one of you to get a notification that the other has changed something in the database. Using an analogy akin to Ken's, you can, as it were, lean over the desk and say to your colleague "Hey, I've changed something, so you need to refresh your data."
A very low-tech way of implementing that would be to have somewhere on your LAN a resource that both of you can easily get at, which you can update when you make a change to the DB that means that the other of you should update your data from the server. One way to do that is to have a small, shared datafile with a number of records in it, one per server db table, which has some sort of timestamp or version-ID number which gets updated when you update the corresponding server table. Then, you can periodically check (poll) this datafile to see whether a given table has changed since you last checked; obviously, if it has, you then re-read the data you want from it from the server and update your local record of the info you read from the shared file.
You can update the shared file using handlers for the events of your Delphi client-side datasets.
There are a number of variations on this theme that I'm sure will be apparent to you; the implementational details really don't matter.
To update the shared file I'm talking about, you will need to lock it while writing to it. This answer:
How do I get the handle for locking a file in Delphi?
will show you how to do that.
Of course, the shared local resource doesn't have to be a data file. One alternative would be to use a Microsoft Message Queue service, which is sometimes used for this kind of thing, but has a steeper learning curve than a shared data file.
By the way, this kind of thing is far easier to do (at least on a small scale like you have) if you use 3-tier database access (e.g. using datasnap).
In a three tier system, only the middle tier (a Delphi datasnap server which you write, but it's not that hard) talks to the server, and the clients only talk to the middle tier. This makes it easy for the middle tier server to notify the other client(s) when one of them changes the db data.
The three-tier arrangement also helps minimise the security problems with accessing a database server via the internet, because you only need one secure connection to the server, not one per client. But that's straying a bit far from your immediate problem.
I hope all this is clear, if not, ask.
Just use a timer and make it refresh the dataset every 5 min. No big deal.
If the usage is not frequent then you can set it to fire every 10 or 15 min.
There is nothing wrong with the timer if it set on longer intervals.
Today's broadband connection's can easily handle the traffic so can Access.
If the table is not huge of course.

dbgrid without client dataset

I have a form with a dbgrid and an sqlquery component. I am trying to fill the dbgrid with the sqlquery. When I do I get the message, "Operation not allowed on Unidirectional dataset." I do NOT want to use a client data set, as I do not want a 'local' copy of the data, I would like to read and display the data directly. How can this be done?
The documentation clearly states (emphasis added):
TSQLQuery is a unidirectional dataset. Unlike other datasets, unidirectional datasets do not buffer multiple records in memory. Because of this, you can only navigate using the First and Next methods. There is no built-in editing support: you can only edit the data in an SQL query by explicitly creating an SQL UPDATE command or by connecting the dataset to a client dataset using a provider.
Because there's no buffering of multiple records, you can't move in any direction except forward, which means that the DBGrid could not display multiple rows or support scrolling.
(In fact, all of the DBExpress components are unidirectional, according to the documentation on Types of DBExpress DataSets.)
You'll have to either use a TClientDataSet or change from using DBExpress to some other method of accessing the data such as ADO instead, or display the data using something other than TDBGrid (like a TStringGrid) and implement your own internal storage. However, TClientDataSet doesn't have to be a disk file, if the amount of data you're retrieving is manageable in memory; all of the data can just stay there without being a "local copy" (an "in-memory dataset").

Is it possible to have a detached TpFIBDataset for FIBPlus?

I remember that when I was working with ADO for Delphi (dbGo) there was a possibility of creating a detached dataset. The idea was that I could read all the data which I wanted from database and then set the connection property to nil. That caused TADOQuery to work as a memory table. I could then use and pass TADOQueryas a TDataSet parameter to my other methods without worrying that I am keeping unnecessary connection or transaction opened.
I would like to have the same functionality when using FIBPlus library. Currently I need to copy data from TpFiBDataset to other structure and then close the data set. Otherwise to access the rows of dataset, transaction must stay opened, even if I have all the data fetched.
I could not achieve detached dataset functionality on my own, is this possible?
No. TpFIBDataSet could not work as a standalone dataset. You should use TpFIBClientDataSet (if you want to apply later updates to db) or any TInMemoryDataSet (just for local reading).

Suggestions for caching a dataset

I'd like to perform the following:
1) Open a dataset (using TMSQuery, SDAC DevArt component)
2) caching the content to disk (imagine a list of cutsomers)
3) the next time I need to Open the dataset I will first populate it with cached data, then I will just Refresh it by calling TMSQuery.RefreshQuick method.
In this way I plan to obtain a substantial improvement in speed, because I don't need to retrieve records that I already retrieved in previous application runs.
How can I obtain this caching? I have many datamodules with TMSQuery, so somehow I would like to have a global routine that checks that everytime I try to Open a TMSQuery, if that query is somehow tagged i will try to restore from cache, call RefreshQuick, in case this fails I will call Open.
Can you please suggest?
(I use Delphi 2009 and SDAC 4.80)
you can use the TClientDataSet and TDataSetProvider components for this, connecting the components in this way.
TMSQuery->TDataSetProvider->TClientDataSet
The TClientDataSet is a very good alternative to persist and retrieve data from an disk.
see these links for more info about the ClientDataset
Using the MIDAS ClientDataset as a replacement for cached updates
A Guide to Using the TClientDataSet in Delphi Database Applications
Effective ClientDataSets and the BriefCase Model
You can do 2 things:
Make descendant of the TMSQuery component and override the Open function
(you search all you datamodule .dfm and .pas files with TMSQuery and replace with TCachedTMSQuery)
Detour/hook the TMSQuery.Open (runtime patching)

Resources