MS SERVER 2K 的触发器中的事务处理问题(只有20分)(20分)

  • 主题发起人 主题发起人 HON
  • 开始时间 开始时间
下面程序系从DFW谋君的例子,未加ROLLBACK TRANSACTION,RETURN 前触发器完全正确,
对明细表QueryInStoreDetail的触发器进行修改,主要是为了试试数据校验,
若校验通不过,如何向前台送提示信息.


//本程序对于一些主要的控件比如TDatabase属性为了便于理解,都放在程序里,不需要这设计时作设置。
unit UnitMain;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Db, ADODB, Grids, DBGrids, StdCtrls, DBTables, Menus;

type
TFormMain = class(TForm)
Query1: TQuery;
Database1: TDatabase;
MainMenu1: TMainMenu;
MenuItemWork: TMenuItem;
ItemInStore: TMenuItem;
ItemExit: TMenuItem;
ItemEmployee: TMenuItem;
ItemStore: TMenuItem;
ItemGoods: TMenuItem;
ItemStorage: TMenuItem;
procedure ItemExitClick(Sender: TObject);
procedure ItemInStoreClick(Sender: TObject);
procedure ItemEmployeeClick(Sender: TObject);
procedure ItemStoreClick(Sender: TObject);
procedure ItemGoodsClick(Sender: TObject);
procedure ItemStorageClick(Sender: TObject);
procedure FormKeyPress(Sender: TObject; var Key: Char);
private
Function ChildFormExists(FormName: String):Boolean;
{ Private declarations }
public
constructor Create(AOwner: TComponent); override; //重载Form的Create构造函数。
{ Public declarations }
end;

var
FormMain: TFormMain;
ServerName,UserName,Password:String;
MainFormCreated:Boolean;
Function CheckTableIsExistsed:Boolean;

implementation
Uses UnitDataBaseInfo,UnitInStore,UnitEmployee, UnitStore, UnitGoods,UnitStorage,UnitFlash;
{$R *.DFM}

Function CheckTableIsExistsed:Boolean;
begin
Result:=True;
try
With TQuery.Create(Application) do
begin
Try
DatabaseName:=FormMain.Database1.DatabaseName;
//检查商品数据表是否建立。
SQL.Clear;
SQL.Add('Select * from SysObjects Where Name="Goods"');
//每个数据库的所有的表都在本数据库的SysObjects中有记录。
Try
Open;
Except
On E:Exception do
begin
ShowMessage('打开系统数据表:SysObjects出错,请检查网络连接是否正常!'+#13#10+
'错误信息:'+E.Message);
Raise;
end;
end;
if IsEmpty then//如果返回结果集为空,则表示没有商品数据表。
begin
SQL.Clear;
SQL.Add('CREATE TABLE Goods (');//创建数据表。
SQL.Add(' [iNo] [INT] NULL ,'); //关键字段。编号
SQL.Add(' [sName] [varchar] (40) NULL ,'); //名称。为避免和数据的保留字冲突,建议字段名前类型缩写,比如用sName而不用Name做字段名。
SQL.Add(' [sEName] [varchar] (40) NULL ,'); //英文名称
SQL.Add(' [iUnit] [varchar] (6) NULL ,'); //单位。
SQL.Add(' [fPrice] [varchar] (4) NULL)'); //单价。
Try
ExecSQL;
//这里用ExecSQL而不是用Open,是因为DataSet的Open方法只是用于打开SQL语是Select语句的返回结果集
//对于其它SQL语句因为没有返回集所以只能用ExecSQL来执行。

SQL.Clear;
SQL.Add('ALTER TABLE Goods WITH NOCHECK ADD');
SQL.Add(' PRIMARY KEY CLUSTERED ([iNo]) ON [PRIMARY]');
ExecSQL;
//创建主关键字段约束。一旦创建主关键字段约束,这个表中的主关键字段就是iNo,这个字段就不允许有重复。并且数据都会按照这个字段自动排序。
Except
On E:Exception do
begin
ShowMessage('创建商品数据表:Goods出错,请检查网络连接是否正常!'+#13#10+
'错误信息:'+E.Message);
Raise;
end;
end;
end;

SQL.Clear;
SQL.Add('Select * from SysObjects Where Name="Employee"');
Try
Open;
Except
On E:Exception do
begin
ShowMessage('打开系统数据表:SysObjects出错,请检查网络连接是否正常!'+#13#10+
'错误信息:'+E.Message);
Raise;
end;
end;
if IsEmpty then
begin
SQL.Clear;
SQL.Add('CREATE TABLE Employee ('); //建立员工表。
SQL.Add(' [iNo] [INT] NOT NULL ,'); //关键字段。编号
SQL.Add(' [sName] [varchar] (40) NULL ,'); //姓名
SQL.Add(' [sEName] [varchar] (40) NULL)'); //英文名。
Try
ExecSQL;
//这里用ExecSQL而不是用Open,是因为DataSet的Open方法只是用于打开SQL语是Select语句的返回结果集
//对于其它SQL语句因为没有返回集所以只能用ExecSQL来执行。
SQL.Clear;
SQL.Add('ALTER TABLE Employee WITH NOCHECK ADD');
SQL.Add(' PRIMARY KEY CLUSTERED ([iNo]) ON [PRIMARY]');
ExecSQL;
//创建主关键字段约束。
Except
On E:Exception do
begin
ShowMessage('创建员工数据表:Emploree出错,请检查网络连接是否正常!'+#13#10+
'错误信息:'+E.Message);
Raise;
end;
end;
end;

SQL.Clear;
SQL.Add('Select * from SysObjects Where Name="Store"');
Try
Open;
Except
On E:Exception do
begin
ShowMessage('打开系统数据表:SysObjects出错,请检查网络连接是否正常!'+#13#10+
'错误信息:'+E.Message);
Raise;
end;
end;
if IsEmpty then
begin
SQL.Clear;
SQL.Add('CREATE TABLE Store ('); //建立仓库表。
SQL.Add(' [iNo] [INT] NOT NULL ,'); //关键字段。编号
SQL.Add(' [sName] [varchar] (40) NULL ,'); //名称。
SQL.Add(' [sEName] [varchar] (40) NULL)'); //英文名。
Try
ExecSQL;
//这里用ExecSQL而不是用Open,是因为DataSet的Open方法只是用于打开SQL语是Select语句的返回结果集
//对于其它SQL语句因为没有返回集所以只能用ExecSQL来执行。
SQL.Clear;
SQL.Add('ALTER TABLE Store WITH NOCHECK ADD');
SQL.Add(' PRIMARY KEY CLUSTERED ([iNo]) ON [PRIMARY]');
ExecSQL;
//创建主关键字段约束。
Except
On E:Exception do
begin
ShowMessage('创建仓库数据表:Store出错,请检查网络连接是否正常!'+#13#10+
'错误信息:'+E.Message);
Raise;
end;
end;
end;

SQL.Clear;
SQL.Add('Select * from SysObjects Where Name="Storage"');
Try
Open;
Except
On E:Exception do
begin
ShowMessage('打开系统数据表:SysObjects出错,请检查网络连接是否正常!'+#13#10+
'错误信息:'+E.Message);
Raise;
end;
end;
if IsEmpty then
begin
SQL.Clear;
SQL.Add('CREATE TABLE Storage ('); //库存数据量表。
SQL.Add(' [iStoreNo] [Int] NOT NULL ,'); //仓库编号
SQL.Add(' [iGoodsNo] [INT] NOT NULL ,'); //商品编号。
SQL.Add(' [fQuantity] [Numeric] (8,2) NULL)'); //商品数量。
Try
ExecSQL;

SQL.Clear;
SQL.Add('ALTER TABLE Storage WITH NOCHECK ADD');
SQL.Add(' PRIMARY KEY CLUSTERED ([iStoreNo],[iGoodsNo]) ON [PRIMARY]');
SQL.Add('ALTER TABLE Storage ADD');
SQL.Add(' FOREIGN KEY ([iStoreNo]) REFERENCES Store ([iNo])');
SQL.Add('ALTER TABLE Storage ADD');
SQL.Add(' FOREIGN KEY ([iGoodsNo]) REFERENCES Goods ([iNo])');
ExecSQL;
//创建主关键字段约束和外键。
//在SQL中,外键是指一个表(称之为关连表)中的某个字段(称之为关连字段)与另一个表(称之为被关连表)的某个字段(称之为被关连字段)关连,
//当创建了外键约束后,关连表的每一个关连字段的记录值,都必须在被关连表中存在。而被关连表中被关连字段的值如果在关连表中存在,
//则被关连表的这个记录的被关连字段值不允许修改,也不允许删除。

//如果说主键约束主要是保证字段值的唯一性的话,外键约束就是为了保存整个数据库数据的完整性。

//也就是说:InStoreMaster的员工编号字段不允许也不应该存在一个在Employee表中不存在的记录。
Except
On E:Exception do
begin
ShowMessage('创建仓库数据表:Store出错,请检查网络连接是否正常!'+#13#10+
'错误信息:'+E.Message);
Raise;
end;
end;
end;

SQL.Clear;
SQL.Add('Select * from SysObjects Where Name="InStoreMaster"');
Try
Open;
Except
On E:Exception do
begin
ShowMessage('打开系统数据表:SysObjects出错,请检查网络连接是否正常!'+#13#10+
'错误信息:'+E.Message);
Raise;
end;
end;
if IsEmpty then
begin
SQL.Clear;
SQL.Add('CREATE TABLE InStoreMaster ('); //创建进仓单明细表。
SQL.Add(' [iNo] [VARCHAR] (12) NOT NULL ,'); //单号
SQL.Add(' [dDate] [DateTime] NULL ,'); //商品编号。
SQL.Add(' [iStore] [INT] NOT NULL ,'); //仓库编号。
SQL.Add(' [iEmployee] [INT] NOT NULL)'); //员工。
Try
ExecSQL;

SQL.Clear;
SQL.Add('ALTER TABLE InStoreMaster WITH NOCHECK ADD');
SQL.Add(' PRIMARY KEY CLUSTERED ([iNo]) ON [PRIMARY]');
SQL.Add('ALTER TABLE InStoreMaster ADD');
SQL.Add(' FOREIGN KEY ([iEmployee]) REFERENCES Employee ([iNo])');
ExecSQL;
//创建主关键字段约束和外键。
//在SQL中,外键是指一个表(称之为关连表)中的某个字段(称之为关连字段)与另一个表(称之为被关连表)的某个字段(称之为被关连字段)关连,
//当创建了外键约束后,关连表的每一个关连字段的记录值,都必须在被关连表中存在。而被关连表中被关连字段的值如果在关连表中存在,
//则被关连表的这个记录的被关连字段值不允许修改,也不允许删除。

//如果说主键约束主要是保证字段值的唯一性的话,外键约束就是为了保存整个数据库数据的完整性。

//也就是说:InStoreMaster的员工编号字段不允许也不应该存在一个在Employee表中不存在的记录。
Except
On E:Exception do
begin
ShowMessage('创建仓库数据表:Store出错,请检查网络连接是否正常!'+#13#10+
'错误信息:'+E.Message);
Raise;
end;
end;
end;

SQL.Clear;
SQL.Add('Select * from SysObjects Where Name="InStoreDetail"');
Try
Open;
Except
On E:Exception do
begin
ShowMessage('打开系统数据表:SysObjects出错,请检查网络连接是否正常!'+#13#10+
'错误信息:'+E.Message);
Raise;
end;
end;
if IsEmpty then
begin
SQL.Clear;
SQL.Add('CREATE TABLE InStoreDetail ('); //创建进仓单明细表。
SQL.Add(' [iNo] [VARCHAR] (12) NOT NULL ,'); //单号
SQL.Add(' [iGoodsNo] [INT] NOT NULL ,'); //商品编号。
SQL.Add(' [fPrice] [Money] NULL ,'); //单价。
SQL.Add(' [fQuantity] [Numeric] (8,2) NULL)'); //数量。
Try
ExecSQL;

SQL.Clear;
SQL.Add('ALTER TABLE InStoreDetail WITH NOCHECK ADD');
SQL.Add(' PRIMARY KEY CLUSTERED ([iNo],[iGoodsNo]) ON [PRIMARY]');
SQL.Add('ALTER TABLE InStoreDetail ADD');
SQL.Add(' FOREIGN KEY ([iGoodsNo]) REFERENCES Goods ([iNo])');
SQL.Add('ALTER TABLE InStoreDetail ADD');
SQL.Add(' FOREIGN KEY ([iNo]) REFERENCES InStoreMaster ([iNo])');
ExecSQL;
//创建主关键字段约束和外键。
//在SQL中,外键是指一个表(称之为关连表)中的某个字段(称之为关连字段)与另一个表(称之为被关连表)的某个字段(称之为被关连字段)关连,
//当创建了外键约束后,关连表的每一个关连字段的记录值,都必须在被关连表中存在。而被关连表中被关连字段的值如果在关连表中存在,
//则被关连表的这个记录的被关连字段值不允许修改,也不允许删除。

//如果说主键约束主要是保证字段值的唯一性的话,外键约束就是为了保存整个数据库数据的完整性。

//也就是说:InStoreDetail的商品编号字段不允许也不应该存在一个在Goods表中不存在的记录。

Except
On E:Exception do
begin
// ShowMessage('创建触发器:ut_GoodInDetail_UDI出错,请检查网络连接是否正常!'+#13#10+
// '错误信息:'+E.Message);
ShowMessage('打开系统数据表:SysObjects出错,请检查网络连接是否正常!'+#13#10+
'错误信息:'+E.Message);
Raise;
end;
end;
end;

SQL.Clear;
SQL.Add('Select * from SysObjects Where Name="ut_GoodInDetail_UDI"');
Try
Open;
Except
On E:Exception do
begin
// ShowMessage('打开系统数据表:SysObjects出错,请检查网络连接是否正常!'+#13#10+
// '错误信息:'+E.Message);
ShowMessage('创建触发器:ut_GoodInDetail_UDI出错,请检查网络连接是否正常!'+#13#10+
'错误信息:'+E.Message);
Raise;
end;
end;
if IsEmpty then
begin
SQL.Clear;
SQL.Add('CREATE TRIGGER ut_GoodInDetail_UDI ON InStoreDetail FOR DELETE , INSERT , UPDATE AS');
//创建触发器。进仓单明细表
SQL.Add('BEGIN');
SQL.Add(' DECLARE @INO_I VARCHAR (12) , @IGOODSNO_I INT , @ISTORENO_I INT , @FQUANTITY_I Numeric (8,2)');
SQL.Add(' DECLARE @INO_D VARCHAR (12) , @IGOODSNO_D INT , @ISTORENO_D INT , @FQUANTITY_D Numeric (8,2)');
//定义变量,在SQL中,所有的变量都用@作头。
SQL.Add(' UPDATE Storage Set fQuantity = 0 WHERE fQuantity IS NULL');
//将所以fQuantity字段为NULL的记录改成0。因为在SQL中,NULL无论与什么进行运算结果都是NULL。

SQL.Add(' DECLARE DELETED_CURSOR CURSOR FOR');
SQL.Add(' SELECT INO , IGoodsNo , fQuantity FROM DELETED;');
//定义游标。DELETED是SQL中的一个临时表,当删除一个或几个记录时这些被删除的记录都会放在这个临时表中
//如果是更新记录,则SQL会先将原来的数据放在DELETED临时表中,再将新的数据放在INSERTED临时表中。也就是说先删除,再增加。
SQL.Add(' OPEN DELETED_CURSOR');
//打开游标。
SQL.Add(' FETCH NEXT FROM DELETED_CURSOR INTO @INO_D , @IGOODSNO_D , @FQUANTITY_D');
//取游标的第一个记录。
SQL.Add(' WHILE @@FETCH_STATUS = 0');
//FETCH语句的结果。@@FETCH_STATUS在SQL中是一个全局变量,当FETCH从游标中正确取得一个记录就等于0/
SQL.Add(' BEGIN');
SQL.Add(' SET @ISTORENO_D = ( SELECT iStore FROM InStoreMaster WHERE INO = @INO_D )');
//取得仓库编号。
SQL.Add(' IF EXISTS(SELECT * FROM Storage WHERE iStoreNo = @ISTORENO_D AND IGoodsNo = @IGOODSNO_D)');
//如果仓库号/商品号在库存表中存在。
SQL.Add(' BEGIN');
SQL.ADD('SHOWMESSAGE("DELE GOODS")');
SQL.Add(' UPDATE Storage Set fQuantity = fQuantity - @FQUANTITY_D WHERE iStoreNo = @ISTORENO_D AND IGoodsNo = @IGOODSNO_D');
//就只更新库存数量。原库存减去数量。
SQL.Add(' END');
SQL.Add(' ELSE');
SQL.Add(' INSERT INTO Storage (iStoreNo , iGoodsNo , fQuantity ) VALUES ( @ISTORENO_D , @IGOODSNO_D , - @FQUANTITY_D )');
//插入一条记录。
SQL.Add(' FETCH NEXT FROM DELETED_CURSOR INTO @INO_D , @IGOODSNO_D , @FQUANTITY_D');
//取下一条记录。
SQL.Add(' END');
SQL.Add(' CLOSE DELETED_CURSOR');
SQL.Add(' DEALLOCATE DELETED_CURSOR');
//关闭游标并释放资源。

SQL.Add(' DECLARE INSERTED_CURSOR CURSOR FOR');
SQL.Add(' SELECT INO , IGoodsNo , fQuantity FROM INSERTED;');
//定义游标。INSERTED是SQL中的一个临时表,当插入一个或几个记录时这些被插入的记录都会放在这个临时表中
//如果是更新记录,则SQL会先将原来的数据放在DELETED临时表中,再将新的数据放在INSERTED临时表中。也就是说先删除,再增加。
SQL.Add(' OPEN INSERTED_CURSOR');
//打开游标。
SQL.Add(' FETCH NEXT FROM INSERTED_CURSOR INTO @INO_I , @IGOODSNO_I , @FQUANTITY_I');
//取游标的第一个记录。
SQL.Add(' WHILE @@FETCH_STATUS = 0');
//FETCH语句的结果。@@FETCH_STATUS在SQL中是一个全局变量,当FETCH从游标中正确取得一个记录就等于0/
SQL.Add(' BEGIN');
SQL.Add(' SET @ISTORENO_I = ( SELECT iStore FROM InStoreMaster WHERE INO = @INO_I )');
//取得仓库编号。
SQL.Add(' IF EXISTS(SELECT * FROM Storage WHERE iStoreNo = @ISTORENO_I AND IGoodsNo = @IGOODSNO_I)');
//如果仓库号/商品号在库存表中存在。
SQL.Add(' UPDATE Storage Set fQuantity = fQuantity + @FQUANTITY_I WHERE iStoreNo = @ISTORENO_I AND IGoodsNo = @IGOODSNO_I');
//就只更新库存数量。原库存加上数量。
SQL.Add(' ELSE');
SQL.Add(' BEGIN');
SQL.ADD('SHOWMESSAGE("NEW GOODS")');
SQL.Add(' INSERT INTO Storage (iStoreNo , iGoodsNo , fQuantity ) VALUES ( @ISTORENO_I , @IGOODSNO_I , @FQUANTITY_I )');
SQL.ADD(' END');
//插入一条记录。
SQL.Add(' FETCH NEXT FROM INSERTED_CURSOR INTO @INO_I , @IGOODSNO_I , @FQUANTITY_I');
//取下一条记录。
SQL.Add(' END');
SQL.Add(' CLOSE INSERTED_CURSOR');
SQL.Add(' DEALLOCATE INSERTED_CURSOR');
//关闭游标并释放资源。

SQL.Add(' DELETE FROM Storage WHERE fQuantity = 0');
//删除库存数为0的记录。
SQL.Add('END');
ExecSQL;
end;
//以上是检查整个数库的表是否完整。
finally
Free;
end;
end;
Except
Result:=False;
end;
end;

constructor TFormMain.Create(AOwner: TComponent);
function ComputerName : String;
var
CNameBuffer : PChar;
fl_loaded : Boolean;
CLen : ^DWord;
begin
GetMem(CNameBuffer,255);
New(CLen);
CLen^:= 255;
fl_loaded := GetComputerName(CNameBuffer,CLen^);
if fl_loaded then
Result := StrPas(CNameBuffer)
//转换PChar到String。
else
Result := '';
FreeMem(CNameBuffer,255);
Dispose(CLen);
end;
begin

MainFormCreated:=True;
FormDataBaseInfo:=TFormDataBaseInfo.Create(Application);
try
FormDataBaseInfo.EditServerName.Text:=ComputerName;
if FormDataBaseInfo.ShowModal=mrOK then
begin
Application.ProcessMessages;
UserName:=FormDataBaseInfo.EditUserName.Text;
Password:=FormDataBaseInfo.EditPassword.Text;
ServerName:=FormDataBaseInfo.EditServerName.Text;
with TDatabase.Create(Application) do
begin
Try
DatabaseName:='TestDatabase';
//随便给DatabaseName命个名字就可以了。
LoginPrompt:=False;
DriverName:='MSSQL';
//这行是关闭TDatabase内置的登录对话框。
Params.Clear;
Params.Add('Server Name='+ServerName);
Params.Add('Database Name=MASTER');
Params.Add('User Name='+UserName);
Params.Add('Password='+Password);
Try
FormFlash.Panel1.Caption:='打开数据库。。。';
Application.ProcessMessages;
Open;//尝试打开数据库,如果正常打开,则说明SQL服务器工作正常,并且用户名和密码正确。
With TQuery.Create(Application) do
begin
try
DatabaseName:='TestDatabase';
SQL.Add('Select * from Sysdatabases Where Name="TestDB"');
try
FormFlash.Panel1.Caption:='查找数据库。。。';
Application.ProcessMessages;
Open;
Except
On E:Exception do
begin
Application.MessageBox(PChar('不能打开MASTER数据库的Sysdatabases表,请确认是否有打开该表的权限。'+#13#10#13#10+
'错误信息:'+E.Message),'提示:',MB_ICONERROR);
MainFormCreated:=False;
end;
end;
if MainFormCreated and IsEmpty then//如果返回为空则表示TestDB还没有创建。
begin
//SQL Server所有的用户数据库都在Master数据库的Sysdatabases表中有记录。
Close;
SQL.Clear;
SQL.Add('Create Database TestDB');
try
FormFlash.Panel1.Caption:='创建数据库。。。';
Application.ProcessMessages;
ExecSQL;
//这里用ExecSQL而不是用Open,是因为DataSet的Open方法只是用于打开SQL语是Select语句的返回结果集
//对于其它SQL语句因为没有返回集所以只能用ExecSQL来执行。
Except
On E:Exception do
begin
Application.MessageBox(PChar('不能创建数据库,请确认是否有创建数据库的权限。'+#13#10#13#10+
'错误信息:'+E.Message),'提示:',MB_ICONERROR);
MainFormCreated:=False;
end;
end;
end;
finally
Free;
end;
end;
Except
on E:exception do
begin
Application.MessageBox(PChar('不能打开数据库,请确认网络联接正常并稍后再试。'+#13#10#13#10+
'错误信息:'+E.Message),'提示:',MB_ICONERROR);
MainFormCreated:=False;
end;
end;
finally
Free;
end;
end;
end else
begin
MainFormCreated:=False;
Application.Terminate;//强行中止系统。
end;
finally
FormDataBaseInfo.Free;
end;
//以上代码尝试用用户输入的密码和用户名打开指定的数据库。如果打开正常,则保存输入的信息到全局变量并进入系统,否则退出。
if MainFormCreated then
begin
inherited Create(AOwner);
//重用父类TForm的构造函数Create。
With Database1 do
begin
Database1.Close;
DriverName:='MSSQL';
DatabaseName:='TestDatabase';
//随便给DatabaseName命个名字就可以了。
LoginPrompt:=False;
//这行是关闭TDatabase内置的登录对话框。
Params.Clear;
Params.Add('Server Name='+ServerName);
Params.Add('Database Name=TestDB');
Params.Add('User Name='+UserName);
Params.Add('Password='+Password);
FormFlash.Panel1.Caption:='连接数据库。。。';
Application.ProcessMessages;
Open;
end;
end else
Application.Terminate;
end;

procedure TFormMain.ItemExitClick(Sender: TObject);
begin
Close;
end;

Function TFormMain.ChildFormExists(FormName:String):Boolean;
Var i:Integer;
begin
Result:=False;
For i:=0 to MDIChildCount-1 do
begin
if MDIChildren.Name=FormName then
begin
MDIChildren.WindowState:=wsMaximized;
Result:=True;
//如果Form已经存在,就显示它
Break;
end;
end;
end;

procedure TFormMain.ItemInStoreClick(Sender: TObject);
begin
if not ChildFormExists('FormInStore') then
FormInStore:=TFormInStore.Create(Self);
//如果不存在就创建它。
end;

procedure TFormMain.ItemEmployeeClick(Sender: TObject);
begin
if not ChildFormExists('FormEmployee') then
FormEmployee:=TFormEmployee.Create(Self);
//如果不存在就创建它。
end;

procedure TFormMain.ItemStoreClick(Sender: TObject);
begin
if not ChildFormExists('FormStore') then
FormStore:=TFormStore.Create(Self);
//如果不存在就创建它。
end;

procedure TFormMain.ItemGoodsClick(Sender: TObject);
begin
if not ChildFormExists('FormGoods') then
FormGoods:=TFormGoods.Create(Self);
//如果不存在就创建它。
end;

procedure TFormMain.ItemStorageClick(Sender: TObject);
begin
if not ChildFormExists('FormStorage') then
FormStorage:=TFormStorage.Create(Self);
//如果不存在就创建它。
end;


procedure TFormMAIN.FormKeyPress(Sender: TObject; var Key: Char);
begin
IF key=#13 then { 判断是按执行键}
IF NOT (ActiveControl IS TDbgrid) THEN
Begin { 不是在TDbgrid控件内}
key:=#0;
perform(WM_NEXTDLGCTL,0,0);{移动到下一个控件}
end else
IF (ActiveControl is TDbgrid) Then{是在 TDbgrid 控件内}
begin
With TDbgrid(ActiveControl) Do
if Selectedindex<(FieldCount-1) then
Selectedindex:=Selectedindex+1{ 移动到下一字段}
else Selectedindex:=0;
end;
END;

END.


unit UnitInStore;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Db, ADODB, DBTables, StdCtrls, Mask, DBCtrls,UnitMain, Grids, DBGrids,
ExtCtrls;

type
TFormInStore = class(TForm)
QueryGoods: TQuery;
QueryEmployee: TQuery;
QueryInStoreMaster: TQuery;
QueryInStoreDetail: TQuery;
QueryStore: TQuery;
DataSourceInStoreMaster: TDataSource;
DataSourceInStoreDetail: TDataSource;
QueryInStoreMasteriNo: TStringField;
QueryInStoreMasterdDate: TDateTimeField;
QueryInStoreMasteriEmployee: TIntegerField;
QueryEmployeeiNo: TIntegerField;
QueryEmployeesName: TStringField;
QueryEmployeesEName: TStringField;
DBGrid1: TDBGrid;
DataSourceEmploree: TDataSource;
DataSourceStore: TDataSource;
QueryInStoreDetailiNo: TStringField;
QueryInStoreDetailiGoodsNo: TIntegerField;
QueryInStoreDetailfPrice: TCurrencyField;
QueryInStoreDetailfQuantity: TFloatField;
QueryGoodsiNo: TIntegerField;
QueryGoodssName: TStringField;
QueryGoodssEName: TStringField;
QueryGoodsiUnit: TStringField;
QueryGoodsfPrice: TStringField;
QueryInStoreMasteriStore: TIntegerField;
Panel1: TPanel;
Label1: TLabel;
Label2: TLabel;
Label4: TLabel;
DBEdit1: TDBEdit;
DBEdit2: TDBEdit;
DBEdit4: TDBEdit;
DBLookupComboBoxStore: TDBLookupComboBox;
Panel2: TPanel;
Label3: TLabel;
DBLookupComboBoxEmployee: TDBLookupComboBox;
DBEdit3: TDBEdit;
DBNavigatorMaster: TDBNavigator;
UpdateSQLDetail: TUpdateSQL;
UpdateSQLMaster: TUpdateSQL;
DBNavigatorDetail: TDBNavigator;
procedure QueryInStoreMasterAfterOpen(DataSet: TDataSet);
procedure QueryInStoreMasterBeforeClose(DataSet: TDataSet);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure QueryInStoreMasteriEmployeeSetText(Sender: TField;
const Text: String);
procedure QueryInStoreMasteriStoreSetText(Sender: TField;
const Text: String);
procedure FormDestroy(Sender: TObject);
procedure QueryInStoreDetailiGoodsNoSetText(Sender: TField;
const Text: String);
procedure QueryInStoreMasterNewRecord(DataSet: TDataSet);
procedure QueryInStoreDetailNewRecord(DataSet: TDataSet);
procedure QueryInStoreMasterBeforeScroll(DataSet: TDataSet);
procedure QueryInStoreMasterBeforeDelete(DataSet: TDataSet);
procedure DBNavigatorMasterBeforeAction(Sender: TObject;
Button: TNavigateBtn);
procedure QueryInStoreDetailBeforeClose(DataSet: TDataSet);
procedure DBNavigatorDetailBeforeAction(Sender: TObject;
Button: TNavigateBtn);
procedure QueryInStoreDetailAfterClose(DataSet: TDataSet);
procedure QueryInStoreDetailCalcFields(DataSet: TDataSet);
procedure FormActivate(Sender: TObject);
procedure DataSourceInStoreDetailStateChange(Sender: TObject);
procedure DataSourceInStoreDetailDataChange(Sender: TObject;
Field: TField);
procedure QueryInStoreDetailiGoodsNoChange(Sender: TField);
procedure DBEdit4KeyPress(Sender: TObject; var Key: Char);
procedure FormKeyPress(Sender: TObject; var Key: Char);
private
GoodsNameField:TStringField;
SumField:TCurrencyField;
procedure SaveMasterCurrentRecord;

{ Private declarations }
public
constructor Create(AOwner: TComponent); override; //重载Form的Create构造函数。
{ Public declarations }
end;

var
FormInStore: TFormInStore;

implementation
Uses UnitSingleEmployee,UnitSingleStore,UnitSingleGoods;
{$R *.DFM}

{ TFormInStore }

constructor TFormInStore.Create(AOwner: TComponent);
begin
if CheckTableIsExistsed then//如果数据表完全正确,就创建Form,否则退出。
begin
inherited Create(AOwner);
//DBLookupComboBoxEmployee的属性可以在Object Inspector窗口中设置,但是为了便于明白,我还是把主要的属性设置写成代码。
DBLookupComboBoxEmployee.DataSource:=DataSourceInStoreMaster;//这个属性想你也知道了。
DBLookupComboBoxEmployee.DataField:='iEmployee'; //虽然我们是用这个控件来显示开这张单的员工姓名
//而InStoreMaster表中又没有姓名这个字段,但是TDBLookupComboBox这个控件让我们可以做到这一点。
DBLookupComboBoxEmployee.ListSource:=DataSourceEmploree; //我们需要列出的下拉列表框中的姓名数据的来源。
DBLookupComboBoxEmployee.ListField:='sName'; //我们需要在下拉列表框中列出的字段
DBLookupComboBoxEmployee.KeyField:='iNo'; //与DataSourceEmploree中和iEmployee属性相关连的Field。

//DBLookupComboBoxStore的属性可以在Object Inspector窗口中设置,但是为了便于明白,我还是把主要的属性设置写成代码。
DBLookupComboBoxStore.DataSource:=DataSourceInStoreMaster;//这个属性想你也知道了。
DBLookupComboBoxStore.DataField:='iStore'; //虽然我们是用这个控件来显示开这张单的仓库名称
//而InStoreMaster表中又没有名称这个字段,但是TDBLookupComboBox这个控件让我们可以做到这一点。
DBLookupComboBoxStore.ListSource:=DataSourceStore; //我们需要列出的下拉列表框中的名称数据的来源。
DBLookupComboBoxStore.ListField:='sName'; //我们需要在下拉列表框中列出的字段
DBLookupComboBoxStore.KeyField:='iNo'; //与DataSourceStore中和iStore属性相关连的Field。


GoodsNameField:=TStringField.Create(QueryInStoreDetail);
GoodsNameField.FieldName:='GoodsNameField';
GoodsNameField.DisplayLabel:='品名';
GoodsNameField.FieldKind:=fkLookup; //这个字段是一个LoopUp字段。
GoodsNameField.DataSet:=QueryInStoreDetail; //这个字段属于QueryInStoreDetail。
GoodsNameField.DisplayWidth:=QueryGoods.FieldByName('sName').DisplayWidth;//显示宽度。
GoodsNameField.Size:=QueryGoods.FieldByName('sName').Size; //宽度。
GoodsNameField.LookupDataSet:=QueryGoods;
GoodsNameField.LookupKeyFields:='iNo';
GoodsNameField.LookupResultField:='sName';
GoodsNameField.KeyFields:='iGoodsNo';
GoodsNameField.Index:=2;

SumField:=TCurrencyField.Create(QueryInStoreDetail);
SumField.FieldName:='SumField';
SumField.DisplayLabel:='合计';
SumField.FieldKind:=fkCalculated; //这个字段是一个计算(Calculated)字段。
SumField.DataSet:=QueryInStoreDetail; //这个字段属于QueryInStoreDetail。
// SumField.DisplayWidth:=QueryGoods.FieldByName('sName').DisplayWidth;//显示宽度。
// SumField.Size:=QueryGoods.FieldByName('sName').Size; //宽度。
//数字型或货币型字段不需设长度。
// SumField.LookupDataSet:=QueryGoods;
// SumField.LookupKeyFields:='iNo';
// SumField.LookupResultField:='sName';
// SumField.KeyFields:='iGoods';
//计算字段不需要设上面四个属性。

QueryInStoreDetail.FieldByName('iNo').DisplayLabel:='No';
QueryInStoreDetail.FieldByName('iNo').Visible:=False;
QueryInStoreDetail.FieldByName('fPrice').DisplayLabel:='单价';
QueryInStoreDetail.FieldByName('fQuantity').DisplayLabel:='数量';
QueryInStoreDetail.FieldByName('iGoodsNo').DisplayLabel:='商品编号';

QueryGoods.Close;
QueryGoods.SQL.Clear;
QueryGoods.SQL.Add('Select * from Goods');
QueryGoods.Open;
QueryEmployee.Close;
QueryEmployee.SQL.Clear;
QueryEmployee.SQL.Add('Select * from Employee');
QueryEmployee.Open;
QueryStore.Close;
QueryStore.SQL.Clear;
QueryStore.SQL.Add('Select * from Store');
QueryStore.Open;

QueryInStoreMaster.Close;
QueryInStoreMaster.RequestLive:=True;
QueryInStoreMaster.CachedUpdates:=True;
QueryInStoreMaster.SQL.Clear;
QueryInStoreMaster.SQL.Add('Select * from InStoreMaster');
QueryInStoreMaster.Open;
end;
end;

procedure TFormInStore.QueryInStoreMasterAfterOpen(DataSet: TDataSet);
begin
IF QueryInStoreMaster.ISEMPTY THEN
BEGIN
SHOWMESSAGE('MASTER ISEMPTY....');
// QUERYINStoreMASTER.APPEND; //此处加这句最好,当数据库位空时强制执行.
END;
QueryInStoreDetail.Close;
QueryInStoreDetail.RequestLive:=True;
QueryInStoreDetail.CachedUpdates:=True;
QueryInStoreDetail.DataSource:=DataSourceInStoreMaster;//
//建立应用程序这关连。
QueryInStoreDetail.SQL.Clear;
QueryInStoreDetail.SQL.Add('Select * from InStoreDetail Where iNo=:iNo');
QueryInStoreDetail.Open;
//请注意这里的SQL语句中有一个iNo参数,但是确没有给这个参数赋值。
//这是因为QueryInStoreDetail的DataSource属性已经设为DataSourceInStoreMaster,
//这时如果没有给参数赋值,它会到DataSource属性所指向的DataSource也就是DataSourceInStoreMaster
//的DataSet所指向的DataSet也就是QueryInStoreMaster去找名为iNo的字段,如果找到,
//它会自动把这个字段当前记录的值作为它的参数值(所以把这段代码放在QueryInStoreMaster的AfterOpen事件中,
//就是因为QueryInStoreMaster必须打开。),如果QueryInStoreMaster没有打开或其中没有这个字段则会出错。
//而且最重要的,当建立这样的关连后,当QueryInStoreMaster的记录移动时,QueryInStoreDetail会自动根据
//QueryInStoreMaster当前记录的iNo字段的值来刷新。
end;

procedure TFormInStore.QueryInStoreMasterBeforeClose(DataSet: TDataSet);
begin
QueryInStoreDetail.Close;
//上面说到如果QueryInStoreMaster已经关闭,则打开QueryInStoreDetail会出错,所以为保险起见,在BeforeClose事件中先关闭QueryInStoreDetail。
end;

procedure TFormInStore.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
Action:=caFree;
//对于MDIChild Form,必须要在OnClose事件中加这一行才能关闭。
end;

procedure TFormInStore.QueryInStoreMasteriEmployeeSetText(Sender: TField;
const Text: String);
Var iNo:Integer;
begin
//在本事件中如果不给QueryInStoreMasteriEmployee.AsInteger赋值则QueryInStoreMasteriEmployee字段的值将无法输入。

//如果输入了非法的字符串或输入的编号不在Employee表中则弹出一个对话框供输入新的记录。
Try
iNo:=StrToInt(Text);
Except
Exit;
//如果字符串不是数字则退出。
end;
With TQuery.Create(Application) do
begin
Try
DatabaseName:=FormMain.Database1.DatabaseName;
SQL.Add('Select * from Employee Where iNo=:iNo');
ParamByName('iNo').AsInteger:=iNo;
//检查输入的编号在Employee表中是否存在。
Open;
if IsEmpty then //返回结果集为空表示不存在。
begin
with TFormSingleEmployee.Create(Self) do
//创建一个输入员工的对话框实例。请看TFormSingleEmployee的OnCreate事件中的代码。
begin
Try
Query1.Append; //添加一个记录。
Query1.FieldByName('iNo').AsInteger:=iNo;//并把新输入的编号加进去。
if ShowModal=mrOK then//如果按OK键。
begin
QueryEmployee.Close; //重新打开Employee表。
QueryEmployee.Open;
QueryInStoreMasteriEmployee.AsInteger:=iNo;
end;
finally
Free;
end;
end;
end else
QueryInStoreMasteriEmployee.AsInteger:=iNo;
finally
Free;
end;
end;
end;

procedure TFormInStore.QueryInStoreMasteriStoreSetText(Sender: TField;
const Text: String);
Var iNo:Integer;
begin
//在本事件中如果不给QueryInStoreMasteriStore.AsInteger赋值则QueryInStoreMasteriStore字段的值将无法输入。

//如果输入了非法的字符串或输入的编号不在Store表中则弹出一个对话框供输入新的记录。
Try
iNo:=StrToInt(Text);
Except
Exit;
//如果字符串不是数字则退出。
end;
With TQuery.Create(Application) do
begin
Try
DatabaseName:=FormMain.Database1.DatabaseName;
SQL.Add('Select * from Store Where iNo=:iNo');
ParamByName('iNo').AsInteger:=iNo;
//检查输入的编号在Store表中是否存在。
Open;
if IsEmpty then //返回结果集为空表示不存在。
begin
with TFormSingleStore.Create(Self) do
//创建一个输入员工的对话框实例。请看TFormSingleStore的OnCreate事件中的代码。
begin
Try
Query1.Append; //添加一个记录。
Query1.FieldByName('iNo').AsInteger:=iNo;//并把新输入的编号加进去。
if ShowModal=mrOK then//如果按OK键。
begin
QueryStore.Close; //重新打开Employee表。
QueryStore.Open;
QueryInStoreMasteriStore.AsInteger:=iNo;
end;
finally
Free;
end;
end;
end else
QueryInStoreMasteriStore.AsInteger:=iNo;
finally
Free;
end;
end;
end;

procedure TFormInStore.FormDestroy(Sender: TObject);
begin
GoodsNameField.Free;
SumField.Free;
end;

procedure TFormInStore.QueryInStoreDetailiGoodsNoSetText(Sender: TField;
const Text: String);
Var iNo:Integer;
begin
//在本事件中如果不给QueryInStoreDetailiGoodsNo.AsInteger赋值则QueryInStoreDetailiGoodsNo字段的值将无法输入。

//如果输入了非法的字符串或输入的编号不在Goods表中则弹出一个对话框供输入新的记录。
Try
iNo:=StrToInt(Text);
Except
Exit;
//如果字符串不是数字则退出。
end;
With TQuery.Create(Application) do
begin
Try
DatabaseName:=FormMain.Database1.DatabaseName;
SQL.Add('Select * from Goods Where iNo=:iNo');
ParamByName('iNo').AsInteger:=iNo;
//检查输入的编号在Goods表中是否存在。
Open;
if IsEmpty then //返回结果集为空表示不存在。
begin
with TFormSingleGoods.Create(Self) do
//创建一个输入对话框实例。请看TFormSingleGoods的OnCreate事件中的代码。
begin
Try
Query1.Append; //添加一个记录。
Query1.FieldByName('iNo').AsInteger:=iNo;//并把新输入的编号加进去。
if ShowModal=mrOK then//如果按OK键。
begin
QueryGoods.Close; //重新打开Employee表。
QueryGoods.Open;
QueryInStoreDetailiGoodsNo.AsInteger:=iNo;
end;
finally
Free;
end;
end;
end else
QueryInStoreDetailiGoodsNo.AsInteger:=iNo;
finally
Free;
end;
end;
end;

procedure TFormInStore.QueryInStoreMasterNewRecord(DataSet: TDataSet);
begin
QueryInStoreMasteriNo.AsString:=IntToHex(Trunc(TimeStampToMSecs(DateTimeToTimeStamp(Now))),12);
//SHOWMESSAGE('MASTERNEW');
//取当前时间的毫秒数转换成十六进制字符串做INO确保不会重复。
end;

procedure TFormInStore.QueryInStoreDetailNewRecord(DataSet: TDataSet);
begin
IF TRIM(QueryInStoreMasteriNo.AsString)='' THEN
BEGIN
SHOWMESSAGE('NO MASTER RECORDER...');
QueryInStoreMaster.APPEND; //不能放在此 ,-->QUERYSTOREMASTERBEFORESCROLL-->SAVEmastercurrentRecord
// QueryInStoreMasteriNo.AsString:=IntToHex(Trunc(TimeStampToMSecs(DateTimeToTimeStamp(Now))),12);
//DBEDIT2.SETFOCUS;
END;
// queryinstoredetail.edit; //新加
QueryInStoreDetailiNo.AsString:=QueryInStoreMasteriNo.AsString;
QueryInStoreDetailfQuantity.AsFloat:=1;
QueryInStoreDetailfPrice.AsFloat:=0;
SHOWMESSAGE('HHH3');
//明细表的INO字段与主表的INO字段要相同。
end;

procedure TFormInStore.QueryInStoreMasterBeforeScroll(DataSet: TDataSet);
begin
// SHOWMESSAGE('HHH');
SaveMasterCurrentRecord;
SHOWMESSAGE('HHH2_QueryInStoreMasterBeforeScroll');
//移动记录前需要保存数据,否则Detail明细表的数据将会丢失。
end;

procedure TFormInStore.SaveMasterCurrentRecord;
var ThisDataSource:TDataSource;
begin
with FormMain.Database1 do
begin
ThisDataSource:=QueryInStoreDetail.DataSource;
QueryInStoreDetail.DataSource:=nil;
//这里需要将QueryInStoreDetail.DataSource置为nil是因为如果不设为空,
//QueryInStoreMaster.Post时会关闭并重新打开QueryInStoreDetail。这样将会丢失数据。
try
if QueryInStoreDetail.State in dsEditModes then//先将正在修改的数据提交到内存缓冲区。
QueryInStoreDetail.Post;
if QueryInStoreMaster.State in dsEditModes then//先将正在修改的数据提交到内存缓冲区。
QueryInStoreMaster.Post;
if QueryInStoreMaster.UpdatesPending or QueryInStoreDetail.UpdatesPending then
//如果缓存中有数据需要更新到服务器。
begin
if QueryInStoreMaster.UpdateStatus = usInserted then//如果当前记录是新增加的记录。
ApplyUpdates([QueryInStoreMaster, QueryInStoreDetail])//先更新主表再更新明细表,因为明细表中的INO字段必须在主表中存在。
else
if QueryInStoreDetail.UpdatesPending then
begin
QueryInStoreDetail.ApplyUpdates;
QueryInStoreDetail.CommitUpdates;
end;
ApplyUpdates([QueryInStoreDetail, QueryInStoreMaster]); //如果先录入明细记录而未录入主计录,会出现MASTER RECORD MISSING 错误;
end;

finally

QueryInStoreDetail.DataSource:=ThisDataSource;//恢复DataSource属性。
end;
end;
end;

procedure TFormInStore.QueryInStoreMasterBeforeDelete(DataSet: TDataSet);
begin
While not QueryInStoreDetail.IsEmpty do
QueryInStoreDetail.Delete;
//删除主表时首先删除从表否则主表保存会出错。因为明细表中INO字段有的值在主表中必须存在。
end;

procedure TFormInStore.DBNavigatorMasterBeforeAction(Sender: TObject;
Button: TNavigateBtn);
begin
if Button in [nbFirst, nbPrior, nbNext, nbLast] then
Exit;
//如果是以上四个按钮则执行内置动作。
case Button of
nbInsert: //把Insert动作改成插入动作。
DBNavigatorMaster.DataSource.DataSet.Append;
nbCancel:
begin
if QueryInStoreMaster.State in dsEditModes then
QueryInStoreMaster.Cancel;
QueryInStoreMaster.CancelUpdates;
QueryInStoreMaster.CommitUpdates;
//如果主表取消,则重要需要保存。
end;
nbDelete:
begin
QueryInStoreMaster.Delete;
SaveMasterCurrentRecord;
//主表删除时从表需要保存。
end;
nbPost:
SaveMasterCurrentRecord;
//提交时保存。
nbRefresh:
begin
QueryInStoreMaster.Close;
QueryInStoreMaster.Open;
//刷新时重新打开数据表。
end;
end;
Abort;
//不再执行内置动作。
end;

procedure TFormInStore.QueryInStoreDetailBeforeClose(DataSet: TDataSet);
begin
Try
//关闭前检查主表是否是插入状态,如果不是就保存数据。如果是,因为服务器没有相应的主表记录,所以从表不能保存。
if QueryInStoreDetail.UpdateStatus<>usInserted then
begin
if QueryInStoreDetail.State in dsEditModes then
QueryInStoreDetail.Post;
if QueryInStoreDetail.UpdatesPending then
begin
QueryInStoreDetail.ApplyUpdates;
QueryInStoreDetail.CommitUpdates;
end;
end;
Except
end;
end;

procedure TFormInStore.DBNavigatorDetailBeforeAction(Sender: TObject;
Button: TNavigateBtn);
begin
case Button of
nbRefresh:
begin
QueryInStoreDetail.Close;
QueryInStoreDetail.Open;
Abort;
//取消执行内置动作。
end;
nbInsert://把Insert动作改成插入动作。
begin
QueryInStoreDetail.Append;
Abort;
//取消执行内置动作。
end;
end;
end;

procedure TFormInStore.QueryInStoreDetailAfterClose(DataSet: TDataSet);
Var i:Integer;
begin
For i:=0 to DBGrid1.Columns.Count-1 do
DBGrid1.Columns.Title.Alignment:=taCenter;
//使用标题置中。
end;

procedure TFormInStore.QueryInStoreDetailCalcFields(DataSet: TDataSet);
begin
SumField.AsCurrency:=QueryInStoreDetailfPrice.AsCurrency*QueryInStoreDetailfQuantity.AsFloat;
//计算字段只能在这里赋值,其他地方赋值没有用,而这个事件中只能对计算字段赋值,因为对于其他任何字段的修改都将触发这个事件。
end;

procedure TFormInStore.FormActivate(Sender: TObject);
begin
WindowState:=wsMaximized;
end;

procedure TFormInStore.DataSourceInStoreDetailStateChange(Sender: TObject);
Var b:Boolean;
begin
b:=(QueryInStoreDetail.State in dsEditModes) or QueryInStoreDetail.UpdatesPending;
b:=b or (QueryInStoreMaster.State in dsEditModes) or QueryInStoreMaster.UpdatesPending;
TNavButton(DBNavigatorMaster.Controls[ORD(nbPost)]).Enabled:=b;
TNavButton(DBNavigatorMaster.Controls[ORD(nbCancel)]).Enabled:=b;
//改变导航接钮的状态/
end;

procedure TFormInStore.DataSourceInStoreDetailDataChange(Sender: TObject;
Field: TField);
Var b:Boolean;
begin
b:=(QueryInStoreDetail.State in dsEditModes) or QueryInStoreDetail.UpdatesPending;
b:=b or (QueryInStoreMaster.State in dsEditModes) or QueryInStoreMaster.UpdatesPending;
TNavButton(DBNavigatorMaster.Controls[ORD(nbPost)]).Enabled:=b;
TNavButton(DBNavigatorMaster.Controls[ORD(nbCancel)]).Enabled:=b;
//改变导航接钮的状态/
end;

procedure TFormInStore.QueryInStoreDetailiGoodsNoChange(Sender: TField);
begin
With TQuery.Create(Application) do
begin
Try
DatabaseName:=FormMain.Database1.DatabaseName;
SQL.Add('Select fPrice From Goods where ino=:INO');
ParamByName('INO').AsInteger:=Sender.AsInteger;
Open;
if (not IsEmpty) and (not FieldByName('fPrice').IsNull) then
QueryInStoreDetailfPrice.AsCurrency:=FieldByName('fPrice').AsCurrency
else
QueryInStoreDetailfPrice.AsCurrency:=1;
Finally
Free;
end;
end;
end;

procedure TFormInStore.DBEdit4KeyPress(Sender: TObject; var Key: Char);
begin
if key=#13 then
TDBEdit(Sender).Field.Text:=TDBEdit(Sender).Text;
end;

procedure TFormInStore.FormKeyPress(Sender: TObject; var Key: Char);
begin
IF key=#13 then { 判断是按执行键}
IF NOT (ActiveControl IS TDbgrid) THEN
Begin { 不是在TDbgrid控件内}
key:=#0;
perform(WM_NEXTDLGCTL,0,0);{移动到下一个控件}
end else
IF (ActiveControl is TDbgrid) Then{是在 TDbgrid 控件内}
begin
With TDbgrid(ActiveControl) Do
if Selectedindex<(FieldCount-1) then
Selectedindex:=Selectedindex+1{ 移动到下一字段}
else Selectedindex:=0;
end;
end;

end.
 
你想怎么样来实现你的触发器?
把你的想法写出来。ok?
 
TO:jerryjean

目的:对明细表QueryInStoreDetail的触发器进行修改,主要是为了试试数据校验,
在前台DBGRID控件中修改QueryInStoreDetail的数据,提交后给出校验信息,并回滚.
若校验通不过,如何向前台送提示信息.RAISERROR ('触发器提示: 数据库更新 [凭证]
资料失败, 请确认 [单据编号] 为 %s 所对应的 [凭证] 是否存在', 16, 1, @INO_D)
但现在发觉在后台用触发器向前台提示异常是很困难的,也达不到我的目的.
问题是:
1.RAISERROR有问题.信息如下:
其它用户修改了该表或视图的内容,你正在修改的数据库行在数据中已不存在.
2.出现题目上所指的问题.

现在看来自己功力有限,只有退回前台处理数据校验,异常处理,在不行退回FOX

 
你把你的触发器在后台建起来后,在'查询分析器'里面执行sql语句。
试着在后台触发这个异常,然后看看它报什么错~~~~~~~~~~~~~
在后台分析好了在往前台移。
 
to:jerryjean
你是热心人,在此表示感激,但问题还是每有解决:

1. 在SQL分析器理可通过,但在前台还是出现错误:
EDBENGINERROR WITH MESSAGE'NO USER TRANSACTION IS CURRENCLY IN PROGRESS,
COMMIT TRANSACTION 请求没有对应的BEGIN TRANSACTION
2. 在SQL分析器里的结果与在数据库右击键,打开表修改异常触发的结果不一样:(加RAISRROR时)
"其它用户修改了该表或视图的内容,你正在修改的数据库行在数据中已不存在."

不知为什么?
 
我认为,如果你采用的是缓存更新,在你调用保存数据到数据库的命令
(如TDatabase.ApplyUpdates),命令本身就包含了事务处理功能。
也就是说,事务是自动开始的,成功则自动commit,否则自动rollback。


在缓存更新模式下,还有一种显式客户端开始事务的方法,类似于:
TDatabase.StartTransaction;
try
数据集1.ApplyUpdates;
数据集2.ApplyUpdates;
。。。。。
TDatabase.Commit;
except
TDatabase.Rollback;
raise;
end;
数据集1.CommitUpdates;
数据集2.CommitUpdates;
。。。。。

以上所写,都是delphi帮助里的内容,供你参考

 
bde对mssql server的一些操作和sql 2000不完全兼容,她只能在sql6.5下完全正常。
 
感谢上面所有的DFW,无耐分太少,只能表示万分的感谢
 
我也遇到此问题,就是用adodatasset.updatebatch(arall)时,无法获得触发器中的错误,不知道怎么解决
 
后退
顶部