2017-01-15 128 views
1

我在寫一個存儲過程。在這裏面,我有以下的mysql插入語句:鎖定表中插入來自MySQL的InnoDB存儲過程

INSERT INTO `Address`(`countryCode`, `addressLine1`, `addressLine2`, `postcode`, `region`, `city`) VALUES (country, addressLine1, addressLine2, postcode, region, city); 
INSERT INTO `UserAddress`(`username`, `creationDate`, `addressId`) VALUES (username,NOW(),(SELECT addressId FROM Address ORDER BY addressId DESC LIMIT 1)); 

正如你所看到的,我上的一個地址表進行插入,然後使用自動對下一行子查詢中增加「addressId」。現在,如果在這兩個語句之間另一個事務會在Address表中插入一些東西,我會在UserAddress表中插入錯誤的addressId。我的猜測是我需要鎖定地址表,而這些語句正在執行。

經過長時間的搜尋後,我發現下面的代碼鎖定地址表:

SELECT addressId FROM Address FOR UPDATE; 

這是否也爲新插入的行工作?如果不是,會怎麼樣?

+2

你會一直將兩行插入在一起嗎?如果是這樣,爲什麼將它們插入單獨的表中?我認爲你的設計有問題。 – GurV

+1

如果您確實需要這樣做,我可能會考慮使用事務。但正如@GurV所說,你可能會遇到設計問題。 –

+0

@GurV它是如何設計的問題?這個想法是用戶可以有多個地址。另外,一個地址可以是多個用戶。另外,地址有創建日期。我需要在地址和用戶表之間有一個表格,然後呢? –

回答

0

這正是爲什麼他們發明了一種LAST_INSERT_ID

INSERT INTO `Address`(`countryCode`, `addressLine1`, `addressLine2`, `postcode`, `region`, `city`) VALUES (country, addressLine1, addressLine2, postcode, region, city); 
INSERT INTO `UserAddress`(`username`, `creationDate`, `addressId`) VALUES (username,NOW(),LAST_INSERT_ID()); 

無需鎖或交易,甚至存儲過程爲抓住最後一個ID的任務。只需在您的應用中執行此操作。

+0

那是併發安全的?真棒! –

+0

很高興有幫助 – e4c5

+0

'LAST_INSERT_ID()'是連接本地的;因此,「貨幣安全」。 –

1

理想情況下,您應該在Transaction之內使用'START TRANSACTION;'和'COMMIT'語法。您還需要將autocommit設置爲0(默認情況下啓用)。 Here是事務和自動提交的文檔。

只要您的更新包裝在交易中並設置了適當的isolation level,其他交易所做的更改不會影響您的交易。

+0

將每個存儲過程都包裝在START TRANSACTION中是否好設計?和'在程序中提交? –

+1

不是一切,而是需要隔離的位,就像你在問題中提到的位置。 –

+2

想想這樣......在這裏:查詢1:從你的銀行賬戶中取出一些錢;問題2:將這筆錢存入您的儲蓄賬戶。如果服務器在查詢1和查詢2之間崩潰怎麼辦?這是需要'BEGIN;查詢1;查詢2; COMMIT;'使_both_成功或_neither_。如果你的應用有類似的東西,你需要交易。 –