我需要编写一个T-SQL存储过程来更新表中的一行。如果该行不存在,则插入它。所有这些步骤都由一个事务包装。
这是一个预订系统,所以它必须是原子和可靠的。如果事务已提交且机票已预订,则必须返回true。
我确定如何使用@@rowcount。这就是我到目前为止所写的。我走的路对吗?
-- BEGIN TRANSACTION (HOW TO DO?)
UPDATE Bookings
SET TicketsBooked = TicketsBooked + @TicketsToBook
WHERE FlightId = @Id AND TicketsMax < (TicketsBooked + @TicketsToBook)
-- Here I need to insert only if the row doesn't exists.
-- If the row exists but the condition TicketsMax is violated, I must not insert
-- the row and return FALSE
IF @@ROWCOUNT = 0
BEGIN
INSERT INTO Bookings ... (omitted)
END
-- END TRANSACTION (HOW TO DO?)
-- Return TRUE (How to do?)
我最终能够插入一行,在它不存在的情况下,使用以下模型:
INSERT INTO table ( column1, column2, column3 )
(
SELECT $column1, $column2, $column3
WHERE NOT EXISTS (
SELECT 1
FROM table
WHERE column1 = $column1
AND column2 = $column2
AND column3 = $column3
)
)
我在:
http://www.postgresql.org/message-id/87hdow4ld1.fsf@stark.xeocode.com
在测试行是否存在时,传递updlock、rowlock、holdlock提示。
begin tran /* default read committed isolation level is fine */
if not exists (select * from Table with (updlock, rowlock, holdlock) where ...)
/* insert */
else
/* update */
commit /* locks are released here */
updlock提示强制查询对已经存在的行使用更新锁,从而防止其他事务在提交或回滚之前修改它。
holdlock提示强制查询接受范围锁,防止其他事务添加匹配您的筛选条件的行,直到您提交或回滚。
rowlock提示将锁粒度强制到行级别,而不是默认的页级别,因此您的事务不会阻止试图更新同一页中不相关行的其他事务(但要注意减少争用和增加锁开销之间的权衡—您应该避免在单个事务中使用大量的行级别锁)。
更多信息请参见http://msdn.microsoft.com/en-us/library/ms187373.aspx。
注意,锁是在执行执行锁的语句时被接受的——调用begin tran并不能让你免受另一个事务的影响,在你到达某个事物之前就对它进行锁定。您应该通过尽可能快地提交事务(晚获取,早释放)来尝试将SQL分解为尽可能短的锁持有时间。
请注意,如果PK是bigint,行级锁可能不太有效,因为SQL Server上的内部哈希对于64位值是退化的(不同的键值可能哈希到相同的锁id)。