0

次の図に示すように、従業員テーブルと従業員更新テーブルがあります。従業員テーブルには、従業員の現在の詳細が常に保持されます。

従業員更新テーブルには、将来のある日付で従業員に発生する更新が保持されます。

例: 従業員のジェームスは現在インドで働いていますが、5 月 1 日からドイツに転勤になります。そのため、5 月 1 日から、彼の地域、国、都市が変更されます。

5 月 10 日の従業員の詳細を確認するために、レポートを引き出す必要があります。この場合、1 日より前に更新されたすべての従業員は、新しい最新の値で表示されるはずです。

ここに画像の説明を入力

region 、 country 、 city のように、従業員の更新が含まれる可能性のあるフィールドは他にほとんどありません。

一部の従業員が更新を行わず、現在のレコードを表示する必要がある可能性もあります。

このクエリを実行することは可能ですか?

このレポートは非​​常に頻繁に必要になるため、このクエリの結果をビューとして取得し、レポートをクエリできるようにすることをお勧めします。

編集 : -

以下は、グレンが回答したこれまでに試したクエリです

WITH eff_emps AS ( 
SELECT 
    emp_id, firstname, region_id, country_id, city, effective_date   
FROM 
  bi_employee 

UNION 

SELECT 
  x.employee_id, 
  e.firstname, 
  CAST(MAX(x.region_id) as number) AS region_id, 
  CAST(MAX(x.country_id) as number) AS country_id, 
  MAX(x.city) AS city, 
  x.effective_date   
FROM ( 
      SELECT 
          employee_id, effective_date                ,
          CASE WHEN COLUMN_NAME = 'REGION_ID' THEN NEW_VALUE ELSE NULL END AS region_id               ,
          CASE WHEN COLUMN_NAME = 'COUNTRY_ID' THEN NEW_VALUE ELSE NULL END AS country_id                ,
          CASE WHEN COLUMN_NAME = 'CITY' THEN NEW_VALUE ELSE NULL END AS city            
      FROM bi_employee_update 
) x ,Bi_employee e   

WHERE e.emp_id = x.employee_id   
GROUP BY 
    x.employee_id, e.firstname, x.effective_date 
) 

SELECT *   FROM eff_emps f   
WHERE effective_date = ( 
  SELECT 
      MAX(effective_date)  
  FROM eff_emps 
  WHERE 
    emp_id = f.emp_id 
    AND effective_date <= TO_DATE('2012-09-01', 'YYYY-MM-DD')    )
4

3 に答える 3

2

編集:大幅に改訂。

最初の解決策は、個々のフィールドが異なる時期に変化することから生じる問題を考慮していませんでした。元の effective_date 値に最新の更新を適用することだけに関心がありました。

これはその回答を書き直したものであり、変更されている各フィールドがまとめてではなく個別に考慮されるようにします。

すべての sql は sqlfiddle で示されています: http://sqlfiddle.com/#!4/ca4f4/6


任意の単一フィールドの最新の変更を取得することから始めます。これでそれを達成できます:

select *
  from ( select empid, when, what from updates
  where when <= to_date('2012-05-10', 'yyyy-mm-dd') 
  ) pivot_data
pivot ( max(when)
       for what in ('region' as max_region_date, 
                    'city' as max_city_date, 
                    'country' max_country_date)
);

これにより、ピボット テーブルが作成され、各フィールドが個別に考慮され、レポートの発効日以下の最大日付が出力されます。このピボット テーブルはより大きなクエリに追加されるため、レポートの日付が埋め込まれる場所にすぎないことに注意してください。

元のセットにさらにデータを追加するには、次を使用しましょう。

EMPID   NEW     WHAT    WHEN
1       germany region  May, 01 2012
1       germany country May, 01 2012
1       münchen city    May, 01 2012
2       boston  city    June, 01 2012
2       canada  country July, 01 2012
2       toronto city    July, 01 2012
2       vancouver   city    August, 01 2012

したがって、8 月 15 日のレポート日付に対して上記のクエリを実行すると、次のようになります。

EMPID   MAX_REGION_DATE MAX_CITY_DATE   MAX_COUNTRY_DATE
1       May, 01 2012    May, 01 2012    May, 01 2012 
2       (null)          August, 01 2012 July, 01 2012

(null) は、empid = 2 の領域に更新のエントリがないことを表します。empid = 2 の他の 2 つの日付は、そのフィールドの最新の変更日を表します。

new次に、これを各フィールドのその日付の値に変換する必要があります。これは、次のような方法で実行できます。

select distinct u1.empid,
   (select u2.new from updates u2
    where u2.what = 'region'
      and u2.empid = u1.empid
      and u2.when = max_dates.max_region_date
    ) region, max_dates.max_region_date,
   (select u3.new from updates u3
    where u3.what = 'country'
      and u3.empid = u1.empid
      and u3.when = max_dates.max_country_date
    ) country, max_dates.max_country_date,
   (select u4.new from updates u4
    where u4.what = 'city'
      and u4.empid = u1.empid
      and u4.when = max_dates.max_city_date
    ) city, max_dates.max_city_date
from updates u1
left join 
  (select *
   from ( select empid, when, what from updates
          where when <= to_date('2012-07-15', 'yyyy-mm-dd') 
        ) pivot_data
    pivot ( max(when)
        for what in ('region' as max_region_date, 
                    'city' as max_city_date, 
                    'country' max_country_date)
       )
   ) max_dates on max_dates.empid = u1.empid;

上記のピボット テーブル出力を派生テーブルとして埋め込み、特定の日付に対応する各フィールド値を個別に選択しています。

これを 15.August のレポートで実行すると、次のようになります。

EMPID REGION    MAX_REGION_DATE    COUNTRY    MAX_COUNTRY_DATE  CITY       MAX_CITY_DATE
2     (null)    (null)             canada     July, 01 2012     vancouver August, 01 2012 
1     germany   May, 01 2012       germany    May, 01 2012      münchen   May, 01 2012 

従業員 2 については、国の値が canada であり、7 月 1 日に更新されたのに対し、最新の都市であるバンクーバーはその後の 8 月 1 日に更新されていることがわかります。

ここで、これを従業員に対するクエリと組み合わせる必要があります。

select e.empid, e.name,
  (coalesce( u.region, e.region)) region,
  (coalesce( u.city, e.city)) city,
  (coalesce( u.country, e.country)) country
from employee e
left join (
  select distinct u1.empid,
   (select u2.new from updates u2
    where u2.what = 'region'
      and u2.empid = u1.empid
      and u2.when = max_dates.max_region_date
    ) region, max_dates.max_region_date,
   (select u3.new from updates u3
    where u3.what = 'country'
      and u3.empid = u1.empid
      and u3.when = max_dates.max_country_date
    ) country, max_dates.max_country_date,
   (select u4.new from updates u4
    where u4.what = 'city'
      and u4.empid = u1.empid
      and u4.when = max_dates.max_city_date
    ) city, max_dates.max_city_date
  from updates u1
  left join 
    (select *
     from ( select empid, when, what from updates
            where when <= to_date('2012-08-15', 'yyyy-mm-dd') 
          ) pivot_data
     pivot ( max(when)
       for what in ('region' as max_region_date, 
                    'city' as max_city_date, 
                    'country' max_country_date)
       )
  ) max_dates on max_dates.empid = u1.empid
) u on u.empid = e.empid
order by e.empid;

この最大の部分は、前に示した派生テーブルです。残りは に対して結合しemployee、次に「null を合体」しing to either take the updated value, or the current value in employee if the update value isます。

これにより、次の結果が得られます。

EMPID    NAME    REGION    CITY      COUNTRY
1        james   germany   münchen   germany
2        mike    americas  vancouver canada

8月15日のレポート日付を使用する場合。上記の sqlfiddle で、私のテスト データに対して (または独自のデータを追加して) 他の日付を検証できます。

では、最終課題です。上記は、更新が将来のみであることを検証するためのデータ整合性制約があることを前提としています。更新が従業員テーブルに適用されると (したがって、その effective_date フィールドが更新されます)、その行は更新テーブルから削除されます。つまり、更新は適用されていない変更のみで構成されます。

その場合を考慮したい場合は、フィールドごとに個別に選択して再度チェックする必要があります。上記の選択を次のように変更します。

select e.empid, e.name,
  (coalesce( (case 
                when (u.max_region_date > e.effective_date) 
                then u.region
              end),
            e.region)) region,
  (coalesce( (case 
                when (u.max_city_date > e.effective_date) 
                then u.city
              end),
            e.city)) city,
  (coalesce( (case 
                when (u.max_country_date > e.effective_date) 
                then u.country
              end),
            e.country)) country
from . . . . . the from in the previous query above . . .

私の場合、以前と同じ出力が得られます。


when追加する必要がありますが、予約語を使用しないこと、およびnew列名に使用しないことについて、Glenn に同意します。(サンプルに残しましたが。)

于 2012-05-24T05:41:03.460 に答える
1
create table employee(emp_id int, name varchar(64), region varchar(64), country varchar(64), city varchar(64), effective_date date)
create table employee_updates(emp_id int, old varchar(64), new varchar(64), effective_date date, what varchar(64))

insert into employee values(1, 'james', 'asia', 'india', 'mumbai',to_date('2012-04-10', 'YYYY-MM-DD'));
insert into employee values(2, 'rick', 'americas', 'us', 'ny',to_date('2012-03-01', 'YYYY-MM-DD'));

insert into employee_updates values(1, 'asia', 'germany', to_date('2012-05-01', 'YYYY-MM-DD'), 'region');
insert into employee_updates values(1, 'india', 'germany', to_date('2012-05-01', 'YYYY-MM-DD'), 'country');
insert into employee_updates values(1, 'mumbai', 'munich', to_date('2012-05-01', 'YYYY-MM-DD'), 'city');

-- <UPDATE 3>
insert into employee values(3, 'jane', 'Europe', 'UK', 'London',to_date('2012-03-01', 'YYYY-MM-DD'));
insert into employee_updates values(3, 'Europe', 'Canada', to_date('2012-05-01', 'YYYY-MM-DD'), 'region');
insert into employee_updates values(3, 'UK', 'Canada', to_date('2012-05-01', 'YYYY-MM-DD'), 'country');
insert into employee_updates values(3, 'London', 'Toronto', to_date('2012-11-01', 'YYYY-MM-DD'), 'city');

: うまくいけば、これはもう少し明確です。有効な日付のレコードの完全なセットを返します。

 WITH combined_records AS (

   SELECT emp_id, name, region, country, city, effective_date
     FROM employee

   UNION

   -- Union with a set of pseudo employee records generated from the updates table
   -- Any of these new "employee" records could have a NULL value in one of the region/country/city fields

   -- 2. Compress the sparse matrix to merge records sharing common (emp_id + effective_date)
   SELECT x.emp_id, e.name, MAX(x.region) AS region, MAX(x.country) AS country, MAX(x.city) AS city, x.effective_date
     FROM ( -- 1. Create a sparse matrix of one row with each record from the updates table
            SELECT emp_id, effective_date
                  ,CASE WHEN WHAT = 'region' THEN NEW ELSE NULL END AS region
                  ,CASE WHEN WHAT = 'country' THEN NEW ELSE NULL END AS country
                  ,CASE WHEN WHAT = 'city' THEN NEW ELSE NULL END AS city
              FROM employee_updates
          ) x
         ,employee e
     WHERE e.emp_id = x.emp_id
     GROUP BY x.emp_id, e.name, x.effective_date

)


SELECT a.emp_id, a.name
      ,COALESCE(a.region, rgn.region) AS region
      ,COALESCE(a.country, cntry.country) AS country
      ,COALESCE(a.city, cty.city) AS city
      ,a.effective_date
  FROM combined_records a
      ,combined_records rgn
      ,combined_records cntry
      ,combined_records cty
  WHERE a.emp_id = rgn.emp_id
    AND rgn.effective_date = ( SELECT MAX(effective_date)
                                 FROM combined_records
                                 WHERE emp_id = a.emp_id
                                   AND region IS NOT NULL
                                   AND effective_date <= a.effective_date )
    AND a.emp_id = cntry.emp_id
    AND cntry.effective_date = ( SELECT MAX(effective_date)
                                 FROM combined_records
                                 WHERE emp_id = a.emp_id
                                   AND country IS NOT NULL
                                   AND effective_date <= a.effective_date )
    AND a.emp_id = cty.emp_id
    AND cty.effective_date = ( SELECT MAX(effective_date)
                                 FROM combined_records
                                 WHERE emp_id = a.emp_id
                                   AND city IS NOT NULL
                                   AND effective_date <= a.effective_date )
  ORDER BY emp_id, effective_date;

出力:

 emp_id | name  |  region  | country |  city   | effective_date
--------+-------+----------+---------+---------+----------------
      1 | james | asia     | india   | mumbai  | 2012-04-10
      1 | james | germany  | germany | munich  | 2012-05-01
      2 | rick  | americas | us      | ny      | 2012-03-01
      3 | jane  | Europe   | UK      | London  | 2012-03-01
      3 | jane  | Canada   | Canada  | London  | 2012-05-01
      3 | jane  | Canada   | Canada  | Toronto | 2012-11-01
(6 rows)

ちなみに、「いつ」ではなく「発効日」を使いました。列に「when」や「new」などのキーワードを使用すると、混乱を招き、悲しみを引き起こします。

于 2012-05-24T04:47:49.497 に答える
0
CREATE OR REPLACE
PROCEDURE P_GET_EMP_UPDATED(edate date) AS
 emp_rec emp_up%rowtype;
 query VARCHAR2(100);    
CURSOR emp_cur IS SELECT * FROM emp_up WHERE eff_date <= edate and eff_date >= sysdate; 
 BEGIN 
   OPEN emp_cur; 
   LOOP 
   FETCH emp_cur INTO emp_rec; 
   EXIT WHEN emp_cur%NOTFOUND;             
  -- query  := 'update emp set '||emp_rec.what||' =  ''' || emp_rec.new || ''' where empid = '|| emp_rec.empid;    
   --dbms_output.put_line (query); 

    EXECUTE IMMEDIATE  'update emp set '||emp_rec.what||' =  ''' || emp_rec.new || ''' where empid = '|| emp_rec.empid;

 END LOOP;   
 END P_GET_EMP_UPDATED;

この手順により、必要な日付まで emp テーブルが更新されます。その後、ロールバックされます。

  SAVEPOINT Emp_modify;
 --call procedure
  execute p_get_emp_updated(to_date('2012-06-03', 'YYYY-MM-DD'));
  select * from emp;  --These rows are from the temprorily updated table
  rollback to SAVEPOINT Emp_modify;

emp から * を選択します。//これにより、元の emp テーブルが得られます

于 2012-05-24T08:07:44.417 に答える