【问题标题】:How to write oracle query to get the employee details as on future date?如何编写 oracle 查询以获取未来日期的员工详细信息?
【发布时间】:2012-05-24 03:56:31
【问题描述】:

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

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

例如员工 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')    )

【问题讨论】:

  • 你需要一个查询吗?或程序之类的东西也可以吗?
  • @TFool。查看或处理任何事情都会奏效。
  • 请帮助任何人?????????????

标签: sql oracle


【解决方案1】:

编辑:大幅修改。

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

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

所有 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,我们看到国家/地区的值是加拿大,在 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 的连接,然后是 'coalesceing 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

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

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

如果我们想考虑这种情况,我们需要再次在 select 中逐个字段地检查它。将上面的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 关于不在列名中使用保留字 whennew 的意见。 (虽然我把它们留在了我的样本中。)

【讨论】:

  • 感谢您的回答。正如您所说的警告,在这种情况下,我们会说仅更改城市而不更改其他城市,它应该采用旧的值(即,如果他们在生效日期之前没有更新)。
  • 具体来说,需要注意的是有多个更改的情况。如:生效日期为 20.May。 5 月 25 日更改为所有字段。 28.May 更改为 just city。如果您查询 30.May,它将选择 28.May 更改,然后从 20.May 生效日期开始获取其余部分。这有关系吗?如果他们一次只改变城市,不理会其他一切,那就很好了。只是需要扩展此代码的多个堆叠更改。
  • 在上述示例中,如果您查询的是 5 月 30 日。除 5 月 28 日更改外,如果其他字段未更改,则应从 5 月 25 日生效之日起获取其他字段。从 5 月 30 日起,5 月 20 日的更改已过时,因为 5 月 25 日进行了更改。
  • 我认为这可能就足够了——试一试并告诉我。 (现在必须去睡觉了,所以明天早上你会得到后续的,如果需要的话。:))
  • 请参阅我发布的最后一个 cmets 以获得 glen 的答案。此查询中也存在同样的问题。
【解决方案2】:
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)

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

【讨论】:

  • 感谢您的回答。我无法运行此查询。我正在使用甲骨文。错误是 SQL 命令意外结束。
  • 第一部分运行正常,但是当我尝试运行第二个选择语句时,即 select * from eff_emps 我得到错误。
  • 好的,我现在可以正常运行查询了。但是查询有一个小问题。如果我说有效日期为 12 月 10 日,那么我将地区和国家/地区设置为空,而不是最新的应该是德国。
  • 为我工作。我稍微修改了查询以处理只有一个区域(城市或地区或国家)发生变化的情况,而不是要求所有 3 个区域都发生变化。我没有要试用的 oracle 实例,但我认为在 Oracle 上运行此类查询没有问题。
  • 是否可以在不同的生效日期而不是在单个生效日期获取所有员工记录?例如emp 1(截至 6 月 10 日)、emp 1(截至 8 月 15 日)和 emp 1(截至 9 月 20 日)都在一个查询中。所以我不想像您在第二次查询中所做的那样传递生效日期。我只想获取不同日期的员工详细信息。这适用于所有员工。因此结果集将有每个员工的重复条目,但生效日期不同。
【解决方案3】:
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表

【讨论】:

  • 这样修改主表风险很大。表会不断更新。
猜你喜欢
  • 1970-01-01
  • 2020-10-08
  • 1970-01-01
  • 2011-09-28
  • 1970-01-01
  • 1970-01-01
  • 2020-03-25
  • 2020-11-22
相关资源
最近更新 更多