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.

Why?

Answers


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.


Need Your Help

Jquery has() vs parents()

jquery javascript-events

I want to check if event target is descendant of specific wrapper.

Freezes in multithreading app

c# multithreading winforms thread-safety

I am trying to make a WinForms multi-threading app, which endlessly generates exceptions in two different threads.