1

NULL を許可する次の組み合わせINSERT&プロシージャをコーディングするためのより良い SQL Server 2008 R2 手法はありますか?UPDATEINSERT

私は、他の開発者が NULL 挿入を処理できるINSERT&UPDATEプロシージャーをどのように書いているかを見ることに非常に興味があります (ユーザーがエントリを元に戻したいと考えていると想像してください)。私が興味を持っている、またはいくつかのトランザクションロールバック手法を使用した、より洗練されたエレガントなソリューションがあることを感謝MERGEしますが、最初の原則から例を構築することをお願いします。リーダーの T-SQL レベル。

この単純化された例の基礎は、Orders株式購入を追跡するテーブルです。この手順では、 が同じか増加しているUPDATES場合にのみ許可する必要があります。OrderStatus

OrderStatus  Explanation
-------------------------
     0       Creation
     1       Checking
     2       Placement
     3       Execution
     ...
     8       Settlement

テーブル構造:

CREATE TABLE Orders(
    OrderID INT IDENTITY,
    Ticker VARCHAR(20) NOT NULL,
    Size DECIMAL(31,15) NULL,
    Price DECIMAL(31,15) NULL,
    OrderStatus TINYINT NOT NULL)

さらに、データの変更をテストできるように、次のデータがあるとします。

SET IDENTITY_INSERT [dbo].[Orders] ON
INSERT INTO [dbo].[Orders] ([OrderID], [Ticker], [Size], [Price], [OrderStatus]) 
VALUES  (1, N'MSFT', CAST(1 AS Decimal(31, 15)), NULL, 0)
        ,(2, N'GOOG', CAST(2 AS Decimal(31, 15)), CAST(523 AS Decimal(31, 15)), 5)
        ,(3, N'AAPL', CAST(1 AS Decimal(31, 15)), NULL, 0)
SET IDENTITY_INSERT [dbo].[Orders] OFF

次のデータが必要です。

OrderID Ticker  Size    Price   OrderStatus
-----------------------------------------------
1       MSFT    1.000   NULL    0
2       GOOG    2.000   523.000 5
3       AAPL    1.000   NULL    0

さて、興味深い部分です。これは、 NULL 挿入を処理できる (つまり、ユーザーがエントリを元に戻すことができる) INSERT&を組み合わせたプロシージャを設計するための最善の努力です。UPDATENULL の入力値が意図的であり、テーブルに書き込む必要があるかどうかと、不足している入力パラメーターとして表示される NULL を区別するために、入力パラメーターが必要であることに注意してください。私のテクニックが非常に冗長だと思うので、なぜ私がこの質問をしているのかが非常に明確であることを願っています.

CREATE PROCEDURE [dbo].[Upsert_Orders] @isNullInsert BIT = 0
    ,@OrderID INT = NULL
    ,@Ticker VARCHAR(20) = NULL
    ,@Size VARCHAR(100) = NULL
    ,@Price VARCHAR(100) = NULL
    ,@OrderStatus TINYINT = NULL
AS
BEGIN
    IF (@OrderID IS NOT NULL)
        -- First check if @OrderID exists
        IF (
                SELECT OrderID
                FROM dbo.Orders
                WHERE OrderID = @OrderID
                ) IS NULL
        BEGIN
            -- @OrderID does not exist therefore replace with NULL
            SET @OrderID = NULL
            PRINT 'spUO. Replaced OrderID ' + CAST(@OrderID AS VARCHAR) + ' input parameter with NULL.'
        END

    IF @OrderID IS NULL
    BEGIN
        -- @OrderID IS NULL so INSERT a new record
        PRINT 'spUO Inserting a new record into the Orders'

        INSERT INTO Orders (
            -- OrderID not needed as IDENTITY new record.
            Ticker
            ,Size
            ,Price
            ,OrderStatus
            )
        VALUES (
            -- @OrderID not needed as IDENTITY new record
            @Ticker
            ,@Size
            ,@Price
            ,@OrderStatus
            )
    END
    ELSE
    BEGIN
        -- @OrderID IS NOT NULL therefore UPDATE the record @OrderID
        PRINT 'spUO Modifying existing record with OrderID ' + CAST(@OrderID AS VARCHAR)

        -- Declare CurrentVariables for @OrderID
        DECLARE -- @CurrentOrderID INT not needed as @OrderID Found
            @CurrentTicker VARCHAR(20)
            ,@CurrentSize DECIMAL(31, 15)
            ,@CurrentPrice DECIMAL(31, 15)
            ,@CurrentOrderStatus TINYINT

        -- Populate Current Variables from Table Orders
        SELECT -- @CurrentOrderID = OrderID not needed as @OrderID Found
            @CurrentTicker = Ticker
            ,@CurrentSize = Size
            ,@CurrentPrice = Price
            ,@CurrentOrderStatus = OrderStatus
        FROM Orders
        WHERE OrderID = @OrderID

        IF ISNULL(@OrderStatus, @CurrentOrderStatus) >= @CurrentOrderStatus
        BEGIN
            -- Update @OrderID if not moving backwards
            IF @isNullInsert = 0
            BEGIN
                -- We are not updating the record with NULL
                PRINT 'spUO NULL Parameter Input Values get replaced with the existing entries'

                UPDATE Orders
                SET -- OrderID = ISNULL(@OrderID, @CurrentOrderID)  not needed as @OrderID Found
                    Ticker = ISNULL(@Ticker, @CurrentTicker)
                    ,Size = ISNULL(@Size, @CurrentSize)
                    ,Price = ISNULL(@Price, @CurrentPrice)
                    ,OrderStatus = ISNULL(@OrderStatus, @CurrentOrderStatus)
                WHERE OrderID = @OrderID
            END
            ELSE
            BEGIN
                -- We are potentially overwritting the record with NULL
                PRINT 'spUO Old entries may be overwritten with NULL'

                UPDATE Orders
                SET -- OrderID = ISNULL(@OrderID, @CurrentOrderID)  not needed as @OrderID Found
                    Ticker = @Ticker
                    ,Size = @Size
                    ,Price = @Price
                    ,OrderStatus = @OrderStatus
                WHERE OrderID = @OrderID
            END
        END
        ELSE
            -- User is trying to re-write hostory. Do Nothing
            PRINT 'spUO You do not have permissions to roll back the OrderStatus.' 
    END
END

UPSERT プロシージャができたので、その使用法を説明します。

ステップ 1: 新しい行を挿入して、フォード株を購入する意図を示します。

EXEC dbo.Upsert_Orders  @Ticker = 'F',
                        @Size = 1,
                        @Price = 10,
                        @OrderStatus = 2

OrderStatusステップ 2:が巻き戻せないことを示しましょう

EXEC dbo.Upsert_Orders  @OrderID = 4,
                        @Ticker = 'F',
                        @Size = 1,
                        @Price = 10,
                        @OrderStatus = 1

これにより、目的の出力が生成されます。

spUO Modifying existing record with OrderID 4 
spUO You do not have permissions to roll back the OrderStatus.

データは次のようになります。

OrderID Ticker  Size    Price   OrderStatus
-----------------------------------------------
1       MSFT    1.000   NULL    0
2       GOOG    2.000   523.000 5
3       AAPL    1.000   NULL    0
4       F       1.000   10.000  2

ステップ 3: 最後に、ユーザーが最初の注文の株式を削除したいと仮定しましょう。次に、私の手順での不幸な方法では、他のデフォルト パラメータを渡す@isNULLInsert BIT必要があり、1 に設定する必要があります。

EXEC dbo.Upsert_Orders  @isNullInsert = 1,
                        @OrderID = 1,
                        @Ticker = 'MSFT',
                        @Size = NULL,
                        @Price = NULL,
                        @OrderStatus = 0

この完全な例が、新しいレコードの追加、既存のレコードの更新、およびレコードのフィールドの削除の概念を示していることを願っています。この記事が長くなって申し訳ありませんが、これは私が作成できた最も簡潔なコードです!

最終データ:

OrderID Ticker  Size    Price   OrderStatus
------------------------------------------------
1       MSFT    NULL    NULL    0
2       GOOG    2.000   523.000 5
3       AAPL    1.000   NULL    0
4       F       1.000   10.000  2

皆さんありがとう、

バーティ。

ps これは Excel VBA から呼び出されます。

4

1 に答える 1

7

マージを使用した回答は次のとおりです。

Create Procedure [dbo].[Upsert_Orders2] 
    @IsNullInsert Bit = 0,
    @OrderID Int = Null,
    @Ticker Varchar(20) = Null,
    @Size Decimal(31,15) = Null,
    @Price Decimal(31,15) = Null,
    @OrderStatus Tinyint = Null
As

Declare @OrderStatusChange Table(Oldstatus int, NewStatus int)

Begin Transaction

Merge
  dbo.Orders As target
Using
  (Select @OrderID As OrderID) As source
On 
  (target.OrderID = source.OrderID)
When Matched Then
  Update Set
    Ticker = Case When @IsNullInsert = 0 Then IsNull(@Ticker, target.Ticker) Else @Ticker End,
    Size = Case When @IsNullInsert = 0 Then IsNull(@Size, target.Size) Else @Size End,
    Price = Case When @IsNullInsert = 0 Then IsNull(@Price, target.Price) Else @Price End,
    OrderStatus = Case When @IsNullInsert = 0 Then IsNull(@OrderStatus, target.OrderStatus) Else @OrderStatus End
When Not Matched Then
  Insert 
    (Ticker, Size, Price, OrderStatus)
  Values
    (@Ticker, @Size, @Price, @OrderStatus)
Output
  deleted.OrderStatus, inserted.OrderStatus into @OrderStatusChange;

If Exists (Select 'x' From @OrderStatusChange Where NewStatus < OldStatus)
  -- Evil History Changer!
  Rollback Transaction
Else
  Commit Transaction
于 2012-11-15T22:43:51.920 に答える