(标题可能需要重新措辞;请随时进行编辑)
我正在审查一个工作项目,负责将SAS程序转换为View。该程序用于从我们的表中为另一组数据源生成源文件。该视图的目的是产生与先前创建的源文件相同的内容,以便可以退出SAS程序,而另一组可以指向我们的视图。
困难在于我们(公司)具有的子类型化概念。在用户方面,我们从技术上讲有3种类型:
根据与日期/时间相关的子类型定义的一个报告项目是用户电话号码。
例如,给定的用户正在类型之间进行转换(UG1到UG2;创建两个条目),并且每种类型的条目的有效日期跨度可能与这两种类型中的TODAY重叠。某些仅与成员相关联的指示符设置为在每个条目处表示它们的类型为UG2(在联接上,包含UG1的条目也将具有指示当前类型的指示器)。
在报告该用户的电话号码时,我们只想查看一个电话号码,但是我们有多个可用的电话号码(家庭,办公,移动和其他)。对于电话号码本身,我们有一个合并的设置,按照家庭,移动,工作和其他的顺序排列(如果家庭没有价值,请检查移动等)。关于子类型化,我们必须检查与UG2相关的电话号码,但是如果没有返回电话号码,那么我们将尝试从UG1中查找电话号码(如果存在)。否则,电话号码将返回为空,并且报告将显示“无电话号码”。如果出现UG1类型的用户,我们还必须朝另一个方向看。
注意:对于包含电话号码的表,还有一个指示符用于指示相同的子类型(UG1条目和UG2条目)。 (基本上是根据创建时或有效日期跨度时的子类型创建的用户信息)
所以简而言之,逻辑类似于这种格式:
排除UG0中的用户。它主要被列为一种可能性,但是如果不清楚,我可以删除它。
对于该视图,对于一个子类型中存在的边缘案例用户,我遇到了困难,而对于另一个子类型中的边缘案例用户,我则遇到了困难。鉴于视图方面的一些限制,没有变量或没有创建## table(临时表),我开始使用通用表表达式(cte)。
每种类型都有自己的CTE,UG1Phone和UG2Phone,并且使用CTE来组合它们。我发现使用完全外部联接可以获取所有需要的数据,以及一些额外的空条目。由于null可能发生(某些方面不存在),因此我看不到一种简单地执行检查以忽略null的方法。我做了一个where语句,检查了用户的子类型指示符,并对与电话号码条目关联的子类型进行了空检查(如果用户子类型只是UG1,则完全外部联接将根据UG2 CTE)。
我的主要问题涉及分隔(UG1 CTE和UG2 CTE)以及如何对数据进行分组(带有许多Case语句的Full Outer Join)。
如前所述,我对每个用户子类型都有一个CTE,并且没有明确的路线图来知道是否还会有其他子类型。我可以合并CTE并移动FULL OUTER JOIN,但是感觉嵌套子查询太混乱了。再次,我对这种连接方式的理由是,如果需要并可以继续使用下一个子类型。
还有另一种产生相同结果的方法吗?我的目标是减少看起来重复的查询或看起来可以简化的查询(对于每个用户CTE,唯一的区别是指标值;合并的CTE具有WHERE语句,该语句也基于如上所述的排序逻辑)。
对于case语句,View要求使用case语句来确定首先使用FULL OUTER JOIN的哪一侧(coalesce语句和其他列)。当前,每个定义的列都包含一个case语句,以确定首先要检查哪个表的可用数据。
是否有一种方法可以执行一个语句,或者至少不添加每一列的case语句?我正在寻找一种方法来确定每个列的使用顺序,而不必在每个列上进行相同的检查。例如:
SELECT
CASE
WHEN Indicator = 1
THEN ISNULL(UG1.effectiveDate, UG2.EffectiveDate)
WHEN Indicator = 2
THEN ISNULL(UG2.effectiveDate, UG2.EffectiveDate)
ELSE
NULL
END AS [EffectiveDate]
, CASE
WHEN Indicator = 1
THEN ISNULL(UG1.phoneNumber, UG2.phoneNumber)
WHEN Indicator = 2
THEN ISNULL(UG2.phoneNumber, UG1.phoneNumber)
ELSE
NULL
END AS [PhoneNumber]
, -- and so on
随意问我上面的任何问题。对于列出的项目,我找到了一个可行的解决方案,但是我觉得对于那些正在研究的人来说,许多单独的部分可能不清楚。因此,我要解决的问题是定义一个明确的方法,将这些数据分组/合并,以确保删除重复的空数据并以正确的顺序检查数据。
(编辑:添加涉及的表结构)
dbo.UsersCommonTable
列:
dbo.UserInformation
列:
dbo.UserReportView
列:
让我知道是否需要进行任何更改,或者对所要求的内容有误解。
(编辑:添加SAS特定代码/逻辑和当前要求)
除了电话号码之间的合并以外,其他要求是相同的。以前,HomePhone是唯一使用的列,并且存在用户类型的定向概念。因此,该逻辑看起来更像这样:
以UG2开头,依此类推。
proc sql noprint;
create table cc_user_list as
select distinct
UserID
,EffectiveDate as effdt2
,mdy(input(scanq(EffectiveDate ,1,'/'),2.),
input(scanq(EffectiveDate ,2,'/'),2.),
input(scanq(EffectiveDate ,3,'/'),4.)) format yymmdd10. as effdt
,mdy(input(scanq(TerminationDate,1,'/'),2.),
input(scanq(TerminationDate,2,'/'),2.),
input(scanq(TerminationDate,3,'/'),4.)) format yymmdd10. as termdt
from imp.UsersCommonTable
where UserTypeId = 1 or UserTypeId = 2
order by UserID ;
quit;
data cc_user_list;
set cc_user_list;
if effdt <= today() <= termdt; /* Only keep currently Active cases */
run;
proc sql noprint;
create table User_phone_number as
select distinct
cats(UserID, '01') as User_no
,homephone as usedPhoneNumber
,datechanged
,startdate
,enddate
from impl=.UserInformation
where (HomePhone ^= ' ') and
UserTypeID = 1 and
(datepart(enddate) >= today()
or enddate = . )
order by User_no ;
quit;
proc sql noprint;
create table User_phone_number as
select distinct
cats(UserID, '01') as User_no
,homephone as otherPhoneNumber
,datechanged
,startdate
,enddate
from imp.UserInformation
where (HomePhone ^= ' ') and
UserTypeID = 2 and
(datepart(enddate) >= today()
or enddate = . )
order by User_no ;
quit;
data User_phone_number (drop=usedPhoneNumber);
set User_phone_number;
if usedPhoneNumber = ' ' and otherPhoneNumber^= ' ' then usedPhoneNumber= otherPhoneNumber;
run;
data User_phone_number (drop=usedPhoneNumber);
set User_phone_number;
if usedPhoneNumber = ' ' then usedPhoneNumber= 'No Phone Number.';
run;
我缩短并改写了很多可用代码。让我知道这是否没有道理(我可以在某种程度上翻译一些SAS,但是我不知道在SAS中制作/更改程序的实际操作)
(编辑:示例数据)
在UsersCommon表中:
UserId CurrentUserTypeId EffectiveDate TerminationDate
U001 2 01/01/2018 09/30/2018
U001 2 09/01/2018 09/30/2019
在用户信息表中:
UserId UserTypeId StartDate EndDate HomePhone
U001 1 02/01/2018 09/15/2018 1112223344
U001 2 09/01/2018 09/30/2019 NULL
报告结果在2018年2月1日至2018年9月15日之间的任意一天运行:
UserId PhoneNumber
U001 1112223344
报告结果在2018年9月15日之后的任意一天运行:
UserId PhoneNumber
U001 No Phone Number.
(进行编辑以包括相关查询的示例)
CREATE VIEW [dbo].[UserReportView] AS
WITH UG1Phone AS (
SELECT
UserId
, UserTypeId
, HomePhone
, MobilePhone
, WorkPhone
, OtherPhone
, StartDate
, EndDate
FROM
UserInformation uinfo
INNER JOIN
(
SELECT
UserId
, UserTypeId
, MAX(DateChanged) LatestChanged
FROM
UserInformation
WHERE
UserTypeId = 1
) Latest
ON
uinfo.UserId = Latest.UserId
AND uinfo.UserTypeId = Latest.UserTypeId
AND uinfo.DateChanged = Latest.LatestChanged
WHERE
uinfo.EndDate >= GETDATE() OR uinfo.EndDate IS NULL)
),
UG2Phone AS (
SELECT
UserId
, UserTypeId
, HomePhone
, MobilePhone
, WorkPhone
, OtherPhone
, StartDate
, EndDate
FROM
UserInformation uinfo
INNER JOIN
(
SELECT
UserId
, UserTypeId
, MAX(DateChanged) LatestChanged
FROM
UserInformation
WHERE
UserTypeId = 2
) Latest
ON
uinfo.UserId = Latest.UserId
AND uinfo.UserTypeId = Latest.UserTypeId
AND uinfo.DateChanged = Latest.LatestChanged
WHERE
uinfo.EndDate >= GETDATE() OR uinfo.EndDate IS NULL)
),
OutputPhone (UserId, ProvidedPhone, DateChanged, StartDate, EndDate, UserTypeId) AS (
SELECT
CASE
WHEN UG1.OrderIndex = 1
THEN ISNULL(UG1.UserId, UG2.UserId)
WHEN UG2.OrderIndex = 1
THEN ISNULL(UG2.UserId, UG1.UserId)
ELSE
NULL
END As UserId
, CASE
WHEN UG1.OrderIndex = 1
THEN ISNULL(UG1.ProvidedNumber, UG2.ProvidedNumber)
WHEN UG2.OrderIndex = 1
THEN ISNULL(UG2.ProvidedNumber, UG1.ProvidedNumber)
ELSE
NULL
END As ProvidedNumber
, --and so on for specified columns
FROM
(
SELECT
DISTINCT
CASE WHEN uct.CurrentUserTypeId = 1 THEN 1 ELSE 2 END OrderIndex
, ugp.UserId
, ugp.DateChanged
, ugp.StartDate
, ugp.EndDate
, ugp.UserTypeId
, COALESCE(ugp.HomePhone, ugp.MobilePhone, ugp.WorkPhone, ugp.OTherPhone) ProvidedNumber
FROM
UG1Phone ugp
INNER JOIN
UsersCommonTable uct
ON
ugp.UserId = uct.UserId AND GETDATE() BETWEEN uct.EffectiveDate and uct.TerminationDate
) UG1
FULL OUTER JOIN
(
SELECT
DISTINCT
CASE WHEN uct.CurrentUserTypeId = 2 THEN 1 ELSE 2 END OrderIndex
, ugp.UserId
, ugp.DateChanged
, ugp.StartDate
, ugp.EndDate
, ugp.UserTypeId
, COALESCE(ugp.HomePhone, ugp.MobilePhone, ugp.WorkPhone, ugp.OTherPhone) ProvidedNumber
FROM
UG2Phone ugp
INNER JOIN
UsersCommonTable uct
ON
ugp.UserId = uct.UserId AND GETDATE() BETWEEN uct.EffectiveDate and uct.TerminationDate
) UG2
ON
UG1.UserId = UG2.UserId
WHERE
1 = CASE
WHEN UG1.OrderIndex = 1
THEN CASE WHEN UG1.UserTypeId IS NOT NULL THEN 1 ELSE 0 END
WHEN UG2.OrderIndex = 1
THEN CASE WHEN UG2.UserTypeId IS NOT NULL THEN 1 EKSE 0 END
ELSE
0
END
), --and so on to complete the rest of the report
GO