I'm trying to replicate a DO loop from SAS into Oracle PL/SQL. Basically, this DO loop iterates over the table and creates multiple rows for a single employee. I'm not extremely familiar with loops in PL/SQL so any help is appreciated. I can't think of any way to recreate this except for creating numerous tables then combining them. I'll explain more on my thoughts at the end; for now, please see an example of the data and what the SAS DO loop is doing.
HIST_EMPLOYEE Table:
+----------+----------+--------+
| EMPLOYEE | START_YR | END_YR |
+----------+----------+--------+
| JOHN | 2013 | 2014 |
| WILL | 2012 | 2016 |
| MARK | 2012 | 2012 |
+----------+----------+--------+
DO loop in SAS:
DATA HIST_EMPLOYEE_NEW;
SET HIST_EMPLOYEE;
DO YR = START_YR TO END_YR;
OUTPUT;
END;
RUN;
Output:
+----------+----------+--------+------+
| EMPLOYEE | START_YR | END_YR | YR |
+----------+----------+--------+------+
| JOHN | 2013 | 2014 | 2013 |
| JOHN | 2013 | 2014 | 2014 |
| WILL | 2012 | 2016 | 2012 |
| WILL | 2012 | 2016 | 2013 |
| WILL | 2012 | 2016 | 2014 |
| WILL | 2012 | 2016 | 2015 |
| WILL | 2012 | 2016 | 2016 |
| MARK | 2012 | 2012 | 2012 |
+----------+----------+--------+------+
The way I'd solve this (which is not efficient in any way) is to create tables filtered on END_YR < START_YR + i
where i is from 0 to 10
, then create the YR
column then combine all tables. I can discuss this further, but I already feel like this is the bad way of doing things.
Just make a table with one value of YR ranging from some minimum to maximum and join on that.
So something like:
with years as (
select 2012 + rownum - 1 as YR
from dual
connect by rownum < (2016 - 2012)
)
select a.*,b.YR
from HIST_EMPLOYEE a
inner join years b
on a.start_yr <= b.yr and b.yr <= a.end_yr
;
Just change the lower (2012) and upper (2016) limits to change the number of years you want to generate.
See this question: How to populate calendar table in Oracle?
Here's one way. The "with" clause is called a Common Table Expression (CTE) and just sets up the test data with a unique id for each entry.
The query uses a CONNECT BY which can be thought of as a looping mechanism for each row returned. It comes along with a variable called "level" which is incremented once for each iteration (it starts at 1). To define how many times to "loop" for each row is the expression (end_yr-start_yr+1). For JOHN we'll need to loop 2 times as we need 2 rows, WILL 5 rows, etc. The "PRIOR ID" clauses help to handle the multiple rows for each original row.
with hist_employee(id, employee, start_yr, end_yr) as (
select 1, 'JOHN', 2013, 2014 from dual union all
select 2, 'WILL', 2012, 2016 from dual union all
select 3, 'MARK', 2012, 2012 from dual
)
select employee, start_yr, end_yr, (start_yr + (level-1)) as YR
from hist_employee
connect by level <= end_yr-start_yr+1
and prior id = id
and prior sys_guid() is not null
order by id;
EMPLOYEE START_YR END_YR YR
-------- ---------- ---------- ----------
JOHN 2013 2014 2013
JOHN 2013 2014 2014
WILL 2012 2016 2012
WILL 2012 2016 2013
WILL 2012 2016 2014
WILL 2012 2016 2015
WILL 2012 2016 2016
MARK 2012 2012 2012
8 rows selected.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.