dfdquery.sql.text := 'select a.*, b.* from a left join b on a.id = b.id
fdqery.cacheupdate := true; //这样可更方便回退多表错误
fdquery.updateoptions.updatenobasefields := true; //副表字段可以编辑
fdquery.updateobject := TFdUpdateSqL; 或者fdquery.onupdaterecord事件。
以下是一个示例:
procedure TForm2.Button3Click(Sender: TObject);
varne:Integer;
beginqrym.CheckBrowseMode;if not qrym.UpdatesPending thenExit;qrym.UpdateObject := FDUpdateSQL1;ne := qrym.ApplyUpdates(0);qrym.UpdateObject := FDUpdateSQL2;ne := ne + qrym.ApplyUpdates(0);if ne = 0 thenbeginqrym.CommitUpdates;ShowMessage('ok');end else ShowMessage('出错');
end;
采用事件方式
procedure TfrmCachedUpdates.qrProductsUpdateRecord(ASender: TDataSet;ARequest: TFDUpdateRequest; var AAction: TFDErrorAction;AOptions: TFDUpdateRowOptions);
beginusProducts.ConnectionName := qrProducts.ConnectionName;usProducts.DataSet := qrProducts;usProducts.Apply(ARequest, AAction, AOptions);usCategories.ConnectionName := qrProducts.ConnectionName;usCategories.DataSet := qrProducts;usCategories.Apply(ARequest, AAction, AOptions);AAction := eaApplied;
end;
也可以采用另一种方式:
var qrya, qryb:TFDuqey;
qrya.sql = 'select * from a';
qryb.sql := 'select * from b';qry.onupdaterecord事件中执行qry复制记录到qrya中,qry 复制记录到qryb中,
分别更新qrya,qryb.
qry执行applyupdate, commitupdate.
附上生成SQL命令的方法
{*******************************************************}
{ }
{ Delphi FireDAC Framework }
{ FireDAC TFDUpdateSQL editor form }
{ }
{ Copyright(c) 2004-2021 Embarcadero Technologies, Inc. }
{ All rights reserved }
{ }
{*******************************************************}
{$I FireDAC.inc}unit FireDAC.VCLUI.USEdit;interfaceuses
{$IFDEF MSWINDOWS}Winapi.Messages, Winapi.Windows,
{$ENDIF}System.SysUtils, System.Classes, Vcl.Graphics, Vcl.Forms, Vcl.ComCtrls,Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Controls,FireDAC.Stan.Intf, FireDAC.Stan.Option,FireDAC.Comp.Client,FireDAC.VCLUI.OptsBase, FireDAC.VCLUI.UpdateOptions, FireDAC.VCLUI.Controls,FireDAC.VCLUI.Memo;typeTfrmFDGUIxFormsUSEdit = class(TfrmFDGUIxFormsOptsBase)pcMain: TPageControl;tsGenerate: TTabSheet;Label1: TLabel;cbxTableName: TComboBox;btnDSDefaults: TButton;btnGenSQL: TButton;btnServerInfo: TButton;GroupBox2: TLabel;lbKeyFields: TListBox;GroupBox3: TLabel;lbUpdateFields: TListBox;GroupBox4: TLabel;lbRefetchFields: TListBox;tsSQL: TTabSheet;tsOptions: TTabSheet;ptreeOptions: TFDGUIxFormsPanelTree;GroupBox5: TPanel;cbQuoteTabName: TCheckBox;cbQuoteColName: TCheckBox;frmUpdateOptions: TfrmFDGUIxFormsUpdateOptions;Bevel4: TBevel;Bevel1: TBevel;Bevel5: TBevel;pcSQL: TPageControl;tsInsert: TTabSheet;tsModify: TTabSheet;tsDelete: TTabSheet;tsLock: TTabSheet;tsUnlock: TTabSheet;tsFetchRow: TTabSheet;procedure cbxTableNameDropDown(Sender: TObject);procedure btnServerInfoClick(Sender: TObject);procedure btnDSDefaultsClick(Sender: TObject);procedure btnGenSQLClick(Sender: TObject);procedure cbxTableNameExit(Sender: TObject);procedure FormCreate(Sender: TObject);procedure mmSQLExit(Sender: TObject);procedure mmSQLKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);procedure cbxTableNameChange(Sender: TObject);procedure cbxTableNameClick(Sender: TObject);privateFConnection: TFDCustomConnection;FDataSet: TFDAdaptedDataSet;FUpdateSQL: TFDUpdateSQL;FOpts: IFDStanOptions;procedure UpdateExistSQLs;procedure GenCommands;function GetSQL(AIndex: Integer): TFDGUIxFormsMemo;function ExecuteBase(AUpdSQL: TFDUpdateSQL; const ACaption: String): Boolean;function UseField(const AFieldName: String): Boolean;publicclass function Execute(AUpdSQL: TFDUpdateSQL; const ACaption: String): Boolean;end;varfrmFDGUIxFormsUSEdit: TfrmFDGUIxFormsUSEdit;implementation{$R *.dfm}usesSystem.UITypes,Vcl.Dialogs, Data.DB,FireDAC.Stan.ResStrs, FireDAC.Stan.Util,FireDAC.DatS,FireDAC.Phys.Intf;{ --------------------------------------------------------------------------- }
procedure TfrmFDGUIxFormsUSEdit.FormCreate(Sender: TObject);
vari: Integer;oSQL: TFDGUIxFormsMemo;
beginfor i := 0 to 5 do beginoSQL := TFDGUIxFormsMemo.Create(Self);oSQL.Parent := pcSQL.Pages[i];oSQL.Left := 5;oSQL.Top := 5;oSQL.Width := oSQL.Parent.ClientWidth - 12;oSQL.Height := oSQL.Parent.ClientHeight - 12;oSQL.Anchors := [akLeft, akTop, akRight, akBottom];oSQL.Align := alNone;oSQL.Visible := True;oSQL.OnExit := mmSQLExit;oSQL.OnKeyDown := mmSQLKeyDown;end;
end;{ --------------------------------------------------------------------------- }
function TfrmFDGUIxFormsUSEdit.GetSQL(AIndex: Integer): TFDGUIxFormsMemo;
beginResult := pcSQL.Pages[AIndex].Controls[0] as TFDGUIxFormsMemo;
end;{ --------------------------------------------------------------------------- }
function TfrmFDGUIxFormsUSEdit.UseField(const AFieldName: String): Boolean;
beginResult := (FDataSet = nil) or (FDataSet.FieldCount = 0) or(FDataSet.FindField(AFieldName) <> nil);
end;{ --------------------------------------------------------------------------- }
procedure TfrmFDGUIxFormsUSEdit.cbxTableNameDropDown(Sender: TObject);
begin
{$IFDEF MSWINDOWS}cbxTableName.Perform(CB_SETDROPPEDWIDTH, Width div 2, 0);
{$ENDIF}if cbxTableName.Items.Count = 0 thentryFConnection.GetTableNames('', '', '', cbxTableName.Items, [osMy]);exceptcbxTableName.DroppedDown := False;raise;end;
end;{ --------------------------------------------------------------------------- }
procedure TfrmFDGUIxFormsUSEdit.cbxTableNameChange(Sender: TObject);
beginbtnGenSQL.Enabled := (cbxTableName.Text <> '');
end;{ --------------------------------------------------------------------------- }
procedure TfrmFDGUIxFormsUSEdit.cbxTableNameClick(Sender: TObject);
beginif cbxTableName.Text <> '' then beginbtnServerInfoClick(nil);btnDSDefaultsClick(nil);end;
end;{ --------------------------------------------------------------------------- }
procedure TfrmFDGUIxFormsUSEdit.cbxTableNameExit(Sender: TObject);
begin
end;{ --------------------------------------------------------------------------- }
procedure TfrmFDGUIxFormsUSEdit.btnServerInfoClick(Sender: TObject);
varoConnMeta: IFDPhysConnectionMetadata;oView: TFDDatSView;sName: String;eAttrs: TFDDataAttributes;i, j: Integer;
beginFConnection.ConnectionIntf.CreateMetadata(oConnMeta);oView := oConnMeta.GetTableFields('', '', cbxTableName.Text, '');trylbKeyFields.Items.Clear;lbUpdateFields.Items.Clear;lbRefetchFields.Items.Clear;for i := 0 to oView.Rows.Count - 1 do beginsName := oView.Rows[i].GetData('COLUMN_NAME');if sName = '' thensName := '_' + IntToStr(lbKeyFields.Items.Count);lbKeyFields.Items.Add(sName);lbUpdateFields.Items.Add(sName);lbRefetchFields.Items.Add(sName);end;for i := 0 to oView.Rows.Count - 1 do beginsName := oView.Rows[i].GetData('COLUMN_NAME');if UseField(sName) then beginj := oView.Rows[i].GetData('COLUMN_ATTRIBUTES');eAttrs := TFDDataAttributes(Pointer(@J)^);if (sName <> '') and (eAttrs * [caCalculated, caInternal, caUnnamed] = []) thenlbUpdateFields.Selected[i] := True;if eAttrs * [caAutoInc, caROWID, caDefault, caRowVersion, caCalculated, caVolatile] <> [] thenlbRefetchFields.Selected[i] := True;end;end;finallyFDFree(oView);end;oView := oConnMeta.GetTablePrimaryKeyFields('', '', cbxTableName.Text, '');tryfor i := 0 to oView.Rows.Count - 1 do beginsName := oConnMeta.UnQuoteObjName(oView.Rows[i].GetData('COLUMN_NAME'));if UseField(sName) then beginj := lbKeyFields.Items.IndexOf(sName);if j <> -1 thenlbKeyFields.Selected[j] := True;end;end;finallyFDFree(oView);end;
end;{ --------------------------------------------------------------------------- }
procedure TfrmFDGUIxFormsUSEdit.btnDSDefaultsClick(Sender: TObject);
varoConnMeta: IFDPhysConnectionMetadata;i, j: Integer;oFld: TField;sFldName: String;
beginif FDataSet = nil thenExit;if (FConnection <> nil) and (FConnection.ConnectionIntf <> nil) thenFConnection.ConnectionIntf.CreateMetadata(oConnMeta)elseoConnMeta := nil;if FDataSet.FieldCount <> 0 then beginfor i := 0 to lbKeyFields.Items.Count - 1 dolbKeyFields.Selected[i] := False;for i := 0 to lbUpdateFields.Items.Count - 1 dolbUpdateFields.Selected[i] := False;for i := 0 to lbRefetchFields.Items.Count - 1 dolbRefetchFields.Selected[i] := False;for i := 0 to FDataSet.FieldCount - 1 do beginoFld := FDataSet.Fields[i];if oFld.Origin = '' thensFldName := oFld.FieldNameelsesFldName := oFld.Origin;if oConnMeta <> nil thensFldName := oConnMeta.UnQuoteObjName(sFldName);j := lbKeyFields.Items.IndexOf(sFldName);if j <> -1 then beginlbKeyFields.Selected[j] := pfInKey in oFld.ProviderFlags;lbUpdateFields.Selected[j] := pfInUpdate in oFld.ProviderFlags;lbRefetchFields.Selected[j] := (oFld.AutoGenerateValue <> TAutoRefreshFlag.arNone);end;end;end;if FDataSet.Adapter <> nil then beginFOpts.UpdateOptions.RestoreDefaults;frmUpdateOptions.LoadFrom(FOpts.UpdateOptions);end;
end;{ --------------------------------------------------------------------------- }
procedure TfrmFDGUIxFormsUSEdit.btnGenSQLClick(Sender: TObject);
beginfrmUpdateOptions.SaveTo(FOpts.UpdateOptions);GenCommands;UpdateExistSQLs;pcMain.ActivePage := tsSQL;
end;{ --------------------------------------------------------------------------- }
procedure TfrmFDGUIxFormsUSEdit.mmSQLExit(Sender: TObject);
beginUpdateExistSQLs;
end;{ --------------------------------------------------------------------------- }
procedure TfrmFDGUIxFormsUSEdit.mmSQLKeyDown(Sender: TObject; var Key: Word;Shift: TShiftState);
beginif (Shift = [ssCtrl]) and ((Key = Ord('A')) or (Key = Ord('a'))) then beginTFDGUIxFormsMemo(Sender).SelectAll;Key := 0;endelse if Key = VK_ESCAPE then beginModalResult := mrCancel;Key := 0;end;
end;{ --------------------------------------------------------------------------- }
procedure TfrmFDGUIxFormsUSEdit.UpdateExistSQLs;
vari: Integer;s: String;
beginfor i := 0 to 5 do begins := pcSQL.Pages[i].Caption;if GetSQL(i).Lines.Count > 0 then beginif Pos('*', s) = 0 thens := s + ' *';endelse beginif Pos('*', s) <> 0 thens := Copy(s, 1, Pos('*', s) - 1);end;pcSQL.Pages[i].Caption := s;end;
end;{ --------------------------------------------------------------------------- }
procedure TfrmFDGUIxFormsUSEdit.GenCommands;
vari, j: Integer;oCmdGen: IFDPhysCommandGenerator;oTab: TFDDatSTable;oCol: TFDDatSColumn;oFld: TField;sFldName: String;oCmd: IFDPhysCommand;oOpts: IFDStanOptions;
beginoTab := TFDDatSTable.Create;FConnection.ConnectionIntf.CreateCommand(oCmd);tryoOpts := oCmd.Options;oOpts.UpdateOptions.Assign(FOpts.UpdateOptions);oOpts.FetchOptions.RowsetSize := 0;oOpts.FetchOptions.Mode := fmManual;oOpts.FetchOptions.Items := oOpts.FetchOptions.Items + [fiMeta];// define tableoCmd.Prepare('select * from ' + cbxTableName.Text);oCmd.Define(oTab);oTab.SourceName := cbxTableName.Text;// Include into Where only fields existing in dataset and// having pfInWhere in ProviderFlagsif FDataSet <> nil thenfor i := 0 to oTab.Columns.Count - 1 do beginoCol := oTab.Columns[i];if coInWhere in oCol.Options then beginoCol.Options := oCol.Options - [coInWhere];for j := 0 to FDataSet.FieldCount - 1 do beginoFld := FDataSet.Fields[j];if oFld.Origin = '' thensFldName := oFld.FieldNameelsesFldName := oFld.Origin;if (AnsiCompareText(oCol.Name, sFldName) = 0) and(pfInWhere in oFld.ProviderFlags) thenoCol.Options := oCol.Options + [coInWhere];end;end;end;// Include into where selected Key fieldsfor i := 0 to lbKeyFields.Items.Count - 1 do beginoCol := oTab.Columns.ColumnByName(lbKeyFields.Items[i]);if lbKeyFields.Selected[i] thenoCol.Options := oCol.Options + [coInKey, coInWhere]elseoCol.Options := oCol.Options - [coInKey, coInWhere];end;// Include into update selected Updating fieldsfor i := 0 to lbUpdateFields.Items.Count - 1 do beginoCol := oTab.Columns.ColumnByName(lbUpdateFields.Items[i]);if lbUpdateFields.Selected[i] thenoCol.Options := oCol.Options + [coInUpdate, coInWhere]elseoCol.Options := oCol.Options - [coInUpdate, coInWhere];end;// Include into refetch selected Refreshing fieldsfor i := 0 to lbRefetchFields.Items.Count - 1 do beginoCol := oTab.Columns.ColumnByName(lbRefetchFields.Items[i]);if lbRefetchFields.Selected[i] thenoCol.Options := oCol.Options + [coAfterInsChanged, coAfterUpdChanged]elseoCol.Options := oCol.Options - [coAfterInsChanged, coAfterUpdChanged];end;// Setup SQL generatorFConnection.ConnectionIntf.CreateCommandGenerator(oCmdGen, oCmd);oCmdGen.FillRowOptions := [foData, foBlobs, foDetails, foClear] +FDGetFillRowOptions(oOpts.FetchOptions);oCmdGen.GenOptions := [goBeautify];if cbQuoteColName.Checked thenoCmdGen.GenOptions := oCmdGen.GenOptions + [goForceQuoteCol]elseoCmdGen.GenOptions := oCmdGen.GenOptions + [goForceNoQuoteCol];if cbQuoteTabName.Checked thenoCmdGen.GenOptions := oCmdGen.GenOptions + [goForceQuoteTab]elseoCmdGen.GenOptions := oCmdGen.GenOptions + [goForceNoQuoteTab];oCmdGen.Options := oOpts;oCmdGen.Table := oTab;// Generate commandsif FOpts.UpdateOptions.EnableInsert thenGetSQL(0).Lines.Text := oCmdGen.GenerateInsert;if FOpts.UpdateOptions.EnableUpdate thenGetSQL(1).Lines.Text := oCmdGen.GenerateUpdate;if FOpts.UpdateOptions.EnableDelete thenGetSQL(2).Lines.Text := oCmdGen.GenerateDelete;if FOpts.UpdateOptions.LockMode <> lmNone then beginGetSQL(3).Lines.Text := oCmdGen.GenerateLock;GetSQL(4).Lines.Text := oCmdGen.GenerateUnLock;end;GetSQL(5).Lines.Text := oCmdGen.GenerateSelect(False);finallyFDFree(oTab);oCmdGen := nil;oCmd := nil;end;
end;{ --------------------------------------------------------------------------- }
function TfrmFDGUIxFormsUSEdit.ExecuteBase(AUpdSQL: TFDUpdateSQL; const ACaption: String): Boolean;
varoTestCmd: TFDCustomCommand;i: Integer;function GetConnection: TFDCustomConnection;beginif FUpdateSQL.ConnectionName <> '' thenResult := FDManager.AcquireConnection(FUpdateSQL.ConnectionName, FUpdateSQL.Name)else beginif FUpdateSQL.Connection <> nil thenResult := FUpdateSQL.Connectionelse if (FDataSet <> nil) and (FDataSet.PointedConnection <> nil) thenResult := FDataSet.PointedConnectionelseResult := oTestCmd.GetConnection(False);if Result = nil thenraise Exception.Create(S_FD_USEditCantEdit);Result := FDManager.AcquireConnection(Result, FUpdateSQL.Name);end;end;function GetParentObject: TPersistent;beginif FDataSet <> nil thenResult := FDataSetelseResult := oTestCmd;end;function GetUpdateOptions: TFDBottomUpdateOptions;beginif FDataSet <> nil thenResult := FDataSet.OptionsIntf.UpdateOptions as TFDBottomUpdateOptionselseResult := oTestCmd.UpdateOptions;end;beginLoadState;FUpdateSQL := AUpdSQL;FDataSet := FUpdateSQL.DataSet;oTestCmd := FUpdateSQL.Commands[arInsert];FConnection := GetConnection;tryFConnection.CheckActive;if (FDataSet <> nil) and not FDataSet.Active and(FDataSet.Command <> nil) and (Trim(FDataSet.Command.CommandText.Text) <> '') and(MessageDlg(S_FD_USEditOpenDS, mtConfirmation, [mbYes, mbNo], -1) = mrYes) thenFDataSet.Open;FOpts := TFDOptionsContainer.Create(GetParentObject, TFDFetchOptions,TFDUpdateOptions, TFDTopResourceOptions, nil);FOpts.ParentOptions := GetUpdateOptions.Container as IFDStanOptions;FOpts.UpdateOptions.Assign(GetUpdateOptions);Caption := Format(S_FD_USEditCaption, [ACaption]);btnDSDefaults.Enabled := (FDataSet <> nil);btnGenSQL.Enabled := False;pcMain.ActivePage := tsGenerate;ActiveControl := cbxTableName;frmUpdateOptions.LoadFrom(FOpts.UpdateOptions);frmUpdateOptions.SQLGenerator := True;cbxTableName.Text := GetUpdateOptions.UpdateTableName;if (cbxTableName.Text = '') and (FDataSet <> nil) and (FDataSet.Table <> nil) thencbxTableName.Text := FDataSet.Table.ActualOriginName;if cbxTableName.Text <> '' then begincbxTableNameChange(cbxTableName);cbxTableNameClick(cbxTableName);end;if btnDSDefaults.Enabled thenbtnDSDefaultsClick(nil);for i := 0 to 5 doGetSQL(i).RDBMSKind := FConnection.RDBMSKind;GetSQL(0).Lines.SetStrings(AUpdSQL.InsertSQL);GetSQL(1).Lines.SetStrings(AUpdSQL.ModifySQL);GetSQL(2).Lines.SetStrings(AUpdSQL.DeleteSQL);GetSQL(3).Lines.SetStrings(AUpdSQL.LockSQL);GetSQL(4).Lines.SetStrings(AUpdSQL.UnlockSQL);GetSQL(5).Lines.SetStrings(AUpdSQL.FetchRowSQL);UpdateExistSQLs;Result := (ShowModal = mrOK);finallyFDManager.ReleaseConnection(FConnection);end;if Result then beginAUpdSQL.InsertSQL.SetStrings(GetSQL(0).Lines);AUpdSQL.ModifySQL.SetStrings(GetSQL(1).Lines);AUpdSQL.DeleteSQL.SetStrings(GetSQL(2).Lines);AUpdSQL.LockSQL.SetStrings(GetSQL(3).Lines);AUpdSQL.UnlockSQL.SetStrings(GetSQL(4).Lines);AUpdSQL.FetchRowSQL.SetStrings(GetSQL(5).Lines);end;SaveState;
end;{ --------------------------------------------------------------------------- }
class function TfrmFDGUIxFormsUSEdit.Execute(AUpdSQL: TFDUpdateSQL; const ACaption: String): Boolean;
varoFrm: TfrmFDGUIxFormsUSEdit;
beginoFrm := TfrmFDGUIxFormsUSEdit.Create(nil);tryResult := oFrm.ExecuteBase(AUpdSQL, ACaption);finallyFDFree(oFrm);end;
end;end.