SQL Server Function Table 應用
一、前言
資料庫的資料處理常見為View、StoreProcedure,但MS SQL 有個與其他資料庫不一樣的功能「Function Table」,其不但可進行各項邏輯運算,且可將其視為Table,可對其進行同一般 Table 之存取作業。
二、特性
(1).可直接進行 select, where, join..等作業
(2).可依據需求自訂查詢結果欄位
(3).重複存取該Function Table時,在一定條件下將重複使用同樣的資源快取
(4).可直接針對欲回傳的資料表進行資料邏輯運算
此回將針對第(4)點進一步做說明
三、範例說明「可直接針對欲回傳的資料表進行資料邏輯運算」
(1).目的條件:利用行政區(戶籍地/居住地)、性別、年齡與訂閱項目之條件,篩選出符合條件的名單
(2).作法目標:
A. 複合查詢條件的應用
B. 解省資源,避免大量資料重複存取運算
C. 可直接做 select 存取以進行下一步作業
(3).SQL參考:
重點作法說明:
A. 直接於該 Function Table 之 Return Table 做資料邏輯運算
B. 為避免喪失 index 故不於 where 條件做 case when 之等於判斷式
C. 函式應用:
i. isjson : 判斷該資料是否為Json格式
ii.JSON_VALUE : 取得Json字串中該Tag欄位的值
iii.string_split : 指定分隔符號切割字串(SQL 2016以上版本支援)
D. Function Table 內容 SQL
-- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <依據條件取得名單>
-- =============================================
CREATE FUNCTION [dbo].[Fun_GetNotificationList]
(
@Condition varchar(500) --查詢條件
)
RETURNS @ReturnTable Table
(
MemberSeq int,
ACID varchar(22),
UID varchar(20),
FECID varchar(64),
Gender varchar(1),
Birthday varchar(8),
RecidentDistrictCode varchar(7),
CurrentDistrictCode varchar(7)
)
AS
BEGIN
if not (isnull(@Condition,'')='' or isjson(@Condition)=0)
begin
/*解析查詢條件*/
Declare @Gender varchar(1)
Declare @BirthdayS varchar(8)
Declare @BirthdayE varchar(8)
Declare @District varchar(max)
Declare @NotificationMainCategoryCode varchar(3)
Declare @NotificationSubCategoryCode varchar(5)
Declare @DistrictRowcount int
Declare @NeedDataFirst int=1
Declare @HasData bit=0
select @Gender=(JSON_VALUE(@Condition,'$.gender'))
,@BirthdayS=(JSON_VALUE(@Condition,'$.birthday[0]'))
,@BirthdayE=(JSON_VALUE(@Condition,'$.birthday[1]'))
,@District=trim(replace(replace(replace(JSON_QUERY(@Condition,'$.district'),char(34),''),char(91),''),char(93),''))
,@NotificationMainCategoryCode=(JSON_VALUE(@Condition,'$.itemNo'))
,@NotificationSubCategoryCode=(JSON_VALUE(@Condition,'$.itemSubNo'))
/*解析行政區*/
Declare @DistrictTable table (
District varchar(7) primary key
)
insert into @DistrictTable(District)
select trim(value)
from string_split(@District,',')
where value<>''
and len(trim(value))=7
set @DistrictRowcount=(select count(1) from @DistrictTable)
/*全無限制,直接回應全部資料*/
if (isnull(@Gender,'')='' and isnull(@BirthdayS,'')=''
and isnull(@BirthdayE,'')='' and isnull(@DistrictRowcount,0)=0
and isnull(@NotificationMainCategoryCode,'')='' and isnull(@NotificationSubCategoryCode,'')='')
begin
insert into @ReturnTable
select
Seq MemberSeq
,Account ACID
,ID UID
,HashID
,Gender
,Birthday
,RecidentDistrictCode
,CurrentDistrictCode
from MemberInfo mi
return
end
/*行政區*/
if (@DistrictRowcount>0)
begin
set @NeedDataFirst=0
insert into @ReturnTable
select
Seq MemberSeq
,Account ACID
,ID UID
,HashID
,Gender
,Birthday
,RecidentDistrictCode
,CurrentDistrictCode
from MemberInfo mi
inner join @DistrictTable dt on dt.District=mi.CurrentDistrictCode
union
select
Seq MemberSeq
,Account ACID
,ID UID
,HashID
,Gender
,Birthday
,RecidentDistrictCode
,CurrentDistrictCode
from MemberInfo mi
inner join @DistrictTable dt on dt.District=mi.RecidentDistrictCode
end
/*訂閱項目 - 細項*/
set @HasData=isnull((select top 1 1 from @ReturnTable),0)
if(isnull(@NotificationSubCategoryCode,'')<>'')
begin
if (@NeedDataFirst=1)
begin
set @NeedDataFirst=0
insert into @ReturnTable
select
mi.Seq MemberSeq
,Account ACID
,ID UID
,HashID
,Gender
,Birthday
,RecidentDistrictCode
,CurrentDistrictCode
from MemberInfo mi
inner join SubscribeManagement sm on mi.Seq=sm.MemberInfo_Seq
inner join NotificationSubCategory sub on sub.Code=@NotificationSubCategoryCode
and charindex(char(44)+cast(sub.Seq as varchar)+char(44),char(44)+sm.Content+char(44),1)>1
end
else if (@HasData=1)
begin
delete tmp
from @ReturnTable tmp
left join SubscribeManagement sm on tmp.MemberSeq=sm.MemberInfo_Seq
left join NotificationSubCategory sub on sub.Code=@NotificationSubCategoryCode
and charindex(char(44)+cast(sub.Seq as varchar)+char(44),char(44)+sm.Content+char(44),1)>1
where sub.Seq is null
end
end
/*訂閱項目 - 大項*/
set @HasData=isnull((select top 1 1 from @ReturnTable),0)
if(isnull(@NotificationMainCategoryCode,'')<>'')
begin
if (@NeedDataFirst=1)
begin
set @NeedDataFirst=0
;with subm as (
select
MemberInfo_Seq,value
from SubscribeManagement
cross apply STRING_SPLIT(Content, ',')
),
member as (
select distinct
MemberInfo_Seq
from subm
inner join NotificationMainCategory main on main.Code=@NotificationMainCategoryCode
inner join NotificationSubCategory sub on sub.seq=subm.value
and main.Seq=sub.NotificationMainCategory_Seq
)
insert into @ReturnTable
select
mi.Seq MemberSeq
,Account ACID
,ID UID
,HashID
,Gender
,Birthday
,RecidentDistrictCode
,CurrentDistrictCode
from member
inner join MemberInfo mi on mi.Seq=member.MemberInfo_Seq
end
else if (@HasData=1)
begin
;with subm as (
select
MemberInfo_Seq,value
from SubscribeManagement
cross apply STRING_SPLIT(Content, ',')
),
member as (
select distinct
MemberInfo_Seq
from subm
inner join NotificationMainCategory main on main.Code=@NotificationMainCategoryCode
inner join NotificationSubCategory sub on sub.seq=subm.value
and main.Seq=sub.NotificationMainCategory_Seq
)
delete tmp
from @ReturnTable tmp
left join member on member.MemberInfo_Seq=tmp.MemberSeq
where member.MemberInfo_Seq is null
end
end
/*生日*/
set @HasData=isnull((select top 1 1 from @ReturnTable),0)
if (isnull(@BirthdayS,'')<>'')
begin
if (@NeedDataFirst=1)
begin
set @NeedDataFirst=0
insert into @ReturnTable
select
Seq MemberSeq
,Account ACID
,ID UID
,HashID
,Gender
,Birthday
,RecidentDistrictCode
,CurrentDistrictCode
from MemberInfo
where isnull(Birthday,'')<>''
and Birthday>=@BirthdayS
end
else if (@HasData=1)
begin
delete @ReturnTable where Birthday is null
delete @ReturnTable where Birthday<@BirthdayS
end
end
set @HasData=isnull((select top 1 1 from @ReturnTable),0)
if (isnull(@BirthdayE,'')<>'')
begin
if (@NeedDataFirst=1)
begin
set @NeedDataFirst=0
insert into @ReturnTable
select
Seq MemberSeq
,Account ACID
,ID UID
,HashID
,Gender
,Birthday
,RecidentDistrictCode
,CurrentDistrictCode
from MemberInfo
where isnull(Birthday,'')<>''
and Birthday<=@BirthdayE
end
else if (@HasData=1)
begin
delete @ReturnTable where Birthday is null
delete @ReturnTable where Birthday>@BirthdayE
end
end
/*性別*/
set @HasData=isnull((select top 1 1 from @ReturnTable),0)
if (isnull(@Gender,'')<>'')
begin
if (@NeedDataFirst=1)
begin
set @NeedDataFirst=0
insert into @ReturnTable
select
Seq MemberSeq
,Account ACID
,ID UID
,HashID
,Gender
,Birthday
,RecidentDistrictCode
,CurrentDistrictCode
from MemberInfo
where Gender=@Gender
end
else if (@HasData=1)
begin
delete from @ReturnTable where isnull(Gender,'')<>@Gender
end
end
end
RETURN
END
F. 執行結果
i. 直接取得清單
/*
{
"gender": "F",
"age": [
"19800506",
"20131231"
],
"area": [
"6400600"
],
"itemNo": "T01",
"itemSubNo": "T0106"
}
*/
declare @Condition varchar(max)='{"gender":"F","age":["19800506","20131231"],"area":["6400600"],"itemNo":"","itemSubNo":""}'
select
MemberSeq '會員編號'
,ACID '會員卡號'
from dbo.Fun_GetNotificationList(@Condition)
ii. 進一步直接計算 : 符合名單之會員數
/*
{
"gender": "F",
"age": [
"19800506",
"20131231"
],
"area": [
"6400600"
],
"itemNo": "T01",
"itemSubNo": "T0106"
}
*/
declare @Condition varchar(max)='{"gender":"F","age":["19800506","20131231"],"area":["6400600"],"itemNo":"","itemSubNo":""}'
select count(1) 符合名單之會員數
from dbo.Fun_GetNotificationList(@Condition)
(4).摘要結論:
A. 達到複合條件排列組合且避免喪失index
B. 無須重複存取資料,解省系統資源
C. 查詢之結果資料無須轉接,即可直接做進一步篩選應用