Теперь можно внести некоторое разнообразие в структуру базы данных testdbExpress и добавить еще одну таблицу, SQL-скрипт создания которой ниже.
CREATE TABLE orders ( OrderNo INT (4) NOT NULL PRIMARY KEY, CustNo INT (4) NOT NULL, Name VARCHAR (24), Price DOUBLE (8,2))
Подобные SQL-инструкции могут быть выполнены с помощью метода TConnection.ExecuteDirect:
procedure TMainForm.Button1Click (Sender: TObject); var SQLScript:String; begin SQLScript:= 'CREATE TABLE orders (OrderNo INT (4) NOT NULL PRIMARY KEY,' +' CustNo INT (4) NOT NULL, Name VARCHAR (24), Price DOUBLE (8,2))'; SQLConnection.ExecuteDirect (SQLScript); end;
Таким образом, нам больше не нужны дополнительные средства для изменения состава таблиц базы данных MySQL - у нас появилась возможность менять ее структуру "на лету", в процессе выполнения приложения. Для того чтобы еще более упростить создание требуемой структуры таблиц в тестовой базе данных, я поместил в демонстрационный проект кнопку "Создать таблицы" - ее нажатие приводит к выполнению команд SQL, создающих эти структуры данных.
Таблица orders позволяет просматривать и модифицировать информацию не только о клиентах, но и об их заказах. Организация связи между этими таблицами по ключевому полю достаточно очевидна. Пользователю предоставляется возможность просмотра детальной информации по заказам при выборе клиента. Как известно, BDE позволяет достаточно просто организовать такого рода связи. DbExpress предоставляет, как минимум, два способа решения этой задачи.
Проще всего будет загрузить содержимое компонентов TSQLTable, соответствующих таблицам customer и orders, в компоненты ClientDataSet (посредством TDataSetProvider). Затем можно организовать связи по ключевому полю между этими наборами данных, используя штатные средства соответствующих компонентов. Однако содержимое обеих MySQL-таблиц размещается в оперативной памяти клиентского ПК, что чревато нехваткой ресурсов в случае больших объемов данных. С другой стороны, это обстоятельство обеспечивает высокую скорость работы. В приведенном ниже листинге описаны ключевые параметры создаваемых при использовании такого подхода компонентов.
object SQLTableOrders: TSQLTable SQLConnection = SQLConnection TableName = 'orders' end object dspOrders: TDataSetProvider DataSet = SQLTableOrders end object cdsOrders: TClientDataSet IndexFieldNames = 'CustNo' MasterFields = 'CustNo' MasterSource = dsCustomer ProviderName = 'dspOrders' end object dsOrders: TDataSource DataSet = cdsOrders end
В данном случае связь между таблицами организована по ключевому полю на уровне компонентов ClientDataSet. Но есть альтернативный подход, реализуемый двумя способами. Речь идет о формировании детализирующего набора данных по факту перехода с одной записи на другую в master-компоненте. Таким образом мы сокращаем расход оперативной памяти за счет скорости работы. Реализовать такой механизм можно с помощью как TSQLTable, так и TSQLQuery. В первом случае используется связь между главным и подчиненным наборами данных по полям MasterSource и MasterFields:
object SQLTableOrdersByCustomer: TSQLTable IndexFieldNames = 'CustNo' MasterFields = 'CustNo' MasterSource = dsCustomer SQLConnection = SQLConnection TableName = 'orders' end object dspOrdersByCustomer: TDataSetProvider DataSet = SQLTableOrdersByCustomer end object cdsOrdersbyCustomer: TClientDataSet ProviderName = 'dspOrdersByCustomer' end object dsOrdersbyCustomer: TDataSource DataSet = cdsOrdersbyCustomer end
Во втором случае применяется параметр, передаваемый в SQL-запрос:
object SQLQueryOrders: TSQLQuery DataSource = dsCustomer Params = < item DataType = ftInteger Name = 'custno' ParamType = ptInput end> SQL.Strings = ( 'select * from orders where (orders.custno =:custno)') SQLConnection = SQLConnection end object dspQOrders: TDataSetProvider DataSet = SQLQueryOrders end object cdsQOrders: TClientDataSet ProviderName = 'dspQOrders' end object dsQOrders: TDataSource DataSet = cdsQOrders end
Оба способа требуют явного указания обработчика события OnDataChanged компонента dsCustomerDataChange:
procedure TMainForm.dsCustomerDataChange (Sender: TObject; Field: TField); begin if cdsOrdersByCustomer.Active then cdsOrdersByCustomer.Refresh; if cdsQOrders.Active then cdsQOrders.Refresh; end;