利用MERGE重新整理庫存快照
SQL Server 2008中有一個新的語法叫做MERGE,這種語法可以融合UPDATE、DELETE和INSERT。特別適合於將交易型的記錄集合併到快照性的結果集中去。非常具有代表性的應用場景就是庫存管理,庫存管理應用(俗稱進銷存)中經常需要獲得某個時間點上的庫存,也稱為庫存結餘。
討論一下一些基礎的東西,在資料庫建模中有兩種基本模型:
- 純交易事務型:這種方法主要是在一張交易表中記錄下所有商品的進出倉記錄,然後根據所有的進出倉詳細記錄來計算得出庫存結餘。這種方法的優勢就在於它儲存了所有的交易明細記錄,所以理論上面可以計算出過往任何一個時間點上的結餘庫存(只能說是理論上的,因為通常情況結餘庫存的計算不一定僅僅按照SKU,還可能根據品類、部門等其他因素,而這些資訊一般都存放在基礎資訊表中。因此如果要追溯以往的庫存結餘,有可能需要這些基礎資訊表也能夠追溯歷史,其實這也就是我們經常在資料倉儲中提到的維度變化問題。)。這種方法的缺點在於如果要獲得庫存結餘則需要進行大量的聚合工作,所以會在庫存結餘查詢的效能方面面臨挑戰,特別是需要查詢實時的庫存結餘時(儘管這種需求在庫存管理應用中並不多見)。
- 純時點快照型:直接建立一張庫存結餘表,每次有進出倉操作的時候,直接更新庫存結餘表,因此這張庫存結餘表裡面的資料實際上就是當前的庫存結餘。這種方法的優勢非常明顯,就是查詢當前庫存的速度會非常快。當然這種方法的缺點也非常明顯——由於這種方法丟棄了交易明細記錄,因此要想追溯歷史庫存的話比較麻煩。(當然也是有辦法的,資料倉儲中有一種方法就是定期快照,也就是每隔一段時間存檔這個時間點上的庫存,當然這種方法需要平衡兩個因素,一是對儲存空間的消耗,二是快照的連續性,存檔越頻繁快照就越連續,儲存空間就會消耗的越厲害。通常這種頻率是由企業對資料分析的需求決定的,根據庫存商品的流動特性一般會在一小時到一週不等。)這種方法最致命的缺定還不在這兒,最為困擾DBA的問題應該是這種模型對應用併發能力的影響。由於所有進出倉操作都需要更新庫存結餘表,所以庫存結餘表會成為資料應用的邏輯瓶頸。
因此一般我們都會用混合模型,為了保證歷史的可追溯,進出倉的事務明細是一定要保留的(至少在一段時間內),而為了滿足對庫存結餘查詢的及時性和效能則需要維護一張快照表,並且保證定期更新這張快照表。為了保證效能並滿足應用的邏輯彈性,這些混和模型的設計是相當重要的,也就是如何更新快照表,不過在這裡我們就不多說了。
在評估SQL Server 2008的過程中,我把SQL Server 2008聯機叢書裡面的程式碼給改了一下,一是更加貼和應用的實際情況,二是聯機叢書的MERGE範例只合並了UPDATE和DELETE,我多加了一個INSERT的情況。:)
在這裡和大家共享一下修改後的程式碼:
--Step 1: Create test table
USE tempdb
GO
CREATE TABLE Inventory_Snapshot
(
ProductID int PRIMARY KEY NOT NULL,
Quantity int NOT NULL
)
GO
CREATE TABLE Inventory_Operation
(
OperationID int PRIMARY KEY IDENTITY(1,1),
OperationDate datetime,
OperationType int, --1:Move in; 2:Move out; 3:Adjustment
ProductID int,
Quantity int
)
GO
--Step 2: Create stored procedure for new inventory operation
CREATE PROCEDURE usp_Inventory_Operation
@productID int,
@operationDate datetime,
@operationType int, --1:Move in; 2:Move out; 3:Adjustment
@quantity int
AS
INSERT INTO Inventory_Operation
(OperationDate, OperationType, ProductID, Quantity)
VALUES
(@operationDate, @operationType, @productID, @quantity)
GO
--Step 3: Create stored procedure for inventory snapshot calculation
CREATE PROCEDURE usp_Inventory_Snapshot_Process
@processDate datetime
AS
MERGE Inventory_Snapshot AS invs
USING (SELECT ProductID, Sum(ABSQuantity) AS SubTotal
FROM (SELECT ProductID, Quantity *
CASE OperationType --1:Move in; 2:Move out; 3:Adjustment
WHEN 1 THEN 1
WHEN 2 THEN -1
WHEN 3 THEN 1
ELSE 0
END AS ABSQuantity FROM Inventory_Operation
WHERE perationDate = @processDate) AggInvo
GROUP BY AggInvo.ProductID)
AS invo(ProductID, SubTotal)
ON (invs.ProductID = invo.ProductID)
WHEN MATCHED AND invs.Quantity <> invo.SubTotal AND invs.Quantity <> invo.SubTotal * -1
THEN UPDATE SET invs.Quantity = invs.Quantity + invo.SubTotal
WHEN MATCHED AND invs.Quantity = invo.SubTotal * -1
THEN DELETE
WHEN TARGET NOT MATCHED
THEN INSERT VALUES (invo.ProductID, invo.SubTotal);
GO
DELETE FROM dbo.Inventory_Snapshot
--Step 4: Test application logic
--2007-1-1
EXEC usp_Inventory_Operation 1000, '2007-1-1', 1, 500
EXEC usp_Inventory_Operation 1001, '2007-1-1', 1, 300
EXEC usp_Inventory_Operation 1002, '2007-1-1', 1, 250
EXEC usp_Inventory_Snapshot_Process '2007-1-1'
SELECT * FROM Inventory_Snapshot
GO
EXEC usp_Inventory_Operation 1001, '2007-1-2', 2, 200
EXEC usp_Inventory_Operation 1003, '2007-1-2', 1, 300
EXEC usp_Inventory_Operation 1000, '2007-1-2', 2, 200
EXEC usp_Inventory_Snapshot_Process '2007-1-2'
SELECT * FROM Inventory_Snapshot
GO
EXEC usp_Inventory_Operation 1000, '2007-1-3', 2, 200
EXEC usp_Inventory_Operation 1002, '2007-1-3', 2, 250
EXEC usp_Inventory_Operation 1004, '2007-1-3', 2, 300
EXEC usp_Inventory_Snapshot_Process '2007-1-3'
SELECT * FROM Inventory_Snapshot
GO
最後需要說明的是,通常DBA或者開發員都會認為交易型記錄一旦寫入資料庫後就不會修改,但實際上不是的。我就在一家大型物流公司中見過某些庫存操作會找一條記錄直接更新(實際上是鎖倉操作,這種設計的初衷可能是考慮到鎖倉操作非常頻繁的緣故吧,不管怎麼樣,直接更新交易型記錄的情況確實存在)。
就像上面的範例程式碼一樣,如果我們對2007年1月2日的進出倉明細記錄執行過了usp_Inventory_Snapshot_Process處理後2007年1月2日的交易記錄又被修改了,這個時候怎麼辦呢?......這或許就是資料庫架構設計迷人之處吧......
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/9079672/viewspace-159336/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 快照庫MV不能成功重新整理問題的解決
- 控制檔案快照和rman利用快照恢復
- MERGE 儲存引擎儲存引擎
- 利用oracle快照dblink解決資料庫表同步問題Oracle資料庫
- 快照是什麼?揭祕儲存快照的實現
- 物理備庫互轉快照備庫
- 資料庫映象和資料庫快照資料庫
- 測試環境,oracle9i 快照重新整理失敗Oracle
- 10 管理物理和快照備庫
- snapshot standby快照備庫角色
- MySQL中merge表儲存引擎用法MySql儲存引擎
- 【Mysql學習】MERGE儲存引擎(一)MySql儲存引擎
- 【Mysql 學習】MERGE儲存引擎(一)MySql儲存引擎
- MyBatis使用Zookeeper儲存資料庫的配置,可動態重新整理MyBatis資料庫
- sql server 2005資料庫快照SQLServer資料庫
- Data Guard新特性:快照備用資料庫資料庫
- 利用PCT解決快速重新整理效能問題
- axios如何利用promise無痛重新整理tokeniOSPromise
- 利用NO_MERGE解決資料字典檢視訪問低效
- Sqlserver在映象資料庫上建立快照指令碼SQLServer資料庫指令碼
- axios如何利用promise無痛重新整理token(二)iOSPromise
- (zt)利用NO_MERGE解決資料字典檢視訪問低效
- 利用NO_MERGE解決資料字典檢視訪問低效 ZT
- OU、庫存組織與子庫存
- 什麼是庫存?什麼是零庫存?庫存的定義
- maven中snapshot快照庫和release釋出庫的區別和作用Maven
- 資料庫實現原理#3(Merge Join).md資料庫
- LVM : 快照LVM
- SAP MM 特殊庫存之T庫存初探
- IBM:利用物聯網技術讓製造商庫存降低了80%IBM
- 安全庫存
- 資料庫系列:RR和RC下,快照讀的區別資料庫
- 關於SQL Server 映象資料庫快照的建立及使用SQLServer資料庫
- 【DATAGUARD】DG系列之RACtoONE快照備用資料庫的搭建資料庫
- docker筆記47-ceph建立快照以及快照恢復Docker筆記
- Oracle的快照standbyOracle
- cutlass進度快照
- kvm主機快照