日期:2014-05-17  浏览次数:21227 次

简单问题再讨论:有主外键关系的多表一次插入
常遇到这样的情况:例如“文章”表是主表,“附件”表是子表,id都是用序列和触发器进行自增的。用户新建了一个文章,同时要给它添加附件,肯定只愿意点一个“保存”按钮。

常听到的解决办法:
一是插入之后按ID检索最大值,但有并发操作的时候很可能出错,必须在程序中采取一些保证措施,但可能经常导致操作失败,用户体验不好;

二是用存储过程,插入主表的时候返回序列的“currval”,再用这个值进行关联,但直接用的话,可能是因为一个存储过程完成之前不会改变序列的值,所以会报出“ PLS-00357: 在此上下文中不允许表, 视图或序列引用 '***.CURRVAL'”的错误;

三是放弃主键的自增,采用随机生成的ID,但这个办法实在是太笨,太浪费空间,效率也低,还不利于调试程序……

大家说说,有没有更好的办法?主要是方法二,能实现的话应该是最好的。

请大家谈谈思路,我会再加分的。

------解决方案--------------------
将nextval的值保存到程序的一个变量中,再插入字表
------解决方案--------------------
SQL code
-- 就1楼的思想效率最高,又不会出错!
-- 给你个例子:
CREATE OR REPLACE PROCEDURE sp_resource2_in_proc
( v_province_id  IN VARCHAR2,
  v_mobiletype   IN VARCHAR2,
  v_productid    IN VARCHAR2,
  v_goodsname    IN VARCHAR2,
  v_spname       IN VARCHAR2,
  v_messagecode  IN VARCHAR2,
  v_content      IN VARCHAR2,
  v_recontent    IN VARCHAR2,
  v_percharge    IN NUMBER,
  v_sendtimes    IN NUMBER,
  v_ishide       IN NUMBER,
  v_paytype      IN NUMBER
)
IS
  sqlstr1 VARCHAR2(4000);
  sqlstr2 VARCHAR2(4000);
  sqlstr3 VARCHAR2(4000);
  v_sub_goodsname VARCHAR2(20);
  v_goodsname_str VARCHAR2(4000);
  v_dot_var NUMBER(18,0);
  v_spid NUMBER(18,0);
  v_province_id2 VARCHAR2(1000);
BEGIN
  v_province_id2 := replace(v_province_id,',',''',''');

  v_goodsname_str := v_goodsname||',';
  select sp_chargeBusiness_seq.nextval into v_spid from dual;

  Insert into sp_chargebusiness(spid, spname,MESSAGECODE,CONTENT,RECONTENT,PERCHARGE) 
  values(v_spid,v_spname,v_messagecode,v_content,v_recontent,v_percharge);

  sqlstr1 := 'Insert into sp_resource2(countryid,province_id,area_id,mobile_type, productid,goodsname,spid,sendtimes,ishide,paytype)';
  sqlstr1 := sqlstr1||' SELECT DISTINCT m.countryid, m.province_id, m.area_id, m.mobile_type, '''||v_productid||'''';

  sqlstr2 := to_char(v_spid)||', '||to_char(v_sendtimes)||','||to_char(v_ishide)||', '||to_char(v_paytype)||' FROM mobileareamap m ';
  sqlstr2 := sqlstr2||' WHERE m.province_id in ('''||v_province_id2||''')';
  sqlstr2 := sqlstr2||' AND m.mobile_type in ('||v_mobiletype||') ';

  while length(v_goodsname_str)>1 loop
    v_dot_var := instr(v_goodsname_str,',',1,1);
    v_sub_goodsname := substr(v_goodsname_str,1,v_dot_var-1);
    v_goodsname_str := substr(v_goodsname_str,v_dot_var+1,length(v_goodsname_str)-v_dot_var);
    sqlstr3 := sqlstr1||','''||v_sub_goodsname||''','||sqlstr2;
    execute immediate sqlstr3;
  end loop;

  COMMIT;

END sp_resource2_in_proc;
/

------解决方案--------------------
控制对程序当前变量值的访问
------解决方案--------------------
探讨
引用:
控制对程序当前变量值的访问
怎讲?

------解决方案--------------------
学习!
------解决方案--------------------
文章的ID用GUID 生成。保存子表的时候,将生成的guid传给子表保存就行了
------解决方案--------------------
SELECT @@IDENTITY 

获取当前插入的自增ID
------解决方案--------------------
SQL code
SQL Server 2000中,有三个比较类似的功能:他们分别是:SCOPE_IDENTITY、IDENT_CURRENT 和 @@IDENTITY,它们都返回插入到 IDENTITY 列中的值。
nbsp;   ; IDENT_CURRENT 返回为任何会话和任何作用域中的特定表最后生成的标识值。IDENT_CURRENT 不受作用域和会话的限制,而受限于指定的表。IDENT_CURRENT 返回为任何会话和作用域中的特定表所生成的值。
     @@IDENTITY 返回为当前会话的所有作用域中的任何表最后生成的标识值。
     SCOPE_IDENTITY 返回为当前会话和当前作用域中的任何表最后生成的标识值SCOPE_IDENTITY 和 @@IDENTITY 返回在当前会话中的任何表内所生成的最后一个标识值。但是,SCOPE_IDENTITY 只返回插入到当前作用域中的值;@@IDENTITY 不受限于特定的作用域。
     例如,有两个表 T1 和 T2,在 T1 上定义了一个 INSERT 触发器。当将某行插入 T1 时,触发器被激发,并在 T2 中插入一行。此例说明了两个作用域:一个是在 T1 上的插入,另一个是作为触发器的结果在 T2 上的插入。
     假设 T1 和 T2 都有 IDENTITY 列,@@IDENTITY 和 SCOPE_IDENTITY 将在 T1 上的 INSERT 语句的最后返回不同的值。
     @@IDENTITY 返回插入到当前会话中任何作用域内的最后一个 IDENTITY 列值,该值是插入 T2 中的值。
     SCOPE_IDENTITY() 返回插入 T1 中的 IDENTITY 值,该值是