テーブル内の行を更新する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?)
MERGE コマンドを見てください。 UPDATE
、INSERT
、および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
フライトごとに1行としますか。もしそうなら:
IF EXISTS (SELECT * FROM Bookings WHERE FLightID = @Id)
BEGIN
--UPDATE HERE
END
ELSE
BEGIN
-- INSERT HERE
END
最大10のチケットがあり、あなたが20を予約しているときそれが新しい列を挿入するので、私が言ったことを仮定します。
行の存在をテストするときに、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にハッシュされる可能性があります)。
私は自分の解決策を書いています。私の方法は '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)ステップ値
これは私が最近しなければならなかったことです。
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
WHERE NOT EXISTS (
SELECT 1
FROM table
WHERE column1 = $column1
AND column2 = $column2
AND column3 = $column3
)
)
私はそれを見つけました:
達成するために マージ 機能を使うことができます。そうでなければあなたはできる:
declare @rowCount int
select @rowCount=@@RowCount
if @rowCount=0
begin
--insert....
INSERT INTO [DatabaseName1].dbo.[TableName1] SELECT * FROM [DatabaseName2].dbo.[TableName2]
WHERE [YourPK] not in (select [YourPK] from [DatabaseName1].dbo.[TableName1])
この問題に対する最善のアプローチは、最初にデータベース列をUNIQUEにすることです。
ALTER TABLE table_name ADD UNIQUE KEY
THEN INSERT IGNORE INTO table_name
、重複するキーになった場合、またはテーブルにすでに存在する場合、値は挿入されません。
完全な解決策は以下のとおりです(カーソル構造を含む)。上記の投稿からの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