检查是否存在行,否则插入

时间:2009-03-12 18:17:30

标签: sql sql-server sql-server-2008 tsql

我需要编写一个更新表中行的T-SQL存储过程。如果该行不存在,请插入它。所有这些步骤都由事务包装。

这适用于预订系统,因此必须原子且可靠。如果交易已经提交且预订航班,则必须返回true。

我是对T-SQL 的新手,并且不确定如何使用@@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?)

11 个答案:

答案 0 :(得分:153)

查看MERGE命令。您可以执行UPDATEINSERT&amp; 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

答案 1 :(得分:146)

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

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

我假设我所说的,因为你做事的方式可以超额预订航班,因为当最多10张票并且你预订20时它会插入一个新行。

答案 2 :(得分:63)

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

答案 3 :(得分:36)

我在写我的解决方案。我的方法不能代表'if'或'merge'。我的方法很简单。

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)SELECT col1,col2 FROM TableName WHERE col1 = @ par1 AND col2 = @ par2 它从TableName中选择搜索值

(2)SELECT @ par1,@ par2 WHERE NOT EXISTS 如果不存在(1)子查询

则需要它

(3)插入TableName(2)步骤值

答案 4 :(得分: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

答案 5 :(得分:2)

我终于能够使用以下模型插入一行,条件是它尚不存在:

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

答案 6 :(得分:1)

您可以使用Merge功能来实现。否则你可以这样做:

declare @rowCount int

select @rowCount=@@RowCount

if @rowCount=0
begin
--insert....

答案 7 :(得分:0)

完整解决方案如下(包括游标结构)。非常感谢Cassius Porcus上面发布的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

答案 8 :(得分:0)

INSERT INTO [DatabaseName1].dbo.[TableName1] SELECT * FROM [DatabaseName2].dbo.[TableName2]
 WHERE [YourPK] not in (select [YourPK] from [DatabaseName1].dbo.[TableName1])

答案 9 :(得分:0)

解决此问题的最佳方法是首先使数据库列为UNIQUE

ALTER TABLE table_name ADD UNIQUE KEY

THEN INSERT IGNORE INTO table_name,如果该值导致表中存在重复的键/已经存在,则不会插入该值。

答案 10 :(得分:-2)

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