Можно ли создать таблицу MS Access в Delphi (например, FireDAC) из полей определения существующей таблицы без использования SQL? - PullRequest
0 голосов
/ 13 апреля 2019

Я хотел создать новый файл MDB, содержащий таблицы, основанные на структуре ранее существующих таблиц.Я знал, что могу использовать newTable.FieldDefs.Add() для воссоздания полей одно за другим в цикле.Но поскольку уже существует старая таблица, полностью заполненная правильным FieldDefs, это казалось ужасно не элегантным.Я искал решение с одним утверждением!

Я обнаружил, что newTable.FieldDefs.Assign(oldTable.FieldDefs) будет компилироваться (и запускаться) без ошибок, но он оставил newTable с нулевыми полями.Это заставило меня ошибочно заключить, что я не понял функцию этого утверждения.(Позже я обнаружил, что сбой произошел только тогда, когда oldTable.open не произошло, чего не могло произойти, если база данных была недоступна, даже если FieldDefs был сделан постоянным и был четко виден в Инспекторе объектов)

Вот мой оригинальный код после некоторой проверки:

procedure TForm2.Button1Click(Sender: TObject);
var
  fname: string;
  Table: TFDTable;
  FDConn: TFDConnection;

begin
  fname := 'C:\ProgramData\mymdb.mdb';
  if FileExists(fname) then DeleteFile(fname);

  { Make new file for Table }
  FDMSAccessService1.Database := fname;
  FDMSAccessService1.DBVersion := avAccess2000;
  FDMSAccessService1.CreateDB;

  { Connect to new file }
  FDConn := TFDConnection.Create(nil);
  FDConn.Params.Database := fname;
  FDConn.Params.DriverID := 'MSAcc';
  FDConn.Connected := true;

  { Set up new Table using old table's structure }
  Table := TFDTable.Create(nil);
  try
    { ADOTable1 has been linked to an existing table in a prior
      database with Field Defs made Persistent using the Fields
      Editor in the Object Inspector. That database will not be
      available in my actual use scenario }
    try
      ADOTable1.open; // Throws exception when database file not found
    except
    end;
    Table.Connection := FDConn;
    { specify table name }
    Table.TableName := ADOTable1.TableName;
    Table.FieldDefs.Assign(ADOTable1.FieldDefs);  // No errors reported
    ShowMessageFmt('New Table %s has %d fields',[Table.TableName,
      Table.FieldDefs.Count]);
    { Reports correct TableName but "0 fields" with table not open
      (i.e. file not found). Reports "23 fields" with table open }
    { Set Table definition into new mdb file }
    Table.CreateTable(False); // Throws exception when 0 fields found
  finally
    Table.Free;
  end;

end;

Оказалось, что решение было использовать ClientDataSet, первоначально связанный с той же старой базой данных, вместо ADOTable.См. Рабочее решение ниже в моем ответе.

Редактировать: Последнее замечание.Я надеялся использовать этот подход FireDAC, как указано здесь, чтобы обойти отсутствие метода TADOTable.CreateTable.Увы, хотя «решения» выше и ниже действительно работают для создания нового TADOTable, определения полей этой таблицы не являются точными копиями исходной таблицы.Это может быть сочетание множества опций TFDTable, которые могут обойти это, но я не смог обнаружить его, поэтому я вернулся к созданию таблиц ADO с SQL.

1 Ответ

1 голос
/ 13 апреля 2019

Благодаря указателю @Ken White (к сожалению, удаленному) я теперь думаю, что у меня есть решение моего первоначального вопроса о клонировании определений полей из старой таблицы во вновь созданную базу данных.Моя первоначальная проблема проистекала из того факта, что функция FieldDefs для таблицы, очевидно, не возвращает фактические сохраненные данные поля, если таблица не «открыта» (т. Е. Подключена к соответствующей базе данных).Поскольку в моем сценарии использования не было действительной базы данных, я не мог «открыть» таблицу.Однако ClientDataSets имеет дополнительную опцию «StoreDefs» наряду с опциями редактора «Выбор параметров» и «Назначение локальных данных».После сохранения этих настроек ClientDataSet отображает свои свойства FieldDefs, не будучи «открытыми».Используя этот подход, кажется, что я могу клонировать сохраненные поля defs в новую таблицу, не нуждаясь в действующей базе данных для их чтения.Еще раз спасибо, Кен, ты сохранил много моих оставшихся волос!Я уверен, что Embarcadero лучше справился бы с рационализацией своих справочных файлов.Они удалили BDE из установки Rio по умолчанию, в то же время указав в своем файле справки обсуждение создания таблиц Access с его типом TTable как способа создания новых таблиц, а затем никогда не указывали на эквивалентные возможности в FireDAC (или где-либо еще), которые они продолжают использовать.служба поддержки.Я потратил много времени из-за этого «недосмотра»!

Вот мой рабочий код после подсказки Кена:

procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
fname: string;
Table: TFDTable;
FDConn: TFDConnection;

begin
  fname := 'C:\ProgramData\mymdb.mdb';
  if FileExists(fname) then DeleteFile(fname);
  FDMSAccessService1.Database := fname;
  FDMSAccessService1.DBVersion := avAccess2000;
  FDMSAccessService1.CreateDB;

  FDConn := TFDConnection.Create(nil);
  FDConn.Params.Database := fname;
  FDConn.Params.DriverID := 'MSAcc';
  FDConn.Connected := true;

  Table := TFDTable.Create(nil);
  try
    Table.Connection := FDConn;
    { specify table name }
    Table.TableName := 'ATable';
    { The existingClientDataSet has been linked to a table in the
      prior, no longer valid, database using StoreDefs, Fetch Params,
      and Assign Local Data in the Object Inspector }
    Table.FieldDefs.Assign(existingClientDataSet.FieldDefs);
    ShowMessageFmt('New Table has %d fields', [Table.FieldDefs.Count]);
    Table.CreateTable(False);
  finally
    Table.Free;
  end;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...