需求;假设AB2个表,分别有设有主键自增长,
A:AID,AName
B:BID,BName,AID
现需要同时往2个表插入数据,
A先插入数据
B再插入数据 需要用到A刚插入的主键作为两表的关联字段
因为是要同时进行插入,所以用了ado.net进行事务处理
我的解决方案是:
插入A表sql语句:
declare @AID_Declare BigInt;Insert into A values('SSS');set @AID_Declare=@@Identity;
插入B表sql语句:
Insert into B values('DDD',@AID_Declare);
这样做,就不能使用SqlParameter了(declare @与sqlparameter里面@冲突),苦于没有好的解决方案,于是我弃用了sqlparameter,都是一点一点拼sql语句
,由于需求的变化,这些表一直再变化字段,导致经常出错,而且由于没有参数化,输入一些奇怪的特殊附号就会报错,求大神指点一二
我有一计,在你的表中增加一列 把这个列当作 你自己定义的主键。不过这个主键是GUID的。
比如说,你增加一个数据
在这里声明主键
string PKey=GUID.newGUID().tostring("N");
然后 把这条数据插入数据库。
在下文中 直接使用这个新增加的主键PKey就行了。
这样做我也想过,那我主键关联就没有任何意义了,而且不符合规矩吧
@威尼斯de: 不符合啥规矩?
你这是一堆问题,不是一个问题啊。
1、你还在使用原始的ADO.NET保存数据? 不过也没关系。
2、你现在的问题是插入主表后,需要取得ID,然后作为子表的ParentID插入。
3、表的字段一直变化,这个得你们内部想怎么办,别人帮不上忙了。
4、所以,你主要的问题是,特殊符号会报错?
如果只是第四个问题,那么也很好办,
第一种方法是参数化,第二种方法是字符转义。
我主要想实现多表插入数据进行事务处理+参数化,实现这个功能,其他的只是阐述一下原因
有一个inserted.id可以直接输出插入的值,你直接执行查询获取第一行第一列就取到ID了
我是要进行事务处理的,所以不能这么干
@威尼斯de: 为什么进行事务处理不能这么干....我在事务里这样做的多了..
@吴瑞祥: 那就不是ado.net的事务处理了吧?
@威尼斯de: 当然是啦...
@吴瑞祥: 那,求贴个技术解决方案,歇歇大神,稍微具体一点
@威尼斯de: 谢谢。。
@威尼斯de: 没有啊.就是直接那样写啊.哦,明白了.你是说在一条sql里做?
@吴瑞祥: 当然不是
@威尼斯de: 楼下帮你写代码了.我不帮人写代码的.
一,下面是用事务实现的你的A,B表插入,用的参数化,代码如下
SqlTransaction tran = null; try { string connectionString = ConfigurationManager.ConnectionStrings["connctionString"].ConnectionString; using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); tran = conn.BeginTransaction(); using (SqlCommand cmd = conn.CreateCommand()) { cmd.Transaction = tran; cmd.CommandText = "insert into A values(@AName);select @@Identity"; cmd.Parameters.Add("@AName", "testName"); int id = Convert.ToInt32(cmd.ExecuteScalar()); cmd.CommandText = "insert into B values(@BName,@AID)"; cmd.Parameters.Add("@BName", "testName"); cmd.Parameters.Add("@AID", id); cmd.ExecuteNonQuery(); MessageBox.Show("插入成功"); } tran.Commit(); } } catch (Exception ex) { tran.Rollback(); throw; }
二,如果参数化不能实现,遇到特殊符号比如单引号之类的,可以在插入之前做判断比如如果包含单引号则加上转义符或多加单引号等等。
那如果事务处理已经被封装城一个类,就不能这么写了吧,如:
/// <summary> /// 执行多条SQL语句,实现数据库事务。 /// </summary> /// <param name="SQLStringList">SQL语句的(key为sql语句,value是该语句的SqlParameter[])</param> public static void ExecuteSqlTran(List<SqlKeys> SQLStringList) { using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); using (SqlTransaction trans = conn.BeginTransaction()) { SqlCommand cmd = new SqlCommand(); try { //循环 foreach (SqlKeys myDE in SQLStringList) { string cmdText = myDE.Sql; SqlParameter[] cmdParms = myDE.SqlParameter; PrepareCommand(cmd, conn, trans, cmdText, cmdParms); int val = cmd.ExecuteNonQuery(); cmd.Parameters.Clear(); } trans.Commit(); } catch { trans.Rollback(); throw; } } } } public class SqlKeys { private string _sql; private SqlParameter[] _sqlParameter; public SqlKeys() { } public SqlKeys(string sSql, SqlParameter[] spSqlParameter) { this.Sql = sSql; this.SqlParameter = spSqlParameter; } public string Sql { get { return this._sql; } set { this._sql = value; } } public SqlParameter[] SqlParameter { get { return this._sqlParameter; } set { this._sqlParameter = value; } } }
其实我不明白楼主为什么一直说不能参数化
@稳稳的河: 因为我用的上面贴出来的代码作为事务处理方法,使用了declare @声明参数 ‘@’和sqlparameter的‘@’冲突
@稳稳的河:
插入A表sql语句:
declare @AID_Declare BigInt;Insert into A values('SSS');set @AID_Declare=@@Identity;
插入B表sql语句:
Insert into B values('DDD',@AID_Declare);
这样做,就不能使用SqlParameter了(declare @与sqlparameter里面@冲突)
@威尼斯de: 嗯额(⊙o⊙)?,你的A,B表如何是一对一还是一对多?,传参数的时候如何对应的?
@火悬崖: 一对多
@威尼斯de: 那调用ExecuteSqlTran这个方法,打算怎么对应AB之间的关系?
@火悬崖: 一个A对应多个b
@威尼斯de:
你的这二个SQL语句是打算怎么用的?
@火悬崖:
声明变量=A表刚插入的一条数据的主键
然后紧接着程序循环 需要插入到表B的数据,拼接成sql,关联两表用到A的主键(即声明的变量)
最后事务处理,不过我这应该是很多条sql语句一次性执行了
@威尼斯de:
”declare @AID_Declare BigInt;Insert into A values('SSS');set @AID_Declare=@@Identity;Insert into B values('DDD',@AID_Declare);Insert into B values('DDD',@AID_Declare);Insert into B values('DDD',@AID_Declare);Insert into B values('DDD',@AID_Declare);“这种形式?
@火悬崖: 是的
@火悬崖: 如果是我就写成存储过程,后面变化也好改,不太推荐一直拼接SQL,将来别人给你维护,基本会是重写
@稳稳的河: 菜鸟没写过,公司也从来不写...,连个参照都没有...
@稳稳的河: 额,如果一个A对应5个B,用存储过程该怎么传参数呢?
@威尼斯de: 如果拼接SQL语句,下面的方法能避免特殊符号吗?string.Format("select * from Users(nolock) where UserID in({0})", userIds);
其实这只是一个缩影,b可能作为主表,对应多个C,那就完了,会声明重复的@Bid_declare,就不能使用,这就是刚测出来的
@火悬崖:
SqlTransaction tran = null; try { string connectionString = ConfigurationManager.ConnectionStrings["connctionString"].ConnectionString; using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); tran = conn.BeginTransaction(); using (SqlCommand cmd = conn.CreateCommand()) { cmd.Transaction = tran; cmd.CommandText = "insert into A values(@AName);select @@Identity"; cmd.Parameters.Add("@AName", "testName"); int id = Convert.ToInt32(cmd.ExecuteScalar()); cmd.CommandText = "insert into B values(@BName,@AID)"; cmd.Parameters.Add("@BName", "testName"); cmd.Parameters.Add("@AID", id); cmd.ExecuteNonQuery(); MessageBox.Show("插入成功"); } tran.Commit(); } } catch (Exception ex) { tran.Rollback(); throw; }
这种方式可以实现,但是无法封装到底层吧
@威尼斯de: 嗯,你可以针对你这个业务用上面的代码封装一个方法,专门为这个业务调用。
推荐复杂一些的就写成存储过程,别去拼接SQL语句了
这样的写存储过程也不好,你可以先插入insert into A values(@AName);返回id,再插入后面的5个表,如果插入失败,那么就回滚,没有必要一定像写存储过程一样,定义变量,这是给自己找了很多事情
请不要使用@@Identity,一般这种拿主键要用SCOPE_IDENTITY()