使用Delphi+MSSQL实现多物料进出仓

本文介绍了一种在Delphi客户端批量处理多物料进出仓的方法,通过将数据转换为特定格式的字符串并传递给SQL Server存储过程,实现了高效的数据处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

前几天写了关于单物料进出仓的存储过程,现在要求从Delphi 7编写的Client端可以同时操作多物料进仓,也就是说可以从客户端一次向数据库提交多条记录。

  在这个题目中,有人采用了在数据库中建立临时表,每次在客户端增加一条记录,就将这条记录提交到临时表,输入结束后,通过调用SQL SERVER 中的存储过程将临时表中的记录再转到正式表中。这种办法可行,但是每增加一条记录,都需将记录先送入临时表,这样就需要访问一次数据库,无疑增加了客户端和数据库之间的通信量,造成不必要的浪费。

  但是,由于SQL SERVER不能接受数组参数,所以无法在Client端将多条记录用数组形式提交到SQL SERVER 的存储过程,这时可以使用一种变通的办法。

  由于 SQL SERVER 有强大的字符串处理能力,因此可以将输入的多条进出仓记录在Client以一定得格式组织成字符串,然后使用SQL SERVER 中的字符串处理功能将提交的字符串再一次分割成独立的记录,供进出仓操作。    

  以下是具体的实现。
  首先建立一个视图:
create view multigio
as
      select j060230gtb.gid,j060230gtb.gname,j060230gtb.gunit,j060230giotb.iosize,j060230giotb.iomoney,j060230giotb.remarks,j060230giotb.iodate,j060230utb.uid from j060230utb,j060230gtb,j060230giotb
where 1=2

  该视图用来在Client端使用ADOTable生成一个包含所需参数的空表(之所以是空表是因为where 1=2)。为了能直观的看到输入的结果,再添加一个TDBGrid组件,将其和ADOTable组件关联。设置ADOTable不自动提交更新,否则就出错了,因为它对应的是一个多张表连接得到视图,这个ADOTable只是用来暂存数据的,里头的数据是永远不送到数据库的。

  进出仓记录添加完成后,通过循环将ADOTable的记录按行循环读出,每个字段之间用指定的符号分割(本例使用的是键盘TAB键上面的那个'`'),记录之间用不同的分隔符,本例使用分号";",连接成一个字符串。然后调用指定的存储过程,将这个字符串作为参数。Delphi代码如下:

if RbtnMulti.Checked = true then //多物料
  begin
      //构造字符串
      rec_set := '';
      DBGridTmpView.DataSource.DataSet.First;
      if DBGridTmpView.DataSource.DataSet.FieldByName('gid').AsString = '' then
      begin
          showmessage('请输入信息!');
          exit;
      end;
      while not DBGridTmpView.DataSource.DataSet.eof do
      begin
        rec_set := rec_set + DBGridTmpView.DataSource.DataSet.FieldByName('gid').AsString + '`';
        rec_set := rec_set + DBGridTmpView.DataSource.DataSet.FieldByName('gname').AsString + '`';
        rec_set := rec_set + DBGridTmpView.DataSource.DataSet.FieldByName('gunit').AsString + '`';
        rec_set := rec_set + DBGridTmpView.DataSource.DataSet.FieldByName('iosize').AsString + '`';
        rec_set := rec_set + DBGridTmpView.DataSource.DataSet.FieldByName('iomoney').AsString + '`';
        rec_set := rec_set + DBGridTmpView.DataSource.DataSet.FieldByName('iodate').AsString + '`';
        rec_set := rec_set + DBGridTmpView.DataSource.DataSet.FieldByName('remarks').AsString + '`';
        rec_set := rec_set + DBGridTmpView.DataSource.DataSet.FieldByName('uid').AsString + '`';
        rec_set := rec_set + ';';
        DBGridTmpView.DataSource.DataSet.Next;
      end;
      //showmessage(rec_set);
      with DataMo.ADOSProcMGio do
      begin
        close;
        Parameters.ParamByName('@m_rec_set').Value := rec_set;
        if RbtnPush.Checked = true then
         Parameters.ParamByName('@m_iomark').Value := '1'
        else
         Parameters.ParamByName('@m_iomark').Value := '0';
        Parameters.ParamByName('@m_stat').Value := '';
        Parameters.ParamByName('@m_msg').Value := '';
        execProc;
        pmsg := Parameters.ParamByName('@m_stat').Value;
        billno := Parameters.ParamByName('@m_msg').Value;
        showmessage(pmsg+ ' 单号:' + billno);
        RbtnSingle.Checked := true;
        RefreshTables(billno,'');
      end;
  end;

  通过以上代码可以将输入连接成符合存储过程切分需要格式的字符串,并且执行进出仓的操作,里头的RefreshTables()函数是自定义的。

  一下是具体的存储过程代码:

--drop proc ProcMultiGio
/*-----------------------多物料进出仓-------------------------*/
/*该存储过程使用";"(分号)做记录分割符,使用"`"(TAB键上面的键)做字段分割符*/
create proc ProcMultiGio
    @m_rec_set varchar(2000),     --客户端提交的总集合
    @m_iomark char(1),              --进出标志
    @m_stat varchar(30) output,   --返回状态
    @m_msg varchar(10) output     --成功返回单号
  as
  declare
      --内部字段参数
      @m_gid char(4),             --1、物料代码
      @m_gname varchar(20),       --2、物料名称
      @m_gunit varchar(8),        --3、物料计量单位
      @m_iosize numeric(10,2),    --4、进出数量
      @m_iomoney numeric(10,2),   --5、进出钱数
      @m_iodate datetime,         --6、进出日期
      @m_remarks varchar(60),     --7、备注信息
      @m_uid char(4),             --8、操作人员代码
      --内部中间量
      @m_billno char(10),         --单号
      @rowpoint int,              --行分割符索引
      @colpoint int,              --列分割符索引
      @eachrow char (200),        --分割出来的整行
      @m_storesize numeric(10,2), --库存量
      @p_space  varchar(2),       --填充
      @p_now    varchar(12)       --时间串
  begin transaction  --启动事务
   /*生成单号*/
   set @m_billno = (select top 1 billno from j060230giotb order by bid desc);/*获取最后一个单号*/
  
   if @m_billno IS NULL
     set @m_billno = 'S000000000';
  
   set @m_billno = substring(@m_billno,8,3);
   set @m_billno = convert(varchar(10),(cast(@m_billno as int) + 1));
   set @p_space = replace(space(3-len(@m_billno)),' ','0');
   set @p_now = (SELECT stuff(replace(CAST(GETDATE() AS CHAR(10)),' ',''),5,2,''));
   set @m_billno = 'T' + @p_now + @p_space + @m_billno; /*形成最终单号 进出标志+时间+填充0 + 编号*/
   /*单号生成完成*/
  
   /*进行字符串的拆分*/
   while len(@m_rec_set) > 0  --循环进行记录分割
   begin
      select @rowpoint = charindex(';',@m_rec_set) --读第一个记录分割符的字符串索引
      select @eachrow  = substring(@m_rec_set,1,@rowpoint-1) --取出一条记录
      select @m_rec_set = substring(@m_rec_set,@rowpoint+1,len(@m_rec_set)-@rowpoint) --从记录集合中删除读过的记录
      
      if len(@eachrow) > 0 --还有记录,对行进行字段分割,将个值赋予每个字段参数
      begin
  --取第一个字段gid->@m_gid
         select @colpoint   = charindex('`',@eachrow) --第一字段末尾
         select @m_gid      = substring(@eachrow,1,@colpoint-1) --去第一个字段
         select @eachrow    = substring(@eachrow,@colpoint+1,len(@eachrow)-@colpoint) --取剩下的字符串
        
         -- 取第二个字段gname->@m_gname
         select @colpoint   = charindex('`',@eachrow)
         select @m_gname    = substring(@eachrow,1,@colpoint-1)
         select @eachrow    = substring(@eachrow,@colpoint+1,len(@eachrow)-@colpoint)
         --取第三个字段gunit->@m_gunit
         select @colpoint   = charindex('`',@eachrow)
         select @m_gunit    = substring(@eachrow,1,@colpoint-1)
         select @eachrow    = substring(@eachrow,@colpoint+1,len(@eachrow)-@colpoint)
         --取第四个字段iosize->@m_iosize
         select @colpoint   = charindex('`',@eachrow)
         select @m_iosize   = substring(@eachrow,1,@colpoint-1)
         select @eachrow    = substring(@eachrow,@colpoint+1,len(@eachrow)-@colpoint)
         --取第五个字段iomoney->@m_iomoney
         select @colpoint   = charindex('`',@eachrow)
         select @m_iomoney  = substring(@eachrow,1,@colpoint-1)
         select @eachrow    = substring(@eachrow,@colpoint+1,len(@eachrow)-@colpoint)
         --取第六个字段iodate->@m_iodate
         select @colpoint   = charindex('`',@eachrow)
         select @m_iodate   = substring(@eachrow,1,@colpoint-1)
         select @eachrow    = substring(@eachrow,@colpoint+1,len(@eachrow)-@colpoint)
         --取第七个字段remarks->@m_remarks
         select @colpoint   = charindex('`',@eachrow)
         select @m_remarks  = substring(@eachrow,1,@colpoint-1)
         select @eachrow    = substring(@eachrow,@colpoint+1,len(@eachrow)-@colpoint)
         --取第八个字段uid->@m_uid
         select @colpoint   = charindex('`',@eachrow)
         select @m_uid      = substring(@eachrow,1,@colpoint-1)
         select @eachrow    = substring(@eachrow,@colpoint+1,len(@eachrow)-@colpoint)
        
         --以下根据取得的参数判断进出仓操作
         if @m_iomark = '1'  --if input
         ---------进仓--------------------
         begin
           if exists (select gid from j060230gtb where gid=@m_gid)  /*判断是否物料是否存在*/
               update j060230gtb set gsize=gsize+@m_iosize where gid=@m_gid; /*存在就更新物料的库存数量*/
           else
               insert into j060230gtb (gid,gname,gsize,gunit) values (@m_gid,@m_gname,@m_iosize,@m_gunit); /*不存在先添加物料到物料表*/
          
           insert into j060230giotb (billno,gid,iosize,iomark,iomoney,remarks,uid,iodate)  /*将进仓信息进表*/
                              values(@m_billno,@m_gid,@m_iosize,'1',@m_iomoney,@m_remarks,@m_uid,@m_iodate)          
         end   --end if input
         ---------进仓------------------

         if @m_iomark = '0'  --if output
         ---------出仓------------------
         begin
           if not exists (select gid from j060230gtb where gid=@m_gid)  /*判断是否物料是否存在*/
           begin --物料不存在
            select @m_stat = @m_gid + '物料不存在!出库失败!'
            select @m_msg  = '无'
            rollback transaction  --事务回滚
            return --结束存储过程
           end
          
           --物料存在
           set @m_storesize =(select gsize from j060230gtb where gid=@m_gid) --读库存量
           if @m_storesize < @m_iosize --不够出
           begin
             select @m_stat = @m_gid +'库存不足,出库失败!'
             select @m_msg  = '无'
             rollback transaction     --事务回滚
             return  --结束存储过程
           end
          
           --满足出仓条件  
           update j060230gtb set gsize=gsize-@m_iosize where gid=@m_gid   --存在就更新物料的库存数量
           -- set @m_stat = '出库成功!'    
           insert into j060230giotb (billno,gid,iosize,iomark,iomoney,remarks,uid,iodate)  /*将出仓信息进表*/
                              values(@m_billno,@m_gid,@m_iosize,'0',@m_iomoney,@m_remarks,@m_uid,@m_iodate)
           --select @m_msg = @m_billno
          
         end  --end if output          
         ---------出仓--------------------
      end --end if

   end --end while
  
  select @m_stat = '操作成功!'
  select @m_msg  = @m_billno
  commit transaction  --end transaction
go
/*-----------------多物料进出仓----------------------------*/

这个存储过程完成了记录的切分和进出仓的操作。

到此多物料进出仓就完成了。

补充:
前一次单物料和这次的多物料共同的缺陷在于生成单号的地方。两次所使用的单号生成方法是相同的,但是这种方法的原理是读最后的单号,生成本次的单号。如果考虑到并发,这种的问题是显而易见的,非常容易出错。因此,这个问题需要采用另外的办法解决,考虑到与本文无关,这里不详细写。
库存管理系统 实验课题一、人员档案的管理物料档案管理 设计实现对企业人员基本情况的档案的输入、删除与修改,企业人员基本情况主要包括:人员代码、姓名、性别、出生日期、身份证号、籍贯、家庭住址、联系电话、其它情况等。 1. 程序须包括主界面程序,程序名用“班级名+座号+main”,主界面可以通过菜单调用到其它所有子程序。 2. 人员档案的增加、修改、删除的程序,其中人员档案表在数据库中必须以“班级+座号+(表示人员的中英文信息)”来命名,人员档案的信息管理程序也必须以“班级+座号+(表示程序名的中英文信息)”来命名。其中人员代码不能为空,性别必须用“男”、“女”选择方式,出生日期必须用日期控件来控制。修改、删除程序在执行修改删除操作前,都必须行记录定位操作,所以必须有相应的查询程序,可以借用查询程序来完成记录定位。人员档案的增加、修改、删除程序也可以用三个程序名,分别编程。 3. 人员档案查询程序,程序名以“班级+座号+(表示程序名的中英文信息)”来命名,可以在查询框中输入人员姓名中的任何一个单字,查询出含该字的所有人员。 物料档案包括物料代码、物料名称、规格型号、计量单位、库存数量、备注等字段。 1. 物料档案的增加、修改、删除程序,程序名以“班级+座号+(表示程序名的中英文信息)”来命名,具体内容参考人员档案管理程序。物料表在数据库中以“班级+座号+(表示程序名的中英文信息)”来命名表,客户端输入时要求物料代码不为空,计量单位可以选择“件、套、公斤、吨、升、米、毫米、个”等,计量单位并可以输入其它单位名,库存数量默认为0。 2. 物料查询程序可以在通过输入框的文字行模糊查询(物料代码、名称、规格中是否含有输入框的文字),程序名用“班级+座号+(表示程序名的中英文信息)”来命名。 实验课题二、简单物料仓/出仓信息管理 企业物料出仓的信息管理物料出仓的信息主要包括出仓单号、出仓日期、操作人员代码、备注、物料代码、仓数量、出仓数量等 1. 企业仓库出仓信息表以“班级+座号+(表示程序名的中英文信息)”来命名,可以用一个表,也可以用二个表(一对关系),具体表设计的思路须在报告中描述。 2. 仓程序与出仓程序可以合并,也可以分开做,名称以“班级+座号+(表示程序名的中英文信息)”来命名。其中操作人员必须用下拉框来选择;单号要自己编规则自动生成,单号产生规则在报告中说明;出仓日期用日期控件;物料代码必须下拉框做选择;当物料出仓时必须判断出仓数量时候超过物料的库存数量,物料的库存数量在物料表中可以获得该物料的库存数量。仓或出仓操作时必须通过调用存储过程来实现存储过程将操作结果返回调用者,显示成功或失败的信息。 3. 仓、出仓存储过程可以合并或分开做,存储过程名称以“班级+座号+(表示程序名的中英文信息)”来命名,存储过程要判断物料是否存在,操作后物料库存数量是否不为负数;如果不满足条件显示,失败提示;如果满足条件,增加出仓表记录一条,并根据出仓物料的数量对物料表中库存数量行修改,提交数据库操作,否则回滚。 4. 出仓单查询程序,以“班级+座号+(表示程序名的中英文信息)”来命名,可以用出仓日期起止条件,物料代码、操作人员、备注(用子串查询)来完成。 实验课题三、统计程序设计与报表打印 按物料统计出仓流量,计算分析流动量最小的物料。按月份打印出仓单表,打印出仓单,按物料打印仓库账本。 1. 物料统计程序,统计各物料在指定时间内的出仓数量总数,用图形显示,程序名以“班级+座号+(表示程序名的中英文信息)”来命名。 2. 打印出仓单程序,给定月份,将该月出仓单全部按单号顺序打印出来,程序名以“班级+座号+(表示程序名的中英文信息)”来命名。 3. 打印仓库账本程序,给定年份和物料,打印物料代码、名称、规格、计量单位、各日期的仓、出仓和库存量;程序名以“班级+座号+(表示程序名的中英文信息)”来命名。 实验课题四、用户登录与权限设计 用户在这里就指操作人员,已经在人员档案中管理,通过设计用户登录和用户权限的管理实现用户按权限访问资源,管理员按要求授予用户权限,登录程序安全稳定,设计用户菜单及程序调用。人员表中需要新增字段口令、用户权限。系统中每个操作人员都可以作为用户,当一个用户权限修改时,不会影响其他用户;可以对每个用户单独授予某个程序的执行权,也可以收回其执行权,而不影响其他用户的权限; 1. 用户登录程序,名称用以“班级+座号+(表示程序名的中英文信息)”来命名,输入用户名和口令,判断正确后,按用户的权限,显示有权的菜单项,无权菜单项必须不可见或不可操作。 2. 用户授权程序,名称用以“班级+座号+(表示程序名的中英文信息)”来命名。有授权权限的用户,可以指定某个用户获取某个程序执行权,或被禁止某个程序的执行权。 实验课题五、物料物料出仓信息管理 一般企业物料出仓时,在一个出仓单号上同时对物料做同或同出操作,通过程序实现物料在一个界面一个单号上同时实现仓或出仓操作,如果不成功,必须全部回滚。 1. 物料出仓,可以在一个程序中完成,也可以分开编程,程序名以“班级+座号+(表示程序名的中英文信息)”来命名,同一单号中出仓单号、出仓日期、操作人员必须完全相同,同一单号中不能仓与出仓同时存在,通过调用存储过程完成出仓操作。 2. 物料出仓存储过程,过程名以“班级+座号+(表示程序名的中英文信息)”来命名,物料出仓单增加到表中,并修改物料的库存数量,失败就显示提示内容。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值