我有一个员工表和员工更新表,如下图所示。员工表始终保存任何员工的当前详细信息。
员工更新表包含将来某个日期将发生的更新。
e.g。员工詹姆斯目前在印度工作,但从1月起,他将被转移到德国。因此,从1月1日起,他的地区,国家和城市将会发生变化。
我需要提取一份报告,以了解员工的详细信息,如同10月5日。在这种情况下,所有具有1oth之前更新的员工都应该显示其新的最新值。
与地区,国家和城市一样,我几乎没有其他可能有员工更新的字段。
某些员工也可能没有任何更新,然后应显示他们当前的记录。
是否可以进行此查询?
我将经常需要此报告,因此最好将此查询的结果作为一个视图,然后可以查询报告吗?
编辑: -
以下是格伦已经回答过的查询
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') )
答案 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关于不在列名中使用保留字when
和new
的问题。 (虽然我把它们放在我的样本中。)
答案 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表