對于這樣的表結(jié)構(gòu),我們最常見的問題就是保存的時(shí)候怎樣處理鍵值的問題,因?yàn)閮蓚€(gè)表關(guān)聯(lián)非常的緊密,我們進(jìn)行保存的時(shí)候需要把它們放在一個(gè)事務(wù)里面,這時(shí)問題就會(huì)出現(xiàn),order表中的id是自動(dòng)增長型的字段。現(xiàn)在需要我們錄入一張訂單,包括在order表中插入一條記錄以及在orderdetail表中插入若干條記錄。因?yàn)閛rder表中的id是自動(dòng)增長型的字段,那么我們在記錄正式插入到數(shù)據(jù)庫之前無法事先得知它的取值,只有在更新后才能知道數(shù)據(jù)庫為它分配的是什么值,然后再用這個(gè)id作為orderdetail表的orderid的值,最后更新oderdetail表。但是,為了確保數(shù)據(jù)的一致性,order與orderdetail在更新時(shí)必須在事務(wù)保護(hù)下同時(shí)進(jìn)行,即確保兩表同時(shí)更行成功,這個(gè)就會(huì)有點(diǎn)困擾。
解決這類問題常見的主要有兩類方法:
一種是微軟在網(wǎng)上書店里使用的方法,使用了四個(gè)存儲(chǔ)過程。改裝一下,使之符合現(xiàn)在的例子
--存儲(chǔ)過程一
create procedure insertorder
@id int = null output,
@orderdate datetime = null,
@productidlist nvarchar(4000) = null,
@numlist nvarchar(4000) = null,
@pricelist nvarchar(4000) = null
as
set nocount on
set xact_abort on
begin transaction
--插入主表
insert orders(orderdate) select @orderdate
select @id = @@identity
-- 插入子表
if @productidlist is not null
execute insertorderdetailsbylist @id, @productidlist, @numlist, @pricelist
commit transaction
return 0
--存儲(chǔ)過程二
create procedure insertorderdetailsbylist
@id int,
@productidlist nvarchar(4000) = null,
@numlist nvarchar(4000) = null,
@pricelist nvarchar(4000) = null
as
set nocount on
declare @length int
declare @firstproductidword nvarchar(4000)
declare @firstnumword nvarchar(4000)
declare @firstpriceword nvarchar(4000)
declare @productid int
declare @num int
declare @price money
select @length = datalength(@productidlist)
while @length > 0
begin
execute @length = popfirstword @@productidlist output, @firstproductidword output
execute popfirstword @numlist output, @firstnumword output
execute popfirstword @pricelist output, @firstpriceword output
if @length > 0
begin
select @productid = convert(int, @firstproductidword)
select @num = convert(int, @firstnumword)
select @price = convert(money, @firstpriceword)
execute insertorderdetail @id, @productid, @price, @num
end
end
--存儲(chǔ)過程三
create procedure popfirstword
@sourcestring nvarchar(4000) = null output,
@firstword nvarchar(4000) = null output
as
set nocount on
declare @oldword nvarchar(4000)
declare @length int
declare @commalocation int
select @oldword = @sourcestring
if not @oldword is null
begin
select @commalocation = charindex(',',@oldword)
select @length = datalength(@oldword)
if @commalocation = 0
begin
select @firstword = @oldword
select @sourcestring = null
return @length
end
select @firstword = substring(@oldword, 1, @commalocation -1)
select @sourcestring = substring(@oldword, @commalocation + 1, @length - @commalocation)
return @length - @commalocation
end
return 0
------------------------------------------------
--存儲(chǔ)過程四
create procedure insertorderdetail
@orderid int = null,
@productid int = null,
@price money = null,
@num int = null
as
set nocount on
insert orderdetail(orderid,productid,price,num)
select @orderid,@productid,@price,@num
return 0
插入時(shí),傳入的子表數(shù)據(jù)都是長度為4000的nvarchar類型,各個(gè)字段使用“,”分割,然后調(diào)用popfirstword分拆后分別調(diào)用insertorderdetail進(jìn)行保存,因?yàn)樵趇nsertorder中進(jìn)行了事務(wù)處理,數(shù)據(jù)的安全性也比較有保障,幾個(gè)存儲(chǔ)過程設(shè)計(jì)的精巧別致,很有意思,但是子表的幾個(gè)數(shù)據(jù)大小不能超過4000字符,恐怕不大保險(xiǎn)。
第二種方法是我比較常用的,為了方便,就不用存儲(chǔ)過程了,這個(gè)例子用的是vb.net。
‘處理數(shù)據(jù)的類
public class dbtools
private const _identity_sql as string = "select @@identity as id"
private const _id_for_replace as string = "_id_for_replace"
‘對主子表插入記錄
public function insfathersonrec(byval main_sql as string, byval paramarray arparam() as string) as integer
dim conn as new sqlconnection(strconn)
dim id as integer
conn.open()
dim trans as sqltransaction = conn.begintransaction
try
'主記錄
mydbtools.sqldata.executenonquery(trans, commandtype.text, main_sql)
'返回新增id號(hào)
id = mydbtools.sqldata.executescalar(trans, commandtype.text, _identity_sql)
'從記錄
if not arparam is nothing then
for each sql in arparam
'將剛獲得的id號(hào)代入
sql = sql.replace(_id_for_replace, id)
mydbtools.sqldata.executenonquery(trans, commandtype.text, sql)
next
end if
trans.commit()
catch e as exception
trans.rollback()
finally
conn.close()
end try
return id
end function
end class
上面這段代碼里有mydbtools,是對常見的數(shù)據(jù)庫操作封裝后的類,這個(gè)類對數(shù)據(jù)庫進(jìn)行直接的操作,有經(jīng)驗(yàn)的.net數(shù)據(jù)庫程序員基本上都會(huì)有,一些著名的例子程序一般也都提供。
上面的是通用部分,下面是對具體單據(jù)的操作
publid class order
public _orderdate as date ‘主表記錄
public childdt as datatable ‘子表記錄,結(jié)構(gòu)與orderdetail一致
public function save() as integer
dim str as string
dim i as integer
dim arparam() as string
dim str as string=”insert into order(orderdate) values(‘” & _orderdate & “’)”
if not childdt is nothing then
arparam = new string(childdt.rows.count - 1) {}
for i=0 to childdt.rows.count-1
arparam(i)= ”insert into orderdetail(orderid,productid,num,price) values(_id_for_replace,” & drow(“productid) & “,” & drow(“num”) & “,” drow(“price”) & “)”
next i
end if
return (new dbtools). insfathersonrec(str,arparam)
end class
上面的兩個(gè)例子為了方便解釋,去掉了一些檢驗(yàn)驗(yàn)證過程,有興趣的朋友可以參照網(wǎng)上書店的例子研究第一種方法,或者根據(jù)自己
新聞熱點(diǎn)
疑難解答
圖片精選