Mystery Parametric SQL Query with joins + TClientDataSet blank fields
Some times even obvious things seem to go wrong. There's a lot of obvious and a lot that can go definitely wrong when dealing with SQL components and even more when they are just a ring of a whole SQLQuery => Provider => ClientDataSet => DataSource => DataControl chain.
Today's example is so incredibly dumb, yet a time waster.
How to replicate it:
Drop a TZQuery (ZeosLib) with a simple parametric join in its SQL, example:
SELECT pr.product_id AS product_id, pr.model AS model, pd.name AS name, pr.image AS image, pr.status AS status, pr.date_added AS date_added, pr.date_modified AS date_modified FROM oc_product pr LEFT JOIN oc_product_description pd ON pr.product_id = pd.product_id AND pd.language_id = :language_id WHERE pr.status = 1 ORDER BY pd.name
Of course we have one parameter, :language_id.
Then drop a TDataSetProvider linked to it, then a TClientDataSet, then a TDataSource and finally a TDBGrid, all linked each to the previous. Finally drop a TDBNavigator and link it as well.
Finally, add fields in the TClientDataSet, captions etc.
At program startup we assign the parameter to the TZQuery component with something like:
qryProduct.Params.ParamByName('language_id').AsInteger := 2;
Where 2 is a demo hardcoded value (in the real application it is determined by querying the Windows current language being used).
Now run the the application: perfect!
Now press "Refresh" on the TDBNavigator.
Either you get a nasty: "Key Violation" or a perfectly blank name column in the TDBGrid.
The solution is so simple yet not so immediately evident.
Looking at the generated refresh SQL (via a TZSQLMonitor) the joined table it's evident the parameter is not working:
2014-01-04 11:19:38 cat: Execute, proto: mysql-5, msg: SELECT pr.product_id AS product_id, pr.model AS model, pd.name AS name, pr.image AS image, pr.status AS status, pr.date_added AS date_added, pr.date_modified AS date_modified FROM oc_product pr LEFT JOIN oc_product_description pd ON pr.product_id = pd.product_id AND pd.language_id = NULL WHERE pr.status = 1 ORDER BY pd.name
The parameter bit is:
pd.language_id = NULL
A quick glance to the ClientDataSet component would reveal that the parameter name has been captured from the query component. But not the value.
Now, I don't know if this is a problem with the TZQuery component not sending the parameter value at runtime or if the functionality is just not implemented by the ClientDataSet but the errors it generates are far from immediately evident.
The fix, of course, is to manually set the parameter value both in the TZQuery and in the TClientDataSet components before Active is set to true.