Загадочный параметрический SQL-запрос с объединениями + пустые поля TClientDataSet

Иногда кажется, что даже очевидные вещи идут не так. Есть много очевидного и много того, что может определенно пойти не так при работе с компонентами SQL, и даже больше, когда они представляют собой всего лишь кольцо целой цепочки SQLQuery => Provider => ClientDataSet => DataSource => DataControl.

Сегодняшний пример такой невероятно глупый, но расточитель времени.

Как это воспроизвести:

Отбросьте TZQuery (ZeosLib) с помощью простого параметрического соединения в его SQL, например:

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

Конечно, у нас есть один параметр :language_id.

Затем удалите связанный с ним TDataSetProvider, затем TClientDataSet, затем TDataSource и, наконец, TDBGrid, каждый из которых связан с предыдущим. Наконец, отбросьте TDBNavigator и тоже свяжите его.

Наконец, добавьте поля в TClientDataSet, подписи и т. Д.

При запуске программы мы назначаем параметр компоненту TZQuery примерно так:

qryProduct.Params.ParamByName('language_id').AsInteger := 2;

Где 2 - это жестко запрограммированное демонстрационное значение (в реальном приложении оно определяется путем запроса текущего используемого языка Windows).

Теперь запустите приложение: отлично!

Теперь нажмите «Обновить» на TDBNavigator.

Либо вы получите неприятный: «Нарушение ключа», либо совершенно пустой name столбец в TDBGrid.

Почему?


person Dario Fumagalli    schedule 04.01.2014    source источник


Ответы (1)


Решение такое простое, но не так очевидное.

Глядя на сгенерированный SQL-запрос обновления (через TZSQLMonitor) в объединенной таблице, очевидно, что параметр не работает:

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

Бит параметра:

pd.language_id = NULL

Быстрый взгляд на компонент ClientDataSet показывает, что параметр name был получен из компонента запроса. Но не значение.

Теперь я не знаю, является ли это проблемой из-за того, что компонент TZQuery не отправляет значение параметра во время выполнения, или если функциональность просто не реализована ClientDataSet, но генерируемые им ошибки далеко не сразу очевидны.

Исправление, конечно же, состоит в том, чтобы вручную установить значение параметра как в TZQuery, так и в компонентах TClientDataSet до того, как Active будет установлено в true.

person Dario Fumagalli    schedule 04.01.2014
comment
Достаточно установить параметр только в ClientDataSet. Хорошее место для этого - событие BeforeOpen. - person Uwe Raabe; 04.01.2014
comment
Большая проблема с дизайном SQL в Delphi заключается в необходимости выполнять всю трудоемкую вещь param.parambyname (). Asinteger и т. Д. Для каждого параметра в первую очередь. Нужно просто иметь возможность передать строку запроса и переменное количество значений / переменных за один вызов функции. Они совершают ту же ошибку с новой библиотекой REST. - person alcalde; 04.01.2014
comment
@UweRaabe У вас есть ссылка, по которой показан пример? Я нашел docwiki.embarcadero.com/RADStudio/XE5/en/ но, похоже, это не показывает, о чем я говорю. - person alcalde; 06.01.2014
comment
@alcalde, на этой странице действительно показан пример: FDConnection1.ExecSQL('insert into testtab values (:id, :name)', [1, 'FireDAC']); - person Uwe Raabe; 06.01.2014