2016-04-02 9 views
1

updateステートメントでユーザー定義関数を使用してテーブルを更新しようとすると、問題が発生しました。SQL Server - 更新ステートメントでユーザー定義関数を使用してテーブルを正しく更新する方法

私は既存のSQLを大幅に簡略化し、私が見ている問題を示すサンプルコードを提供しました。私は私が欲しいもの、同じタイムスタンプ

と100行の3セットにMyTestTableで300行のグループに必要な、私はすべて同じタイムスタンプ値

でテストテーブルに300行から始め

Timestamp    Count 
2016-04-01 15:51:00  100 
2016-04-01 15:52:00  100 
2016-04-01 15:53:00  100 

は、私が今見ていることは同じタイムスタンプで更新されたすべての300行である:表示するには、このようなものです

Timestamp    Count 
2016-04-01 15:51:00  300 

このクエリを作成する最も良い方法は何ですか?以下は

問題

CREATE TABLE [MyTestTable] 
(
    [ID] [int], 
    [Timestamp] [smalldatetime] 
) ON [PRIMARY] 
GO 

CREATE FUNCTION [dbo].[fn_MyTestFunction] 
    (@StartTime smalldatetime, 
     @EndTime smalldatetime, 
     @RandomNumberOfSeconds int) 
RETURNS smalldatetime 
AS 
BEGIN 
    DECLARE @Timestamp SMALLDATETIME 

    -- Find an existing Timestamp between @StartTime and @EndTime in the MyTestTable 
    -- with less than 100 rows with that timestamp 
    SET @Timestamp = (SELECT TOP 1 [Timestamp] 
         FROM MyTestTable 
         WHERE [Timestamp] BETWEEN @StartTime AND @EndTime 
         GROUP BY [Timestamp] 
         HAVING COUNT(*) < 100) 

    -- If no row found with timestamp between @StartTime and @EndTime 
    -- or no timestamp found which has less than 100 rows with that timestamp 
    -- Create a timestamp with a time somewhere between @StartTime and @EndTime 
    if (@Timestamp is null) 
    begin 
     set @Timestamp = dateadd(ss, @RandomNumberOfSeconds, @StartTime) 
    end 

    return @Timestamp 
END 
GO 

declare @Counter int 
set @Counter = 0 

-- Populate the test table with 300 rows, all initially with the same timestamp value 
while @Counter < 300 
begin 
    insert MyTestTable (ID, [Timestamp]) values (@Counter, 'April 1, 2016') 
    set @Counter = @Counter + 1 
end 

declare @StartTime smalldatetime 
declare @EndTime smalldatetime 
declare @RandomNumberOfSeconds float 
set @RandomNumberOfSeconds = 60 

set @StartTime = current_timestamp 
set @EndTime = dateadd(minute, 30, @StartTime) 

update MyTestTable 
    set [Timestamp] = dbo.fn_MyTestFunction(@StartTime, @EndTime, @RandomNumberOfSeconds) 

select [Timestamp], count(*) as "Count" 
from MyTestTable 
group by [Timestamp] 

答えて

3

これはコメントのために長すぎるを再現するいくつかの単純化されたサンプルコードです。

updateステートメントは単一のトランザクションとして完了します。つまり、テーブルへの変更は、トランザクションがコミットされるまで表示されません。

あなたのコードは、更新プログラムが一度に1つの行をコミットすると仮定しているようです。つまり、関数を呼び出すたびに異なるバージョンのテーブルが表示されることを前提としています。しかし、それはSQLの仕組みではありません。更新が実行されている間、テーブルへの参照はすべて「古い」値を参照します。コミットが行われるまで、「新しい」値は明白ではありません。

+0

実際にトランザクションは独自の変更を見ることができますが、問題は恐らくステートメントレベルのHaloween Protectionです。 –

+0

はい、私はそうでなければならないと考えました。更新ステートメントが完了するまで、変更は表示されません。だから、どうやって私が探している結果を得るために別の方法でこのクエリを書くことができますか?私が提供した簡単な例では、300行を使用しましたが、実際のコードは毎分1000行ずつ更新されます。だから私はかなり効率的な何かを探しています。 –

+0

カーソルを使って一度に1つの行をループすることで、これを動作させることができますが、これを達成するためのより効率的な方法が必要だと思いますか? @JackAllen。 –

関連する問題