一下是一个订单插入的存储过程,但在执行时有时候提示出错,经过在ASP.NET中跟踪后发现即使是相同的数据有时候存储过程的执行会成功,有时候又失败,特别是在存储过程中加入事务处理后,出错的概率更高,去掉事务处理后出错的概率就比较低了,实在是搞不懂,请大家指教,谢谢!
ALTER PROCEDURE [dbo].[usp_AddOrderBill] 
@OrderBillCode varchar(50),
@CustomerId int,
@OperatorEmployeeId int,
@SellerEmployeeId int,
@PayTypeId int,
@OrderBillTypeId int,
@StoreHouseId int,
@OrderDate datetime,
@TotalMoney decimal(18,4),
@IsPayed bit,
@PayedDate datetime,
@IsAllShipedOut bit,
@Res varchar(50),
    @Visible bit
AS
BEGIN
declare @billCode varchar(50)
declare @todayCode varchar(50)   select @todayCode= convert(varchar(8),getdate(),112)
   set @todayCode=right(@todayCode,6)
   --begin transaction T
   select top 1 @billCode=OrderBillCode from tb_OrderBill order by OrderBillId desc
   if (@billCode is null)
   begin
           set @OrderBillCode='XS'+@todayCode+'001'
       end
   else
       begin
          if(@todayCode<>substring(@billCode,3,6))
             begin
                set @OrderBillCode='XS'+@todayCode+'001'
             end
          else
             begin
                 declare @code int
                 set @code=Convert(int,substring(@billCode,9,3))
                 set @code=@code+1
                 set @OrderBillCode='00'+convert(varchar(50),@code)
                 set @OrderBillCode=right(@OrderBillCode,3)
                 set @OrderBillCode='XS'+@todayCode+@OrderBillCode
             end
       end
--以上代码获取订单编号,订单编号是自动生成的,形式是"XS"+今日日期+顺序号,比如:“XS090729001”"XS090729002" 等
   INSERT INTO [dbo].[tb_OrderBill](
OrderBillCode,
CustomerId,
OperatorEmployeeId,
SellerEmployeeId,
PayTypeId,
OrderBillTypeId,
StoreHouseId,
OrderDate,
TotalMoney,
IsPayed,
PayedDate,
IsAllShipedOut,
Res,
        Visible
) VALUES (
@OrderBillCode,
@CustomerId,
@OperatorEmployeeId,
@SellerEmployeeId,
@PayTypeId,
@OrderBillTypeId,
@StoreHouseId,
@OrderDate,
@TotalMoney,
@IsPayed,
@PayedDate,
@IsAllShipedOut,
@Res,
        @Visible)
   --if(@@error<>0)
--begin
    --   rollback transaction T
    --   return 0
    --end
   --else
-- begin
      --commit transaction T
  return SCOPE_IDENTITY()
     --end
END

解决方案 »

  1.   

    首先先加上这个:
    BEGIN 
    set nocount on 
    declare @billCode varchar(50) 
    declare @todayCode varchar(50) 
    另外你的单据编号,可以参考一下这个:--关于ERP公司单据编码的问题。
    一般ERP企业里,要用到很多种类的单据,是有一定得编码规则的,如销售订单编码:Seord123,前面的字符为单据编号前缀,
    后面的数字为这类单据的流水号,依次加1,那他们是怎么实现这种单据编号的。
    他们一般是把各类单据的编号统一放在一个表里,其中字段有单据TYPE(单据类型),PREC(单据前缀),NO(当前的数字编号)等。
    步骤是这样的:
    --1、从这个表里取前缀和数字部分+1组合起来作为单据号。
    --2、将单据保存到业务表之前,先判断业务表里是否已存在这个单据编号,没有的话,就插入数据,然后更新单据编号表中的NO=NO+1
    --4、如果有的话,将该单据号的数字再加1,然后再进行插入判断,反复进行,直到插入数据为止,当然别忘了更新单据编号表中的NO为最新值。
      

  2.   


    --建议,你在可能出错的地,print 'dddddd' 一下!
      

  3.   


    --应该是数据值的,错误!
    1,把insert into 的语句,写成,字符串的!
    2,通过exec (@sql) 来执行!(在执行之前,先print(@sql)看他的正确性!)
      

  4.   

    其实你的这个存储过程,就是一个取单据号,一个插入语句。
    取单据号这块,要再优化一下。
    另外,单据一般是一个主表,一个从表。tb_OrderBill是主表吧?从表呢?
      

  5.   

    感觉生成单据号那段可以用一个select 完成,然后放到insert里
      

  6.   

    sdhdy:您所提的问题很正确,从表我是另外插入的。呵呵。这个主表的目的就是为了确保生成的单据号的唯一性才这么做的,
     
      

  7.   

    报错信息就是返回的 return SCOPE_IDENTITY() 的值为0,而不是实际的orderBillId的自增列编号的值
      

  8.   

    楼主的单据编号设了UNIQUE约束了吧?
    获得订单号这块,我帮你优化一下。
      

  9.   

    (★触发器专家VS触发器难题★) 您好,目前还没有加UNIQUE约束。而且这个表也确实是自增ID做主键的主键的字段名为orderBillId,这个字段是也订单明细表tb_OrderBillDetail的一个外键。而orderBillCode是订单的单据号,主要用于查询的问题确实是没有插入记录引起的,但问题是我通过在ASP.NET中调试发现在插入记录的数据几乎一模一样的情况下有的能成功,有的不能成功!真的是好奇怪的问题,
      

  10.   

    将插入订单明细表的操作也放到一起吧,理论上应该不存在没有明细的订单。另外,你返回订单序号的语句,应该可以直接计算得出吧,为什么还要查询一下呢?
    求订单号一段直接Select下L
    select 'xs'+@todaycode+right('00'+isnull(max(right(OrderBillCode,3)),0)+1,3) as billcode
      

  11.   

    你的取单据号那个,应该不会花费时间的,最好能做成一个函数,调用起来比较方便。
    你在取得下一个单据号的时候,别人也可能同时取得这个单据号,他先保存了,你的没保存。
    你在插入单据时,单据号就会有冲突。
    你在插入单据编号前,先判断你这个单据号,是否存在,不存在的时候插入,已经存在的话就重新取号。
    然后继续判断,直至插入数据。
    这样的话,就不会有问题的。
    国内一些大ERP厂商就这么做的,我以前做过。
      

  12.   

    (★触发器专家VS触发器难题★) :所以为了保证我获取的单据号的唯一性,我加了事务处理的。理论上应该能确保唯一性的。我想我这么编写的效率应该高于ERP厂商所采用的技术的效率啊,我只要查询一次数据库就可以的啊,
    (散人) :对应的订单明细表会有多条记录,我不知道怎么把多条记录的信息一次新传入到存储过程中。呵呵。所以我就把它独立到存储过程的外面利用ADO.NET来完成了,还请指教
      

  13.   

    大家好,我刚才又调试了一下,现把插入失败和插入成功的两个对象的数据值贴出来给大家看,这两个对象的内容几乎一模一样,(除了一个TOTALMONEY以外)左边栏目是插入失败的对象的三个截图,右边栏目的是插入成功的对象的三个截图,请大家帮忙分析产生的原因
    存储过程的完整代码如下:
    ALTER PROCEDURE [dbo].[usp_AddOrderBill] 
    @OrderBillCode varchar(50),
    @CustomerId int,
    @OperatorEmployeeId int,
    @SellerEmployeeId int,
    @PayTypeId int,
    @OrderBillTypeId int,
    @StoreHouseId int,
    @OrderDate datetime,
    @TotalMoney decimal(18,4),
    @IsPayed bit,
    @PayedDate datetime,
    @IsAllShipedOut bit,
    @Res varchar(50),
        @Visible bit
    AS
    BEGIN
    --set nocount on
    declare @billCode varchar(50)
    declare @todayCode varchar(50)   select @todayCode= convert(varchar(8),getdate(),112)
       set @todayCode=right(@todayCode,6)
       begin transaction T
       select top 1 @billCode=OrderBillCode from tb_OrderBill order by OrderBillId desc
       if (@billCode is null)
       begin
               set @OrderBillCode='XS'+@todayCode+'001'
           end
       else
           begin
              if(@todayCode<>substring(@billCode,3,6))
                 begin
                    set @OrderBillCode='XS'+@todayCode+'001'
                 end
              else
                 begin
                     declare @code int
                     set @code=Convert(int,substring(@billCode,9,3))
                     set @code=@code+1
                     set @OrderBillCode='00'+convert(varchar(50),@code)
                     set @OrderBillCode=right(@OrderBillCode,3)
                     set @OrderBillCode='XS'+@todayCode+@OrderBillCode
                 end
           end
       INSERT INTO [dbo].[tb_OrderBill](
    OrderBillCode,
    CustomerId,
    OperatorEmployeeId,
    SellerEmployeeId,
    PayTypeId,
    OrderBillTypeId,
    StoreHouseId,
    OrderDate,
    TotalMoney,
    IsPayed,
    PayedDate,
    IsAllShipedOut,
    Res,
            Visible
    ) VALUES (
    @OrderBillCode,
    @CustomerId,
    @OperatorEmployeeId,
    @SellerEmployeeId,
    @PayTypeId,
    @OrderBillTypeId,
    @StoreHouseId,
    @OrderDate,
    @TotalMoney,
    @IsPayed,
    @PayedDate,
    @IsAllShipedOut,
    @Res,
            @Visible)
       if(@@error<>0)
    begin
           rollback transaction T
           return 0
        end
       else
     begin
          commit transaction T
      return SCOPE_IDENTITY()
         end
    END
      

  14.   

    (熊猫咪咪) :主键列是orderbillId 它本身即使自增列的。而orderBillCode是订单编码,主要用户查询用的。
      

  15.   

    UPDATE    tb_Product
    SET              releaseTime = DATEADD(month, 1, releaseTime)
    WHERE     (DATEDIFF(month, releaseTime, GETDATE()) = 1) AND (productId < 100)
    这个代码尽然可以运行了!这里我通过限定productId来保证只有一条记录需要修改,竟然就成功了!真是奇怪啊,如果有两条记录需要修改则提示“子查询返回值不止一个”的错误!
      

  16.   

    (★触发器专家VS触发器难题★) :关键是我现在不知道我的存储过程的问题所在啊。要是改成你所说的像大的ERP厂商这么做的话我就需要大量修改源代码了。我现在的目的是要找出我的存储过程的问题所在。呵呵
      

  17.   

    你那个订单号码如果是一个人操作的话,不会有问题。
    如果有2个人几乎同时保存的话,就会出问题了,因为你的那个订单号码可能已经被别人占用了,所以你取得订单号码的时间要尽量短,保存的时候要判断你的那个订单号码是否在单据里已经存在了,如果存在的话,你要再取一个,就这么简单的道理。看看我在17楼写的,非常明白了。
    BYE
      

  18.   

    问题是我一个人在本机进行测试时也会出现这个插入失败的错误啊。一个人在本机测试根本没有并发的问题。而且即使是在并发情况下,我对订单号的获取以及插入是在一个事务处理中完成的,应该也不会出现重复问题。而且即使有出现重复的问题,但我没有对orderbillCode字段建立唯一约束,因此也不会出现不能插入的问题啊。
      

  19.   

    你试试把获得最大编号那段直接写到INSERT语句里
      

  20.   

    [sql]declare @code int 
                    set @code=Convert(int,substring(@billCode,9,3)) 
                    set @code=@code+1 
                    set @OrderBillCode='00'+convert(varchar(50),@code) 
                    set @OrderBillCode=right(@OrderBillCode,3) 
                    set @OrderBillCode='XS'+@todayCode+@OrderBillCode 这段代码有问题,当今天的单据超过9时,在录入新的单据,单据号会超过定义的单据号长度
    改成
    set @code=Convert(int,right(@billCode,3)) 
      

  21.   

    应该没有问题,因为以下代码保证了值取右边的三位
    set @OrderBillCode=right(@OrderBillCode,3) 
      

  22.   

    还没搞定?
    看看这个表:tb_OrderBill触发器的情况。
      

  23.   

    单号算法问题。单号设定了唯一约束数据量大,且并发高时,一算就算好几个重的,自然会出错了。用个表保存最大单号,一行一列,不建任何索引,每个进程算号时,先update单号+1, 再取出来, 这样update的锁定时间是很短的。
    不要把update的操作和后面插新单的操作放在一个事务中, 这样造成的对单号保存表的占用时间极短,就算后面的单号插入操作不成功,唯一造成的问题就是造成单号断号,并不影响实际使用。
      

  24.   

    问题基本已经定位到了,就是主外键约束的问题,但奇怪的是我在SQL中多次重复执行如下的存储过程(数据是一样的)大部分的情况下是可以成功执行的,只有少部分的情况下出现下面的错误,真是百思不得其解
    EXEC @return_value = [dbo].[usp_AddOrderBill]
    @OrderBillCode = N'XS090730017',
    @CustomerId = 657,
    @OperatorEmployeeId = 47,
    @SellerEmployeeId = 46,
    @PayTypeId = 1,
    @OrderBillTypeId = 1,
    @StoreHouseId = 4,
    @OrderDate = N'2009-7-30 0:00:00',
    @TotalMoney = 6564,
    @IsPayed = false,
    @PayedDate = N'1900-1-1 0:00:00',
    @IsAllShipedOut = true,
    @Res = N' ',
    @Visible = trueSELECT 'Return Value' = @return_valueGO消息 547,级别 16,状态 0,过程 usp_AddOrderBill,第 51 行
    INSERT 语句与 FOREIGN KEY 约束"FK_tb_OrderBill_tb_Customer"冲突。该冲突发生于数据库"MISServer2",表"dbo.tb_Customer", column 'CustomerId'。
    语句已终止。
      

  25.   

    而且在tb_Customer表中确实是有customerId=657的客户信息的,应该是满足它的主外键约束条件的啊