web-dev-qa-db-ja.com

行が存在するか確認し、そうでなくれば挿入

テーブル内の行を更新する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?)
221
Robert

MERGE コマンドを見てください。 UPDATEINSERT、およびDELETEを1つのステートメントで実行できます。

これは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
153
Sung M. Kim

フライトごとに1行としますか。もしそうなら:

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

最大10のチケットがあり、あなたが20を予約しているときそれが新しい列を挿入するので、私が言ったことを仮定します。

147

行の存在をテストするときに、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ヒントは、行がすでに存在する場合は、その行に対して更新ロックを強制的に適用し、コミットまたはロールバックするまで他のトランザクションがその行を変更できないようにします。

ホールドロックヒントは、クエリが範囲ロックを取得するように強制し、コミットまたはロールバックするまで他のトランザクションがフィルタ基準に一致する行を追加することを防ぎます。

Rowlockヒントは、デフォルトのページレベルではなく行レベルにロックの細分性を強制するため、トランザクションは同じページ内の無関係の行を更新しようとする他のトランザクションをブロックしません(ただし、競合の減少と増加ロックのオーバーヘッド - 1回のトランザクションで多数の行レベルロックをかけることは避けてください。

詳細については、 http://msdn.Microsoft.com/ja-jp/library/ms187373.aspx を参照してください。

ロックはそれを実行するステートメントとして実行されるので注意してください。begin tranを呼び出しても、別のトランザクションがロックをつまんでいても、そのロックが解除されることはありません。できるだけ早くトランザクションをコミットすることにより(SQLを遅く取得し、早く解放する)、できるだけ短い時間ロックを保持するようにSQLを調整してください。

SQL Serverの内部ハッシュは64ビット値では縮退するため、PKが重要な場合、行レベルのロックはあまり効果がない可能性があります(異なるキー値が同じロックIDにハッシュされる可能性があります)。

63
Cassius Porcus

私は自分の解決策を書いています。私の方法は '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存在しない場所(1)副問い合わせから存在しない場合にかかる

(3)TableNameに挿入する(2)ステップ値

36
Cem

これは私が最近しなければならなかったことです。

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
2
TheTXI

次のモデルを使用して、行がまだ存在しないという条件で、行を挿入できました。

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/[email protected]

2
Paul G

達成するために マージ 機能を使うことができます。そうでなければあなたはできる:

declare @rowCount int

select @rowCount=@@RowCount

if @rowCount=0
begin
--insert....
1
JoshBerke
INSERT INTO [DatabaseName1].dbo.[TableName1] SELECT * FROM [DatabaseName2].dbo.[TableName2]
 WHERE [YourPK] not in (select [YourPK] from [DatabaseName1].dbo.[TableName1])
0
Almamun

この問題に対する最善のアプローチは、最初にデータベース列をUNIQUEにすることです。

ALTER TABLE table_name ADD UNIQUE KEY

THEN INSERT IGNORE INTO table_name、重複するキーになった場合、またはテーブルにすでに存在する場合、値は挿入されません。

0
Maurice Elagu

完全な解決策は以下のとおりです(カーソル構造を含む)。上記の投稿からのbegin trans ... commitコードを提供してくれたCassius Porcusに感謝します。

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
0
user2836818