在使用连接时,我正在努力解决逻辑难题。所以我有两个表显示在EmpStatus和EmpEIN下面。所以我想要做的是加入这两个表并产生如下输出。如果消费者拥有一个完整月份的EmpNumber而不是应该获得另一行中的状态,那么状态应该带有空条目 在EmpNumber。
我不知道如何加入这两个表来产生输出。如果一些sql专业版可以帮助我,那将是非常棒的。
EmpStatus:
ConsumerID JanStatus FebStatus MarStatus AprStatus MayStatus
1001 P P P P P
1002 P P P P P
1003 P P P P P
1004 P P P P P
1005 P P P P P
EmpEIN:
ConsumerID EmpNumber EmpNumberStartDate EmpNumberEndDate
1001 102020202 1/1/2015 31/1/2015 -- dates in dd/mm/yyyy format
1001 210201021 1/2/2015 31/3/2015
1002 NULL NULL NULL
1003 NULL NULL NULL
1004 NULL NULL NULL
1005 NULL NULL NULL
OUTPUT:
ConsumerID EmpNumber JanStaus FebStatus MarStatus AprStatus MayStatus
1001 102020202 P NULL NULL NULL NULL
1001 210201021 NULL P P NULL NULL
1001 NULL NULL NULL NULL P P
1002 NULL P P P P P
1003 NULL P P P P P
1004 NULL P P P P P
1005 NULL P P P P P
答案 0 :(得分:0)
我已经解决了解决方案的难点,即将日期范围分解为整个月(以及任何溢出的任何一方),并检查是否有任何跨度不能覆盖整个月。然后我在MAX(CASE ... WHEN ... END)stmt中标记每个月的状态,并使用固定状态' P'或NULL。所以实际上我完全忽略了EmpStatus表。加入以下解决方案是您需要做的事情,还有更多的测试。
-- create test data . This will be your EmpEIN table
create table DateTable ( ConsumerID int, EMP_NUM int, from_date date , to_date date)
insert into DateTable VALUES ( 1001, 1, '2/1/2015', '3/31/2015' )
CREATE FUNCTION [dbo].[DaysInMonth](@date datetime)
RETURNS int
AS
BEGIN
SET @date = DATEADD(MONTH, 1, @date)
DECLARE @result int = (select DAY(DATEADD(DAY, -DAY(@date), @date)))
RETURN @result
END
Go
CREATE FUNCTION SplitDates
(
@from_date Datetime ,
@to_date Datetime
)
RETURNS
@Table_Var TABLE
(
mo_from_date Datetime,
mo_to_date Datetime,
Days_In_Month int
)
AS
BEGIN
;WITH cte_SplitDates AS
(
SELECT @from_date as from_date
, @to_date as to_date
, @from_date AS mo_from_date
, DATEADD(day, day(@from_date)* -1 + 1, @from_date) AS bom_date
-- FROM DateTable
UNION ALL
SELECT from_date
, to_date
, DATEADD(month,1,bom_date)
, DATEADD(month,1,bom_date)
FROM cte_SplitDates
where DATEADD(month,1,mo_from_date) < to_date
)
INSERT INTO @Table_Var
SELECT
mo_from_date ,
CASE when to_date < DATEADD(month,1,bom_date)
THEN
to_date
ELSE
DATEADD(day, -1, DATEADD(month,1,bom_date))
END AS mo_to_date ,
[dbo].[DaysInMonth](mo_from_date) as Days_In_Month
FROM cte_SplitDates
RETURN
END
现在使用上述帮助函数的查询中最重要的部分是:
SELECT A.ConsumerID, A.EMP_NUM ,
MAX( CASE WHEN A.Mnth =1 AND Days_In_Month = Diff_Start_End_Dates THEN 'P' ELSE NULL END ) as JanStaus ,
MAX( CASE WHEN A.Mnth =2 AND Days_In_Month = Diff_Start_End_Dates THEN 'P' ELSE NULL END ) as Febtaus ,
MAX( CASE WHEN A.Mnth =3 AND Days_In_Month = Diff_Start_End_Dates THEN 'P' ELSE NULL END ) as MarStaus ,
MAX( CASE WHEN A.Mnth =4 AND Days_In_Month = Diff_Start_End_Dates THEN 'P' ELSE NULL END ) as AprStaus ,
MAX( CASE WHEN A.Mnth =5 AND Days_In_Month = Diff_Start_End_Dates THEN 'P' ELSE NULL END ) as MayStaus
-- Other Months can be added just as above
FROM
(
SELECT D.ConsumerID, D.EMP_NUM , month( S.mo_from_date) as Mnth,
S.mo_from_date , S.mo_to_date, S.Days_In_Month, DATEDIFF( Day, S.mo_from_date , S.mo_to_date) + 1 As Diff_Start_End_Dates
FROM -- DateTable D
(
SELECT * FROM DateTable D
UNION ALL
Select ConsumerID , NULL As EMP_NUM , DATEADD(D, 1, MAX(to_date) ) , STR(YEAR(MAX(to_date))) + '-12-31' -- This is how the NULL EmpNumber is added.
FROM DateTable D
Group by ConsumerID
) D
CROSS APPLY dbo.SplitDates(D.from_date, D.to_date) S
) A
Group by A.ConsumerID, A.EMP_NUM
说明: 该查询使用两个辅助函数
我建议您使用示例DateTable来使用Query和辅助函数。此表与EmpEIN表具有相同的模式。 (而且我没有使用其他状态表,因为它的数据本质上是非常静态的)
注意:我从这里借用了CTE代码:https://stackoverflow.com/a/20272758/2628302
答案 1 :(得分:0)
Oracle替代方案:
WITH base AS
( SELECT
ee.ConsumerID, ee.EmpNumber,
CASE
WHEN EmpNumber IS NULL THEN JanStatus
WHEN
EmpNumberStartDate<= TO_DATE('01-Jan-'||TO_CHAR(EmpNumberStartDate,'YYYY')) AND
EmpNumberEndDate >=ADD_MONTHS(TO_DATE('01-Jan-'||TO_CHAR(EmpNumberStartDate,'YYYY')),1)-1
THEN JanStatus
ELSE NULL
END JanStatus,
CASE
WHEN EmpNumber IS NULL THEN FebStatus
WHEN
EmpNumberStartDate<= TO_DATE('01-Feb-'||TO_CHAR(EmpNumberStartDate,'YYYY')) AND
EmpNumberEndDate >=ADD_MONTHS(TO_DATE('01-Feb-'||TO_CHAR(EmpNumberStartDate,'YYYY')),1)-1
THEN FebStatus
ELSE NULL
END FebStatus,
CASE
WHEN EmpNumber IS NULL THEN MarStatus
WHEN
EmpNumberStartDate<= TO_DATE('01-Mar-'||TO_CHAR(EmpNumberStartDate,'YYYY')) AND
EmpNumberEndDate >=ADD_MONTHS(TO_DATE('01-Mar-'||TO_CHAR(EmpNumberStartDate,'YYYY')),1)-1
THEN MarStatus
ELSE NULL
END MarStatus,
CASE
WHEN EmpNumber IS NULL THEN AprStatus
WHEN
EmpNumberStartDate<= TO_DATE('01-Apr-'||TO_CHAR(EmpNumberStartDate,'YYYY')) AND
EmpNumberEndDate >=ADD_MONTHS(TO_DATE('01-Apr-'||TO_CHAR(EmpNumberStartDate,'YYYY')),1)-1
THEN AprStatus
ELSE NULL
END AprStatus,
CASE
WHEN EmpNumber IS NULL THEN MayStatus
WHEN
EmpNumberStartDate<= TO_DATE('01-May-'||TO_CHAR(EmpNumberStartDate,'YYYY')) AND
EmpNumberEndDate >=ADD_MONTHS(TO_DATE('01-May-'||TO_CHAR(EmpNumberStartDate,'YYYY')),1)-1
THEN MayStatus
ELSE NULL
END MayStatus
FROM
EmpStatus es
JOIN
EmpEIN ee ON ee.ConsumerID = es.ConsumerID
)
SELECT
b.ConsumerID, TO_NUMBER(NULL) EmpNumber,
NVL2(b.JanStatus, NULL, es.JanStatus) JanStatus,
NVL2(b.FebStatus, NULL, es.FebStatus) FebStatus,
NVL2(b.MarStatus, NULL, es.MarStatus) MarStatus,
NVL2(b.AprStatus, NULL, es.AprStatus) AprStatus,
NVL2(b.MayStatus, NULL, es.MayStatus) MayStatus
FROM
( SELECT
ConsumerID,
MAX(JanStatus) JanStatus,
MAX(FebStatus) FebStatus,
MAX(MarStatus) MarStatus,
MAX(AprStatus) AprStatus,
MAX(MayStatus) MayStatus
FROM base
WHERE empnumber IS NOT NULL
GROUP BY ConsumerID
) b
JOIN
EmpStatus es ON es.ConsumerID = b.ConsumerID
UNION ALL
SELECT *
FROM base
ORDER BY ConsumerID, EmpNumber NULLS LAST
答案 2 :(得分:0)
我提供此解决方案来说明您需要使用的技术。第一张表格不正常;在进行连接之前,你需要UNPIVOT它。第一个表中的列名包含实际数据,这是一个巨大的设计缺陷。您必须在UNPIVOT时恢复数据 - 您将在查询中看到我是如何做到这一点的。 (我对它进行了硬编码,您可以使用应用于列名称的字符串函数的日期函数,但在我看来这并不是一个很大的改进...你必须在最后的透视中“撤消”这个,无论如何,你无法避免硬编码列名。)
我将“P everywhere”更改为不同的字母,以便更容易理解正在发生的事情。加入是customerid
,按日期 - 您将在查询中看到此内容。
with
empstatus ( ConsumerID, JanStatus, FebStatus, MarStatus, AprStatus, MayStatus ) as (
select 1001, 'A', 'B', 'C', 'D', 'E' from dual union all
select 1002, 'F', 'G', 'H', 'I', 'J' from dual union all
select 1003, 'K', 'L', 'M', 'N', 'O' from dual union all
select 1004, 'P', 'Q', 'R', 'S', 'T' from dual union all
select 1005, 'U', 'V', 'W', 'X', 'Y' from dual
),
empein ( ConsumerID, EmpNumber, EmpNumberStartDate, EmpNumberEndDate ) as (
select 1001, 102020202, date '2015-01-01', date '2015-01-31' from dual union all
select 1001, 210201021, date '2015-02-01', date '2015-03-31' from dual union all
select 1002, NULL, NULL , NULL from dual union all
select 1003, NULL, NULL , NULL from dual union all
select 1004, NULL, NULL , NULL from dual union all
select 1005, NULL, NULL , NULL from dual
),
a ( consumerid, mth, val ) as ( -- UNPIVOT
select * from empstatus
unpivot ( val for mth in ( JanStatus as date '2015-01-01',
FebStatus as date '2015-02-01', MarStatus as date '2015-03-01',
AprStatus as date '2015-04-01', MayStatus as date '2015-05-01' ) )
),
j ( consumerid, empnumber, mth, val ) as ( -- JOIN
select a.consumerid, e.empnumber, a.mth, a.val
from a left outer join empein e
on a.consumerid = e.consumerid
and a.mth between e.empnumberstartdate and empnumberenddate
)
select * from j -- PIVOT
pivot ( min(val) for mth in ( date '2015-01-01' as JanStatus,
date '2015-02-01' as FebStatus, date '2015-03-01' as MarStatus,
date '2015-04-01' as AprStatus, date '2015-05-01' as MayStatus ) )
order by consumerid, empnumber;
<强>输出强>:
CONSUMERID EMPNUMBER JANSTATUS FEBSTATUS MARSTATUS APRSTATUS MAYSTATUS
---------- ---------- --------- --------- --------- --------- ---------
1001 102020202 A
1001 210201021 B C
1001 D E
1002 F G H I J
1003 K L M N O
1004 P Q R S T
1005 U V W X Y