0

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

而员工更新表包含将在未来某个日期发生在员工身上的更新。

例如,员工 james 目前在印度工作,但从 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

编辑:大幅修改。

第一个解决方案没有考虑各个领域在不同时间发生变化所产生的问题。它只关心将最新更新应用于原始有效日期值。

这是对该答案的重写,返回,以便单独考虑正在更改的每个字段,而不是联合考虑。

所有 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,然后是 'coalesce ing to either take the updated value, or the current value in employee if the update value isnull`。

这导致:

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

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

现在,最后一个问题。上面假设您有数据完整性约束来验证仅在将来进行更新——一旦将更新应用于员工表(从而更新它的有效日期字段),该行就会从更新表中删除。也就是说,更新仅包含未应用的更改。

如果我们想考虑这种情况,我们需要再次在 select 中逐个字段地检查它。将上面的选择修改为:

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 关于不在您的列名中使用保留字when的意见。new(虽然我把它们留在了我的样本中。)

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

顺便说一句,我使用了“effective_date”而不是“when”。您正在使用诸如“何时”和“新”之类的关键字来表示只会令人困惑并导致悲伤的列。

于 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 回答