Главная »Статьи »ADO: Связка Master-Detail для базы MS Access
ADO: Связка Master-Detail для базы MS Access

Человеку следует познавать, исходя из правила, что он далек от действительности.
Демокрит.

Применение локальных баз данных часто чрезвычайно эффективно и использование ADO для работы с базой MS Access представляется весьма неплохим решением. Организация связки Master-Detail с использованием компонентов TADOTable описана в литературе неоднократно, но, к сожалению, это решение не позволяет фильтровать подчиненную таблицу. Поэтому здесь рассматривается вариант с использованием компонентов TADODataSet и SQL-выражений.

Создадим приложение, демонстрирующее графики некоторых сигналов: синус, меандр, треугольник, пила. Наименования сигналов хранятся в главной таблице tbMaster, их значения — в подчиненной таблице tbDetail.

Структура таблиц
tbMaster
tbDetail
Имя
Тип
Ключ
MainID COUNTER PRIMARY KEY
Name TEXT(31)  
Имя
Тип
Ключ
DetailID COUNTER PRIMARY KEY
MainID INTEGER  
X FLOAT  
Y FLOAT  
SQL-инструкции создания таблиц
CREATE TABLE tbMaster (
  MainID COUNTER CONSTRAINT PrimaryKey PRIMARY KEY,
  [Name] TEXT(31)
)
CREATE TABLE tbDetail (
  DetailID COUNTER CONSTRAINT PrimaryKey PRIMARY KEY,
  MainID INTEGER,
  X FLOAT,
  Y FLOAT
)
Связь между таблицами
SQL-инструкция создания связи
ALTER TABLE tbDetail ADD CONSTRAINT MasterDetail FOREIGN KEY (MainID) REFERENCES tbMaster 
  ON UPDATE CASCADE ON DELETE CASCADE

 

Структура связей компонентов и значения их основных свойств
 

Примечания:

  1. Свойства MasterFields и IndexFieldNames компонента adstDetail (TADODataSet) здесь не используются для создания связки Master-Detail.
  2. Для организации связки Master-Detail служит предложение WHERE MainID = :MainID в SQL-инструкции CommandText компонента adstDetail. Также для этого настроен параметр Parameters[0], и значение DataSource.
  3. Имена полей счетчиков в таблицах (являющихся первичными ключами) специально заданы разными. Иначе, например если они имеют имя ID, то выражение выше принимает вид WHERE MainID = :ID, что вызывает неопределенность.
  4. Создание связи между таблицами и включение в это выражение инструкций ON UPDATE CASCADE ON DELETE CASCADE позволяет возложить на движок действия по удалению значений из подчиненной таблицы при удалении соответствующего значения из главной.

Практика

Главная форма приложения на этапе проектирования

Настраиваем компоненты в соответствии с рисунком выше.

Подключаем модули: Series, ComObj, ActiveX.

Задаем константы:

resourcestring
  rsConnStrFmt = 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=%s';

  rsCreateMasterTable = 'CREATE TABLE tbMaster (' + #13#10 +
    'MainID COUNTER CONSTRAINT PrimaryKey PRIMARY KEY,' + #13#10 +
    '[Name] TEXT(31)' + #13#10 +
    ')';

  rsCreateDetailTable = 'CREATE TABLE tbDetail (' + #13#10 +
    'DetailID COUNTER CONSTRAINT PrimaryKey PRIMARY KEY,' + #13#10 +
    'MainID INTEGER,' + #13#10 +
    'X FLOAT,' + #13#10 +
    'Y FLOAT' + #13#10 +
    ')';

  rsCreateConstraint = 'ALTER TABLE tbDetail ADD CONSTRAINT MasterDetail ' +
    'FOREIGN KEY (MainID) REFERENCES tbMaster ON UPDATE CASCADE ON DELETE CASCADE';

Функция создания файла новой базы данных:

function CreateAccessDatabase(AFileName: String): String;
var
  Catalog: OleVariant;
begin
  Result:= '';
  try
    Catalog:= CreateOleObject('ADOX.Catalog');
    Catalog.Create(Format(rsConnStrFmt, [AFileName]));
    VariantClear(Catalog);
  except
    on E: Exception do
      Result:= E.Message;
  end;
end;

Функция форматирования строки подключения:

function GetConnStr(AFileName: String): String;
begin
  Result:= Format(rsConnStrFmt, [AFileName]);
end;

Обработчик нажатия кнопки Create (создание файла новой базы данных, таблиц, добавление данных):

procedure TfrmMasterDetail.btnCreateClick(Sender: TObject);
begin
  SaveDlg.InitialDir:= ExtractFilePath(Application.ExeName);
  if not SaveDlg.Execute then Exit;
  // Если файл существует - стереть его сначала
  if FileExists(SaveDlg.FileName) then
    DeleteFile(SaveDlg.FileName);

  ADOConnect.Close;
  // Создаем файл базы
  CreateAccessDatabase(SaveDlg.FileName);
  // Создаем таблицы
  ADOConnect.ConnectionString:= GetConnStr(SaveDlg.FileName);
  ADOConnect.Execute(rsCreateMasterTable, cmdText);
  ADOConnect.Execute(rsCreateDetailTable, cmdText);
  ADOConnect.Execute(rsCreateConstraint, cmdText);
  // Открываем таблицы
  ADOConnect.Open;
  adstMaster.Open;
  adstDetail.Open;
  // Добавляем кривые
  adstDetail.DisableControls;
  adstMaster.DisableControls;
  try
    AddCurves;
  finally
    adstDetail.EnableControls;
    adstMaster.EnableControls;
  end;
  Caption:= Format('%s - %s', [Application.Title, SaveDlg.FileName]);
end;

Остальное см. прилагаемый пример.

Приложение на этапе выполнения

Вводить данные следует в поля Names, X и Y. Поля DetailID и MainID (для обеих таблиц) заполняются автоматически.

При обнаружении сбоев в процессе добавления данных с сообщением "Не удается найти строку для обновления..." следует в начало обработчика события OnDataChange обоих DataSource добавить:
if adstMaster.Active then
  adstMaster.Properties['Update Resync'].Value:= adResyncAll;
для adstMaster и
if adstDetail.Active then
  adstDetail.Properties['Update Resync'].Value:= adResyncAll;
для adstDetail.

К статье прилагаются файлы с исходным кодом на Delphi 2010.


Внимание! Запрещается воспроизведение данной статьи или ее части без согласования с автором. Если вы желаете разместить эту статью на своем сайте или издать в печатном виде, свяжитесь с автором.
Автор статьи: Вершинин И.В.

 
Используются технологии uCoz