Ber*_*tie 1 sql t-sql vba stored-procedures sql-server-2008-r2
是否有更好的SQL Server 2008 R2技术来编写允许NULL 的以下组合INSERT和UPDATE过程INSERT?
我非常有兴趣了解其他开发人员如何编写INSERT和UPDATE可以处理NULL插入的过程(想象用户想要撤消条目).我很欣赏将有更复杂和优雅的解决方案使用MERGE或者我有兴趣看到的一些事务回滚技术,但是,我确实要求你从第一原则建立你的例子,因为这可能导致帖子具有更广泛的吸引力,无论如何什么读者T-SQL级别.
这个简单示例的基础是Orders跟踪股票购买的表格.该程序应仅在相同或增加UPDATES时允许OrderStatus.
OrderStatus Explanation
-------------------------
0 Creation
1 Checking
2 Placement
3 Execution
...
8 Settlement
Run Code Online (Sandbox Code Playgroud)
表结构:
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)
Run Code Online (Sandbox Code Playgroud)
此外,让我们假设我们有以下数据,以便我们可以测试修改数据:
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
Run Code Online (Sandbox Code Playgroud)
您应该拥有以下数据:
OrderID Ticker Size Price OrderStatus
-----------------------------------------------
1 MSFT 1.000 NULL 0
2 GOOG 2.000 523.000 5
3 AAPL 1.000 NULL 0
Run Code Online (Sandbox Code Playgroud)
现在为有趣的部分.这是我尽最大努力设计一个可以处理NULL插入的组合INSERT&UPDATE过程(即允许用户撤消条目).请注意,我需要一个输入参数来区分NULL的输入值是否是有意的并且需要写入表中而不是显示为缺少输入参数的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
Run Code Online (Sandbox Code Playgroud)
现在我们有一个UPSERT程序,让我说明它的用法:
第1步:插入新行以显示购买一些福特股票的意图:
EXEC dbo.Upsert_Orders @Ticker = 'F',
@Size = 1,
@Price = 10,
@OrderStatus = 2
Run Code Online (Sandbox Code Playgroud)
第2步:让我们看看它OrderStatus不能被卷回来
EXEC dbo.Upsert_Orders @OrderID = 4,
@Ticker = 'F',
@Size = 1,
@Price = 10,
@OrderStatus = 1
Run Code Online (Sandbox Code Playgroud)
这会产生所需的输出:
spUO Modifying existing record with OrderID 4
spUO You do not have permissions to roll back the OrderStatus.
Run Code Online (Sandbox Code Playgroud)
数据现在看起来像:
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
Run Code Online (Sandbox Code Playgroud)
第3步:最后,让我们假设用户想要删除第一个订单的共享,然后在我的程序下的不幸方法需要传递其他默认参数并且@isNULLInsert BIT需要设置为1.
EXEC dbo.Upsert_Orders @isNullInsert = 1,
@OrderID = 1,
@Ticker = 'MSFT',
@Size = NULL,
@Price = NULL,
@OrderStatus = 0
Run Code Online (Sandbox Code Playgroud)
希望这个完整的例子说明了添加新记录,更新现有记录和删除记录字段的概念.对这篇文章的篇幅感到抱歉,但这是我能够制作的最简洁的代码!
最终数据:
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
Run Code Online (Sandbox Code Playgroud)
谢谢大家,
伯蒂.
ps这将从Excel VBA调用.
这是使用merge的答案.
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
Run Code Online (Sandbox Code Playgroud)