近日在工作中,遇到一個項目,需要將SQL Server中的紀錄拆分顯示,也就是將一條紀錄根據某種方式拆分成多條紀錄。比如說在某個帳單系統中,記帳時,按照常規方式按條記;出帳時,要以0.5每紀錄的方式進行拆分,即3元的帳單,要拆分成6條紀錄,每條0.5元,除金額外,其它欄位保持不變。
這是個很有趣的問題,最簡陋的方法莫過於使用遊標,一條紀錄一條紀錄的分析並插入。可是,如果紀錄數很多(比如上十萬條帳單),那肯定是一場惡夢。如何拆分才能使效率最佳化呢?我在網上搜尋了一下“SQL 紀錄拆分”,竟然還真讓我找到了,他用的是JOIN的方式達到拆分效果,看起來效率應該比遊標高許多,不過對方是將VarChar欄位用","分割進行拆分,我採取拿來主義的方法,對他的方法進行了修改,下面以上面說的帳單系統為例,細說一下:
設定一個帳單基本值,此值即每條出帳紀錄的金額。在SELECT中,將帳單的金額減去此基本值乘以一個有限序列數i,其結果可用來表示第i條出帳單的剩餘金額,如果得數大於等於0,表示還有剩餘的金額,出帳紀錄有效;若是小於0,則意味著剩餘金額不足,出帳紀錄無效。有效出帳紀錄中的金額自然等於帳單基本值,不過這裡面還有幾個問題:
1、這裡只考慮了帳單金額可以被帳單基本值整除的情況,這種情況下,假如帳單基本值是0.5元,自然是每條出帳單0.5元;而假若帳單是3.2元,帳單基本值是0.5元,那麼最後一條帳單紀錄肯定不是0.5元而是0.2元,所以還需要判斷一下剩餘金額是否大於0小於帳單基本值,如果是,則剩餘金額就是該條出帳單的金額。
2、有限的序列數i,如何產生有限序列數?方法很多,建立一個暫存資料表(或者表變數),1、2、3、4、5……一條一條插入即可產生序列數啦。另一個方法比較有趣,使用某個系統資料表中的紀錄做引子,產生序列數。
下面是SQL樣本:DECLARE @BaseValue AS INT --帳單基本值
SET @BaseValue = 50
DECLARE @Fee TABLE (ID INT IDENTITY(1, 1), Name VARCHAR(32), Fee INT, FeeTime DATETIME)
INSERT INTO @Fee VALUES ('Andy', 300, '2007-01-01 10:22:42')
INSERT INTO @Fee VALUES ('John', 300, '2007-01-01 10:22:32')
INSERT INTO @Fee VALUES ('Lara', 310, '2007-01-01 10:22:22')
INSERT INTO @Fee VALUES ('Philip', 240, '2007-01-01 10:22:52')
DECLARE @Tmp TABLE(ID INT IDENTITY(0, 1), A INT) --序列數表(必須從0開始)
INSERT INTO @Tmp (A)
SELECT NULL FROM sys.columns --用系統資料表做引子,產生序列數
SELECT
F.ID, F.Name, 'Fee' =
CASE
WHEN Fee - @BaseValue * T.ID >= @BaseValue THEN @BaseValue
ELSE Fee - @BaseValue * T.ID
END,
FeeTime
FROM @Fee AS F INNER JOIN @Tmp AS T ON Fee - @BaseValue * T.ID > 0 ORDER BY F.ID, T.ID