2016-07-24 19 views
0

조인으로 작업하는 동안 논리적 인 퍼즐로 어려움을 겪고 있습니다. 그래서 EmpStatus와 EmpEIN 아래에 2 개의 테이블이 있습니다. 그래서 제가하고 싶은 것은이 2 개의 테이블을 조인하고 아래와 같은 출력을 만들어내는 것입니다. 소비자가 다른 행에 상태를 가져야하는 것보다 한 달 동안 EmpNumber를 보유한 경우 상태가 EmpNumber에 null 항목 과 함께 표시되어야합니다.여러 조건을 기반으로 2 개의 테이블을 결합하는 데 문제가 발생했습니다.

출력을 생성하기 위해이 두 테이블을 어떻게 조인 할 수 있는지 잘 모르겠습니다. 일부 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

"P"에 대한 논리 이해가하지 않는 것 - 플러스 최종 날짜 열에는 날짜 일관성이 없다. 약간의 설명과 실제로 시도한 것을 제공해주십시오. – MageeWorld

+0

@MageeWorld이 문제 때문에 P를 상수로 가정 할 수 있습니다. 기본적으로 상태. Employee 1001에는 EmpNumber 102020202가 1/1/2015 년에서 31/1/2015 기간 동안 할당되었으므로 JanStatus 열의 상태 P 만 출력의 EmpNumber 행에 대해 가져야합니다. –

+0

JanStatus 등에 대한 열이 있지만 EmpEIN의 날짜는 (아마도 다른) 연도입니다. Jan2015Status 등을 의미 했습니까? 그런 다음 EmpStatus 및 OUTPUT의 열 수 (이름이 다를 수 있음)가 달라질 수 있으며 '2015 년 1 월에서 2015 년 1 월'과 같이 고정 된 창이 있습니까? 해결되지 않으면 동적 SQL이 필요합니다. 대부분의 경우 훌륭한 솔루션이 아닙니다. – mathguy

답변

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 

설명 : 쿼리가 두 도우미 기능

를 사용
  1. DaysInMonth - 일의 번호를 가져옵니다을 주어진 날짜에 한 달에
  2. SplitDates - 날짜 범위를 전체 월 단위로 나눕니다. 기간이 전체 월 (7/10 - 7/20)에 미치지 않으면 은 같은 것을 반환합니다.

샘플 DateTable을 사용하여 쿼리 및 도우미 함수를 사용하는 것이 좋습니다. 이 테이블은 EmpEIN 테이블과 동일한 스키마를가집니다.

주 (데이터가 거의 자연의 정적으로 그리고 스피가 다른 상태 테이블을 사용하지 않음) : https://stackoverflow.com/a/20272758/2628302

+0

시간을내어 이것을 가져 오셔서 감사합니다. 내 실제 문제는 결과를 얻기 위해 2 개의 테이블을 결합 할 수있는 방법입니다. 귀하의 솔루션에서 당신은 단 하나의 테이블로 작업하고 있으며 P를 할당 할 수있는 범위에 있는지 (2 함수로) 확인하고 있습니다. 그러나 EmpStatus와 EmpEin 테이블에 어떻게 가입 할 수 있는지, ID 1001 인 직원은 상태를 조정할 수 있기 때문에 –

+0

다시 한 번 감사드립니다. 합류 부분으로 돌아와서, 나는이 외부 select 조인을 * EmpEin에서 완전 외부 조인을 시도했다. EmpStatus b on a.ConsumerID = b.ConsumerID. 문제는 EmpNumber에 값이 있고 EmpNumber 시작 및 종료 날짜가 1/1/2015-31/12/2015에서 전체 전체 연도를 포함하지 않을 때만 동일한 empID에 대해 새 행이 필요하다는 것입니다. 나는 당신이 모든 조각없이 제안을하는 것이 어렵다는 것을 이해합니다. 그러나 좋은 제안에 합류 한 제안은 무엇입니까? –

+0

@kushalbhola ohhh 그래서 consumerID의 경우 EmpNumber가있는 해당 데이터가없는 누락 월이있는 경우 null EmpNumber (이 경우에는 3 행)와 함께 반영됩니다 .... 맞습니까? – objectNotFound

0

오라클 대안 : 내가 제공

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 
+0

2015 만 필요한 경우' '|| TO_CHAR (EmpNumberStartDate,'YYYY ')'를'2015 '로 변경하십시오. – Unoembre

0

여기에서 CTE 코드를 빌려 이 솔루션을 사용하여 필요한 기술을 설명하십시오. 첫 번째 표는 정상적인 형식이 아닙니다. 당신은 당신이 조인을하기 전에 그것을 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 
관련 문제