在資料庫應用中,大量資料輸入時,許多欄位(如部門、職稱、聯絡電話、郵遞區號)在連續筆記錄間重複率極高。FoxPro 時代的 SET CARRY ON 允許使用者將上一筆記錄的欄位值「帶入」新記錄,僅修改差異即可存檔,大幅提升輸入效率。本文示範如何在 Delphi 7 + BDE/ADO + TTable/TDataSet 架構下,以動態 Variant 陣列實作同等效果。
MyDynamic_Record: Variant(VarArrayCreate 建立,索引 0~FieldCount-1)。MyRecordScatTo)。MyRecordGatherFrom)。My_TablePostError)。type
TForm1 = class(TForm)
Table1: TTable;
CheckBox1: TCheckBox; { 啟用「攜帶」 }
Button1: TButton; { 插入新記錄 }
Button2: TButton; { 關閉 }
DBGrid1: TDBGrid;
procedure FormCreate(Sender: TObject);
procedure Table1BeforeInsert(DataSet: TDataSet);
procedure Table1AfterInsert(DataSet: TDataSet);
procedure Table1PostError(DataSet: TDataSet; E: EDatabaseError; var Action: TDataAction);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
MyDynamic_Record: Variant;
function MyRecordScatTo(DataSet0: TDataSet; var MyDynamic_Record0: Variant): Boolean;
function MyRecordGatherFrom(DataSet0: TDataSet; var MyDynamic_Record0: Variant): Boolean;
procedure My_TablePostError(DataSet: TDataSet; E: EDatabaseError; var Action: TDataAction; sFieldName: string);
end;
function TForm1.MyRecordScatTo(DataSet0: TDataSet; var MyDynamic_Record0: Variant): Boolean;
var
i: Integer;
begin
Result := False;
try
if not DataSet0.Active then
begin
MessageDlg('當前無有效記錄,無法使用「攜帶」!', mtError, [mbOK], 0);
Exit;
end;
MyDynamic_Record0 := VarArrayCreate([0, DataSet0.FieldCount - 1], varVariant);
with DataSet0 do
for i := 0 to FieldCount - 1 do
if Fields[i].DataType <> ftMemo then { 排除 Blob/Memo }
MyDynamic_Record0[i] := Fields[i].Value;
Result := True;
except
Result := False;
end;
end;
procedure TForm1.Table1BeforeInsert(DataSet: TDataSet);
begin
if CheckBox1.Checked then
if not MyRecordScatTo(Table1, MyDynamic_Record) then
CheckBox1.Checked := False; { 失敗自動關閉功能 }
end;
function TForm1.MyRecordGatherFrom(DataSet0: TDataSet; var MyDynamic_Record0: Variant): Boolean;
var
i: Integer;
begin
Result := True;
try
with DataSet0 do
for i := 0 to FieldCount - 1 do
if Fields[i].DataType <> ftMemo then
Fields[i].Value := MyDynamic_Record0[i];
except
Result := False;
end;
end;
procedure TForm1.Table1AfterInsert(DataSet: TDataSet);
begin
if CheckBox1.Checked then
MyRecordGatherFrom(Table1, MyDynamic_Record);
end;
procedure TForm1.My_TablePostError(DataSet: TDataSet; E: EDatabaseError; var Action: TDataAction; sFieldName: string);
var
sMes: string;
begin
MessageBeep(0);
sMes := '當前記錄資料錯誤,請修正後再存檔。';
if Pos(UpperCase('key violation'), UpperCase(E.Message)) > 0 then
sMes := Format('「%s」值重複,請修改!', [sFieldName]);
if Pos(UpperCase('must have a value'), UpperCase(E.Message)) > 0 then
sMes := '必填欄位不可為空!';
sMes := sMes + #13#10 + '(按 Esc 取消編輯)';
MessageDlg(sMes, mtError, [mbOK], 0);
Action := daAbort;
end;
procedure TForm1.Table1PostError(DataSet: TDataSet; E: EDatabaseError; var Action: TDataAction);
begin
My_TablePostError(DataSet, E, Action, '編號'); { 傳入主鍵欄位名 }
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Table1.Insert;
if DBGrid1.CanFocus then DBGrid1.SetFocus;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Table1.Refresh;
Table1.Close;
Close;
end;
Table1.Insert。
TTable 改為泛型 TDataSet 參數,即可套用於 TADOTable、TClientDataSet、TFDTable 等。TStringList 讓使用者勾選哪些欄位「可攜帶」,避免主鍵、流水號等不可重複欄位被複製。TClientDataSet 的 CloneCursor 或 CopyDataSet 實現一次複製多筆。「攜帶」功能雖屬小技巧,卻能大幅降低資料輸入人員的重複勞動。以 Variant 動態陣列為媒介,不綁定特定資料集元件、自動適應欄位數量與型別,是 Delphi 快速開發資料庫應用的經典範例。移植至 FireDAC、UniDAC 等現代架構時,僅需替換資料集基類與欄位型別判斷即可無縫復用。