如何编写oracle查询以获取未来日期的员工详细信息?

时间:2012-05-24 03:56:32

标签: sql oracle

我有一个员工表和员工更新表,如下图所示。员工表始终保存任何员工的当前详细信息。

员工更新表包含将来某个日期将发生的更新。

e.g。员工詹姆斯目前在印度工作,但从1月起,他将被转移到德国。因此,从1月1日起,他的地区,国家和城市将会发生变化。

我需要提取一份报告,以了解员工的详细信息,如同10月5日。在这种情况下,所有具有1oth之前更新的员工都应该显示其新的最新值。

enter image description here

与地区,国家和城市一样,我几乎没有其他可能有员工更新的字段。

某些员工也可能没有任何更新,然后应显示他们当前的记录。

是否可以进行此查询?

我将经常需要此报告,因此最好将此查询的结果作为一个视图,然后可以查询报告吗?

编辑: -

以下是格伦已经回答过的查询

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')    )

3 个答案:

答案 0 :(得分: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

因此,如果您运行上述查询的报告日期为15.August,您将获得:

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的另外两个日期代表该字段的最新更改日期。

现在,我们需要将此值转换为每个字段在该日期的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,我们看到该国的价值是加拿大,这是在01.July更新,而最近的城市,温哥华,后来,在01.August。

现在,我们需要将此与针对员工的查询相结合:

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的加入,然后是“合并ing to either take the updated value, or the current value in employee if the update value is null”。

这导致:

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

使用报告日期为15.August。您可以在上面给出的sqlfiddle中根据我的测试数据验证其他日期(或添加您自己的日期。

现在,最后一期。上面假设您有数据完整性约束来验证更新仅在将来 - 一旦更新应用于employee表(从而更新它的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 . . .

在我的情况下,您获得与以前相同的输出。


我应该补充一下,我同意Glenn关于不在列名中使用保留字whennew的问题。 (虽然我把它们放在我的样本中。)

答案 1 :(得分: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)

顺便说一下,我用的是“effective_date”而不是“when”。你正在使用像“when”和“new”这样的关键字,这些关键词会让人感到困惑并引起悲伤。

答案 2 :(得分: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表