我需要编写一个 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?)
IF EXISTS (SELECT * FROM Bookings WHERE FlightID = @Id)
BEGIN
--UPDATE HERE
END
ELSE
BEGIN
-- INSERT HERE
END
我假设我所说的,因为你的做法可能会超额预订航班,因为当最多 10 张机票而你预订 20 张机票时,它会插入一个新行。
MERGE 命令。您可以在一条语句中执行 UPDATE
、
INSERT
和 DELETE
。这是使用
MERGE
的工作实现 - 在进行更新之前检查航班是否已满,否则进行插入。
if exists(select 1 from INFORMATION_SCHEMA.TABLES T
where T.TABLE_NAME = 'Bookings')
begin
drop table Bookings
end
GO
create table Bookings(
FlightID int identity(1, 1) primary key,
TicketsMax int not null,
TicketsBooked int not null
)
GO
insert Bookings(TicketsMax, TicketsBooked) select 1, 0
insert Bookings(TicketsMax, TicketsBooked) select 2, 2
insert Bookings(TicketsMax, TicketsBooked) select 3, 1
GO
select * from Bookings
然后...
declare @FlightID int = 1
declare @TicketsToBook int = 2
--; This should add a new record
merge Bookings as T
using (select @FlightID as FlightID, @TicketsToBook as TicketsToBook) as S
on T.FlightID = S.FlightID
and T.TicketsMax > (T.TicketsBooked + S.TicketsToBook)
when matched then
update set T.TicketsBooked = T.TicketsBooked + S.TicketsToBook
when not matched then
insert (TicketsMax, TicketsBooked)
values(S.TicketsToBook, S.TicketsToBook);
select * from Bookings
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 提示强制查询采用范围锁,防止其他事务添加与您的过滤条件匹配的行,直到您提交或回滚。
行锁提示强制锁定粒度为行级别而不是默认页面级别,因此您的事务不会阻止尝试更新同一页面中不相关行的其他事务(但要注意减少争用和增加争用之间的权衡)在锁定开销中 - 您应该避免在单个事务中获取大量行级锁)。
请参阅
http://msdn.microsoft.com/en-us/library/ms187373.aspx了解更多信息。 请注意,锁被视为执行获取它们的语句 - 调用 begin tran 并不能让您免受另一个事务在您到达某事物之前夹住锁的影响。 您应该尝试并考虑您的 SQL,通过尽快提交事务(晚获取,早释放)来尽可能短地持有锁。
请注意,如果您的 PK 是 bigint,行级锁的效果可能会较差,因为 SQL Server 上的内部哈希对于 64 位值来说是退化的(不同的键值可能会哈希到相同的锁 ID)。
INSERT INTO TableName (col1,col2)
SELECT @par1, @par2
WHERE NOT EXISTS (SELECT col1,col2 FROM TableName
WHERE col1=@par1 AND col2=@par2)
例如:
INSERT INTO Members (username)
SELECT 'Cem'
WHERE NOT EXISTS (SELECT username FROM Members
WHERE username='Cem')
(1) 从表名中选择 col1,col2,其中 col1=@par1 AND col2=@par2 它从 TableName 搜索值中选择
(2) 在不存在的情况下选择@par1、@par2 如果不存在则需要 (1) 子查询
(3) 插入 TableName (2) 步骤值
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[cjso_UpdateCustomerLogin]
(
@CustomerID AS INT,
@UserName AS VARCHAR(25),
@Password AS BINARY(16)
)
AS
BEGIN
IF ISNULL((SELECT CustomerID FROM tblOnline_CustomerAccount WHERE CustomerID = @CustomerID), 0) = 0
BEGIN
INSERT INTO [tblOnline_CustomerAccount] (
[CustomerID],
[UserName],
[Password],
[LastLogin]
) VALUES (
/* CustomerID - int */ @CustomerID,
/* UserName - varchar(25) */ @UserName,
/* Password - binary(16) */ @Password,
/* LastLogin - datetime */ NULL )
END
ELSE
BEGIN
UPDATE [tblOnline_CustomerAccount]
SET UserName = @UserName,
Password = @Password
WHERE CustomerID = @CustomerID
END
END
begin trans ... commit
代码。
declare @mystat6 bigint
declare @mystat6p varchar(50)
declare @mystat6b bigint
DECLARE mycur1 CURSOR for
select result1,picture,bittot from all_Tempnogos2results11
OPEN mycur1
FETCH NEXT FROM mycur1 INTO @mystat6, @mystat6p , @mystat6b
WHILE @@Fetch_Status = 0
BEGIN
begin tran /* default read committed isolation level is fine */
if not exists (select * from all_Tempnogos2results11_uniq with (updlock, rowlock, holdlock)
where all_Tempnogos2results11_uniq.result1 = @mystat6
and all_Tempnogos2results11_uniq.bittot = @mystat6b )
insert all_Tempnogos2results11_uniq values (@mystat6 , @mystat6p , @mystat6b)
--else
-- /* update */
commit /* locks are released here */
FETCH NEXT FROM mycur1 INTO @mystat6 , @mystat6p , @mystat6b
END
CLOSE mycur1
DEALLOCATE mycur1
go
--Insert a new record
INSERT INTO dbo.Table2(NoEtu, FirstName, LastName)
SELECT t1.NoEtuDos, t1.FName, t1.LName
FROM dbo.Table1 as t1
WHERE NOT EXISTS (SELECT (1) FROM dbo.Table2 AS t2
WHERE t1.FName = t2.FirstName
AND t1.LName = t2.LastName
AND t1.NoEtuDos = t2.NoEtu)
ALTER TABLE table_name ADD UNIQUE KEY
THEN INSERT IGNORE INTO table_name
,如果导致重复键/表中已存在该值,则不会插入该值。