虛擬數字存儲表——SQLServer2012可高用


窗口函數之虛擬數字輔助表


數字輔助表是一個整數序列,可以用它來完成多種不同的查詢任務。數字表有很多任務,如生成日期和時間值序列,及分裂值列表。通常,建議在數據庫中保存這樣一個永久表,並填充盡可能多的數字,然后需要的時候查詢它,然而,在某些環境中,我們沒有機會創建和填充新的表,以及需要的查詢邏輯。

下面函數摘自 T-SQL性能調優秘笈——基於SQLServer2012窗口函數

創建虛擬數字輔助函數


use master --根據實際需要修改要保存的庫中
go
--判斷是否已存在 創建函數名、如果存在則執行刪除操作
if OBJECT_ID('dbo.GetNums','IF') is not null drop function dbo.GetNums
go
--創建函數 GetNums 並指定 兩個參數及返回值類型 這里以table 的形式返回
create function dbo.GetNums(@low as bigint ,@high as bigint) returns table
as
--函數體
return
	with
        --新建L0 內容中添加2行
        L0 as(select C from(values(1),(1)) as D(c)),
        --新建L1 數據來源L0 並連接L0 得到4行數據,L1的數據量為L0的 二次方 
        L1 as(select 1 as C from L0 cross join L0 as B),
        L2 as(select 1 as C from L1 cross join L1 as B),
        L3 as(select 1 as C from L2 cross join L2 as B),
        L4 as(select 1 as C from L3 cross join L3 as B),
        --至此 L5的數據達到了 2^2^2^2^2 數據量達到 4,294,967,296 行數據
        L5 as(select 1 as C from L4 cross join L4 as B),
        --將L5數據排序 使用帶 order by (select null) 的row_number() 生成實際的數字
        Nums as (select row_number() over(order by (select null)) as rownum from L5)

        --根據 參數@low,@high 限定實際的輸出行數。
        /** SQLServer 2012 后的寫法,SQLServer 2012 添加了 offset /fetch 選項 **/
        select @low+rownum -1 as n from Nums order by rownum
        offset 0 rows fetch first @high - @low +1 rows only;

        /** SQLServer 2012 以前**/
        --select top(@high -@low +1) @low+rownum -1 as n from Nums order by rownums;
go

測試函數生成效果

  1. 獲取范圍在11-50 列
select n from dbo.GetNums(11,50)
  1. 性能測試 獲取0-1000000內列
select n from dbo.GetNums(0,1000000) --執行時間在10S左右
  1. 生成日期序列
--設定參數 起始時間 和結束時間
--請自行查閱 SQLServer中 日期函數
declare
@start as date = '20210701',
@end as date ='20210901'
--得到 每天 日期序列
select dateadd(day,n,@start) as date from dbo.GetNums(0,datediff(day,@start,@end))
--得到沒12小時 時間間隔的日期序列
select dateadd(HOUR,n*12,@start) as date from dbo.GetNums(0,datediff(HOUR,@start,@end)/12)
--通過修改 date 函數中時間得到不同時間間隔時間序列,比如,年,月,周,天,時,分,秒

生成樣本數據

使用虛擬數字輔助表 實現生成樣本數據,以供測試使用

新建兩張數據表用來模擬 存儲銀行的賬戶信息、和交易流水

  1. 創建表表結構
if OBJECT_ID('dbo.transactions','U') is not null drop table transactions;
if OBJECT_ID('dbo.accounts','U') is not null drop table accounts;

create table dbo.accounts(
actid int not null, --用戶id
actname varchar(50) not null, --用戶名
constraint pk_accounts primary key(actid)
);
create table dbo.transactions(
actid int not null, --用戶id
tranid int not null, --消費流水號
val money not null,  --消費值
trandate datetime not null,  --日期
constraint pk_transactions primary key(actid,tranid),
constraint fk_transactions_accounts 
	foreign key(actid)
	references dbo.accounts(actid)
);

根據虛擬數字輔助函數,生成樣本數據、並添加到數據表中
在accounts 中添加100個賬戶,並在transactions 表中為每個賬戶生成 20000筆交易

  1. 添加賬戶信息

--添加100個用戶賬戶,並將名稱 命名為 account+序列號
insert into dbo.accounts with(tablock)(actid,actname)
select n as actid,'account'+cast(n as varchar(10)) as actname
from dbo.GetNums(1,100)

用戶添加效果

  1. 添加消費流水記錄
--為每個賬號添加20000個交易記錄,由於交易時間不是固定的這里使用隨機數 rand()進行日期拼接
insert into dbo.transactions with(tablock)(actid,tranid,val,trandate)
select a.n as actid,t.n as tranid,
(abs(CHECKSUM(newid())%2)*2-1)*(abs(CHECKSUM(newid())%1300)) as val, --隨機消費值
--由於消費記錄不一定在固定的時間點,這里用的隨機生成日期形式。
convert(datetime,
datediff(day,'1900-01-01','2010-01-01') --計算 1900-01-01 到 開始日期
+ abs(CHECKSUM(newid()))%datediff(day,'2010-01-01','2021-09-01') --開始時間到隨機結束時間的隨機數
+(abs(CHECKSUM(newid())%86400000)*0.00000001)) --生成時分秒時間 86400000為每天的固定毫秒數
as trandate
from dbo.GetNums(1,100) as a
cross join dbo.GetNums(1,20000) as t

記錄添加效果

這里的隨機數嘗試用 rand()方法 生成時間日期整張表相同,隨之棄用,也嘗試封裝過值函數 newid() 關鍵字 顯示報錯 在函數內對帶副作用的運算符 'newid' 的使用無效。 然后才想到這種辦法

核實表數據,發現好多數據不合常理,比如:消費流水號 tranid 與 日期不符,

--修改數據讓數據更合理 利用窗口函數排序進行修改參數
with g as(
	select actid,tranid,val,trandate,
	ROW_NUMBER() over(partition by actid order by trandate) as rownum
	from transactions  
	)
update g set g.tranid=rownum 

修改數據后效果

修改完成后 數據看起來還比較合理 有一個地方需要注意 消費流水號 前幾行相加出現負數的情況,可以通過修改流水號,讓數據看起來是某一短時間值,比如:在流水號 加一個固定的值,或者在添加數據時 修改 GetNums() 中的參數 例如:cross join dbo.GetNums(500000,520000) as t 這樣就比較合理了

下篇文章介紹SQLServer 的幾個窗口函數,數據基於 添加樣本數據表transactions


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM