2016-07-24 19 views
0

私はジョインで作業する際に論理的な困惑に苦しんでいます。 EmpStinとEmpEINの下に2つのテーブルがあります。だから私がしたいことは、これらの2つのテーブルを結合し、以下のような出力を生成することです。コンシューマーが1か月間にEmpNumberを持っていて、別の行にステータスを取得する必要がある場合は、ステータスはEmpNumberのヌルエントリ でなければなりません。複数の条件に基づいて2つのテーブルを結合する際に問題が発生する

出力を生成するためにこれらの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を定数と見なすことができます。基本的にステータスです。従業員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 

説明: クエリは2つのヘルパー関数

  1. DAYSINMONTHを使用していますが - 日のノーを取得します。特定の日付の月に1回
  2. SplitDates - 日付範囲を月全体の塊に分割します。期間が1か月間(7/10 - 7/20)でない場合は、 が返されます。

サンプルのDateTableを使用して、クエリ関数とヘルパー関数を使用することをおすすめします。この表には、EmpEIN表と同じスキーマがあります。私はここからCTEコードを借り:https://stackoverflow.com/a/20272758/2628302

+0

時間をかけてこれを考えてくれてありがとう。私の実際の問題は、結果を得るために2つのテーブルにどのように参加できるかということです。あなたのソリューションでは、1つのテーブルだけで作業しており、2つの関数を使ってPを割り当てる範囲にあるかどうかをチェックしています。しかし、私はEmpStatusテーブルとEmpEinテーブルにどのように参加して3行ID 1001の従業員は、ステータスを調整できるだけであるためです。 –

+0

もう一度ありがとうございます。結合部に戻ってきて、このselect *のような完全な外部結合をEmpEinから試みました。完全な外部結合EmpStatus b on aConsumerID = b.ConsumerID。問題は、EmpNumberに値があり、EmpNumberの開始日と終了日が1/1/20/30〜31/12/2015の完全な年全体を包含しない場合に限り、同じempIDに対して新しい行が必要であることです。私はあなたのためにすべての部分がなくても提案をするのは難しいと思っていますが、良いことに参加することを示唆しています –

+0

@kushalbhola ohhhなので、consumerIDにはEmpNumberの対応するデータがない月がありませんヌル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

を私が提供

注意を(そのデータはかなり静的な性質であるようそしてIAMが他のステータステーブルを使用していません)このソリューションは、使用する必要がある技術を説明するものです。最初のテーブルは通常の形式ではありません。結合を行う前に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 
関連する問題