我需要编写一个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?)

当前回答

这是我最近不得不做的事情:

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

其他回答

INSERT INTO table ( column1, column2, column3 )
SELECT $column1, $column2, $column3
EXCEPT SELECT column1, column2, column3
FROM table

在测试行是否存在时,传递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)。

这是我最近不得不做的事情:

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
INSERT INTO [DatabaseName1].dbo.[TableName1] SELECT * FROM [DatabaseName2].dbo.[TableName2]
 WHERE [YourPK] not in (select [YourPK] from [DatabaseName1].dbo.[TableName1])

我假设每个航班都有一排座位?如果是这样:

IF EXISTS (SELECT * FROM Bookings WHERE FLightID = @Id)
BEGIN
    --UPDATE HERE
END
ELSE
BEGIN
   -- INSERT HERE
END

我认为我说的,因为你的做事方式可能会超额预订航班,因为当最多有10张票而你预订了20张票时,它会插入一个新的行。