程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> MYSQL數據庫 >> 關於MYSQL數據庫 >> 小議主子表INT自增主鍵插入記錄的方法

小議主子表INT自增主鍵插入記錄的方法

編輯:關於MYSQL數據庫

主子表最常見的大概就是用在進銷存、MRP、ERP裡面,比如一張銷售訂單,訂單Order(ID,OrderDate),訂單明細OrderDetail(OrderID, ProductID, Num,Price)這個大概就是最簡單的主子表了,兩個表通過ID與OrderID建立關聯,這裡主鍵ID是自增的INT類型,OrderID是表OrderDetail的外鍵。當然,鍵的選擇方法很多,現在我們選擇的是在sql裡面最簡單的方法。

對於這樣的表結構,我們最常見的問題就是保存的時候怎樣處理鍵值的問題,因為兩個表關聯非常的緊密,我們進行保存的時候需要把它們放在一個事務裡面,這時問題就會出現,Order表中的ID是自動增長型的字段。現在需要我們錄入一張訂單,包括在Order表中插入一條記錄以及在OrderDetail表中插入若干條記錄。因為Order表中的ID是自動增長型的字段,那麼我們在記錄正式插入到數據庫之前無法事先得知它的取值,只有在更新後才能知道數據庫為它分配的是什麼值,然後再用這個ID作為OrderDetail表的OrderID的值,最後更新OderDetail表。但是,為了確保數據的一致性,Order與OrderDetail在更新時必須在事務保護下同時進行,即確保兩表同時更行成功,這個就會有點困擾。


解決這類問題常見的主要有兩類方法:


一種是微軟在網上書店裡使用的方法,使用了四個存儲過程。改裝一下,使之符合現在的例子


--存儲過程一


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

--存儲過程二

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

--存儲過程三

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

------------------------------------------------

--存儲過程四

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

插入時,傳入的子表數據都是長度為4000的NVARCHAR類型,各個字段使用“,”分割,然後調用PopFirstWord分拆後分別調用InsertOrderDetail進行保存,因為在InsertOrder中進行了事務處理,數據的安全性也比較有保障,幾個存儲過程設計的精巧別致,很有意思,但是子表的幾個數據大小不能超過4000字符,恐怕不大保險。

第二種方法是我比較常用的,為了方便,就不用存儲過程了,這個例子用的是VB.Net。

‘處理數據的類

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號

ID = myDBTools.SqlData.ExecuteScalar(trans, CommandType.Text, _IDENTITY_SQL)

'從記錄

If Not arParam Is Nothing Then

For Each sql In arParam

'將剛獲得的ID號代入

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,是對常見的數據庫操作封裝後的類,這個類對數據庫進行直接的操作,有經驗的.Net數據庫程序員基本上都會有,一些著名的例子程序一般也都提供。

上面的是通用部分,下面是對具體單據的操作

Publid class Order

Public _OrderDate as date ‘主表記錄

Public ChildDt as datatable ‘子表記錄,結構與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


上面的兩個例子為了方便解釋,去掉了一些檢驗驗證過程,有興趣的朋友可以參照網上書店的例子研究第一種方法,或者根據自己的需要對第二種方法進行修改。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved