Logic for change in one column value in pl/sql - oracle

I have an assignmentes table asg_tab with effective start date and effective end date columns which track on which dates which change was made.
asg_tab
eff_start_date eff_End_date PER_ASG_ATTRIBUTE2 job name pos name
01-Jan-2015 03-feb-2015 Ck Bonus Retail Mgr
04-Feb-2015 20-Feb-2015 UK Bonus Sales Mgr
21-Feb-2015 28-Nov-2015 UK Bonus Sales Snr. Mgr
Now I have to calculate the number of days for which PER_ASG_ATTRIBUTE2 is UK Bonus. For example in the above case it will be days between 04-Feb-2015 to 28-Nov-2015.
I have written the logic below, which is fetching values from cursor.
cursor cur_asg
is
select
eff_start_date,
eff_End_date,
PER_ASG_ATTRIBUTE2,
job_name,
pos_name
from
asg_tab
Logic I have built :
START_DT ='01-Jan-2015'
END_DT ='31-Dec-2015'
IF PER_ASG_ATTRIBUTE2 LIKE '%UK Bonus%' THEN
(
l_new_ATTR = PER_ASG_ATTRIBUTE2
l_effective_date = i.eff_start_date
IF (l_new_ATTR <> l_old_ATTR) AND (l_effective_date >= START_DT ) AND (l_effective_date =< END_DT)
THEN
(
l_days=eff_end_date -eff_start_date
)
l_old_ATTR = l_new_ATTR
)
The issue which is coming up is that from this condition: IF (l_new_ATTR <> l_old_ATTR) AND (l_effective_date >= START_DT ) AND (l_effective_date =< END_DT)
This condition will pick the 2nd row where the PER_ASG_ATTRIBUTE2 changed from Ck Bonus to UK Bonus but when the pos name changes the 3rd row is generated.
Even though the PER_ASG_ATTRIBUTE2 is still UK Bonus this will not be filtered in the if condition.
What more can I add to this condition ?

Related

Power Pivot - Looking up details with start and end dates?

I need help in the formula correctness of my approach in this PowerPivot table in Excel.
I have two tables: daily timesheet and employee details. The idea is to lookup the department, sub-department, and managers from the employee_details table based on the employee number and shift date in the daily_timesheet table.
Here's a representation of the tables:
daily_timesheet
shift_date
emp_num
scheduled_hours
actual_worked_hrs
dept
sub_dept
mgr
2022-02-28
01234
7.5
7.34
16100
16181
05432
2022-03-15
01234
7.5
7.50
16200
16231
06543
employee_details
emp_num
dept_code
sub_dept_code
mgr_emp_num
start_date
end_date
is_current
01234
16000
16041
04321
2022-01-01
2022-01-31
FALSE
01234
16100
16181
05432
2022-02-01
2022-01-28
FALSE
01234
16200
16231
06543
2022-03-01
null
TRUE
End dates have null values if it is the employee's current assignment; but it's never null if the is_current field is FALSE.
Here's what I've managed so far:
First, lookup the current start date in the employee details is less than or equal to the shift date.
If true, I'm using the LOOKUP function to return the department, sub-department, and manager by searching the employee number and the true value in the is_current field.
Else, I use the MIN function to get the value of those fields and wrap it around a CALCULATE function then apply FILTER for: (1) emp_num matching the timesheet, (2) is_current that has a FALSE value, (3) start_date less than or equal to the shift_date, and (4) end_date is greater than or equal to the shift_date.
And the bedrock of my question is actually, the item 3 above. I know using the MIN function is incorrect, but I can't find any solution that will work.
Here's the formula I've been using for to get the dept in the daily_timesheet table from the employee_details table:
=IF(
LOOKUP(employee_details[start_date],
employee_details[emp_num],
daily_timesheet[emp_num],
employee_details[is_current] = TRUE) <= daily_timesheet[shift_date],
LOOKUP(employee_details[dept_code],
employee_details[emp_num],
daily_timesheet[emp_num],
employee_details[is_current] = TRUE),
CALCULATE(MIN(employee_details[dept_code]),
FILTER(employee_details, employee_details[emp_num] = daily_timesheet[emp_num]),
FILTER(employee_details, employee_details[is_current] = FALSE),
FILTER(employee_details, employee_details[start_date] <= daily_timesheet[shift_date]),
FILTER(employee_details, employee_details[end_date] >= daily_timesheet[shift_date]))
)
Any advice please?

Oracle: Update values in table with aggregated values from same table

I am looking for a possibly better approach to this.
I have created a temp table in Oracle 11.2 that I'm using to pre calculate values that I will need in other selects instead of always generating them again with each select.
create global temporary table temp_foo (
DT timestamp(6), --only the date part will be used in this example but for later things I will need the time
Something varchar2(100),
Customer varchar2(100),
MinDate timestamp(6),
MaxDate timestamp(6),
Filecount int,
Errorcount int,
AvgFilecount int,
constraint PK_foo primary key (DT, Customer)
) on commit preserve rows;
I then first insert some fixed values for everything except AvgFilecount. AvgFilecount should contain the average for the Filecount for the 3 previous records (going by the date in DT). It doesn’t matter that the result will be converted to an int, I don’t need the decimal places
DT | Customer | Filecount | AvgFilecount
2019-04-30 | x | 10 | avg(2+3+9)
2019-04-29 | x | 2 | based on values before this
2019-04-28 | x | 3 | based on values before this
2019-04-27 | x | 9 | based on values before this
I thought about using a normal UPDATE statement as this should be faster than looping through the values. I should mention that there are no gaps in the DT field but obviously there is a first one where I won‘t find any previous records. If I would loop through, I could easily calculate AvgFilecount with (the record before previous record/2 + previous record)/3 which I cannot with UPDATE as I cannot guarantee the order of how they are executed. So I‘m fine with just taking the last 3 records (going by DT) and calcuting it from there.
What I thought would be an easy update is giving me headaches. I‘m mostly doing SQL Server where I would just join the 3 other records but it seems is a bit different in Oracle. I have found https://stackoverflow.com/a/2446834/4040068 and wanted to use the second approach in the answer.
update
(select curr.DT, curr.temp_foo, curr.Filecount, curr.AvgFilecount as OLD, (coalesce(Minus1.Filecount, 0) + coalesce(Minus2.Filecount, 0) + coalesce(Minus3.Filecount, 0)) / 3 as NEW
from temp_foo curr
left join temp_foo Minus1 ON Minus1.Customer = curr.Customer and trunc(Minus1.DT) = trunc(curr.DT-1)
left join temp_foo Minus2 ON Minus2.Customer = curr.Customer and trunc(Minus2.DT) = trunc(curr.DT-2)
left join temp_foo Minus3 ON Minus3.Customer = curr.Customer and trunc(Minus3.DT) = curr.DT-3
order by 1, 2
)
set OLD = NEW;
Which gives me an
ORA-01779: cannot modify a column which maps to a non key-preserved
table
01779. 00000 - "cannot modify a column which maps to a non key-preserved table"
*Cause: An attempt was made to insert or update columns of a join view which
map to a non-key-preserved table.
*Action: Modify the underlying base tables directly.
I thought this should work as both join conditions are in the primary key and thus unique. I am currently implementing the first approach in the above mentioned answer but it is getting quite big and it feels like there should be a better solution to this.
Other things I thought about trying:
using a nested subselect (nested because Oracle doesn’t know top(n) and I need to sort the subselect) to select the previous 3 records ordered by DT and then he outer select with rownum <=3 and then I could just use AVG(). However, I was told subselect can be quite slow and joins are better in Oracle performance wise. Dunno if that is really the case, haven‘t done any testing
Edit: My insert right now looks like this. I am already aggregating the Filecount for a day as there can be multiple records per DT per Customer per Something.
insert into temp_foo (DT, Something, Customer, Filecount)
select dates.DT, tbl1.Something, tbl1.Customer, coalesce(sum(tbl3.Filecount),0)
from table(Function_Returning_Daterange(NULL, NULL)) dates
cross join
(SELECT Something,
Code,
Value
FROM Table2 tbl2
WHERE (Something = 'Value')) tbl1
left outer join Table3 tbl3
on tbl3.Customer = tbl1.Customer
and trunc(tbl3.MinDate) = trunc(dates.DT)
group by dates.DT, tbl1.Something, tbl1.Customer;
You could use an analytic average with a window clause:
select dt, customer, filecount,
avg(filecount) over (partition by customer order by dt
rows between 3 preceding and 1 preceding) as avgfilecount
from tmp_foo
order by dt desc;
DT CUSTOMER FILECOUNT AVGFILECOUNT
---------- -------- ---------- ------------
2019-04-30 x 10 4.66666667
2019-04-29 x 2 6
2019-04-28 x 3 9
2019-04-27 x 9
and then do the update part with a merge statement:
merge into tmp_foo t
using (
select dt, customer,
avg(filecount) over (partition by customer order by dt
rows between 3 preceding and 1 preceding) as avgfilecount
from tmp_foo
) s
on (s.dt = t.dt and s.customer = t.customer)
when matched then update set t.avgfilecount = s.avgfilecount;
4 rows merged.
select dt, customer, filecount, avgfilecount
from tmp_foo
order by dt desc;
DT CUSTOMER FILECOUNT AVGFILECOUNT
---------- -------- ---------- ------------
2019-04-30 x 10 4.66666667
2019-04-29 x 2 6
2019-04-28 x 3 9
2019-04-27 x 9
You haven't shown your original insert statement; it might be possible to add the analytic calculation to that, and avoid the separate update step.
Also, if you want the first two date values to be calculated as if the 'missing' extra days before them had zero counts, you could use sum and division instead of avg:
select dt, customer, filecount,
sum(filecount) over (partition by customer order by dt
rows between 3 preceding and 1 preceding)/3 as avgfilecount
from tmp_foo
order by dt desc;
DT CUSTOMER FILECOUNT AVGFILECOUNT
---------- -------- ---------- ------------
2019-04-30 x 10 4.66666667
2019-04-29 x 2 4
2019-04-28 x 3 3
2019-04-27 x 9
It depends what you expect those last calculated values to be.

Dax - Filter by value then count occurence in table

I am working with an employee table and was wondering if I could get some help.
My data table has rows with start and end date values. I filter these rows down using Filter(data table, [start date]<=[Measure.MaxMonth]&&[end date]>=[Measure.MaxMonth]. [Measure.MaxMonth] is a measure that sits on a disconnected date table and functions like a parameter.
Here is a formula that I have been testing but have not been getting the desired results:
Measure.DirectReports = CALCULATE(COUNTROWS(Data Table),filter(Data Table,Data Table[Mgr ID]=Data Table[Emp ID]&&Data Table[Start Date]<=[Meas.LastMonth]&&Data Table[End Date]>=[Meas.LastMonth]))
Measure.LastMonth = max(EOM[End of Month]) ->this value can equal any month end between July 2005 and July 2017. EOM is a date table with a row for each month end - 7/31/2005, 8/31/2005,....7/31/2017
This gives me a table structured liked this:
Emp ID,Emp Attr,Mgr ID,Start Date,End Date
1,B,4,10/1/2013,10/6/2013
1,B,4,10/7/2013,12/29/2013
1,B,4,12/30/2013,12/28/2014
1,B,8,12/29/2014,10/4/2015
1,B,8,10/5/2015,12/27/2015
1,B,12,12/28/2015,5/15/2016
1,B,12,5/16/2016,10/2/2016
1,B,12,10/3/2016,12/25/2016
2,B,4,12/1/2014,12/28/2014
2,B,4,12/29/2014,12/27/2015
2,B,4,12/28/2015,2/7/2016
2,B,4,2/8/2016,3/6/2016
2,B,8,3/7/2016,6/1/2016
3,B,6,7/1/2015,12/27/2015
3,B,8,12/28/2015,6/30/2016
3,B,6,7/1/2016,9/4/2016
3,B,6,9/5/2016,12/25/2016
3,B,6,12/26/2016,5/7/2017
3,B,4,5/8/2017,6/11/2017
3,B,4,6/12/2017,6/25/2017
3,B,4,6/26/2017,7/9/2017
3,B,19,7/10/2017,12/31/9999
4,A,,7/1/1996,4/2/2006
4,A,,4/3/2006,12/31/2007
4,A,,1/1/2008,5/22/2011
4,A,,5/23/2011,11/16/2014
4,A,,11/17/2014,6/11/2017
4,A,,6/12/2017,6/25/2017
4,A,,6/26/2017,12/31/9999
5,B,4,11/8/2010,1/2/2011
5,B,4,1/3/2011,5/22/2011
5,B,4,5/23/2011,1/1/2012
5,B,4,1/2/2012,5/31/2012
5,B,4,6/1/2012,7/1/2012
5,B,4,7/2/2012,9/7/2012
6,B,4,1/3/2011,5/22/2011
6,B,4,5/23/2011,9/5/2011
6,B,4,9/6/2011,1/1/2012
6,B,4,1/2/2012,12/30/2012
6,B,4,12/31/2012,12/29/2013
6,B,4,12/30/2013,5/18/2014
6,B,4,5/19/2014,11/16/2014
6,B,4,11/17/2014,12/28/2014
6,B,4,12/29/2014,3/22/2015
6,B,4,3/23/2015,12/27/2015
6,B,4,12/28/2015,3/6/2016
6,B,4,3/7/2016,8/21/2016
6,B,4,8/22/2016,10/30/2016
6,B,4,10/31/2016,12/25/2016
6,B,4,12/26/2016,1/8/2017
6,B,4,1/9/2017,5/7/2017
6,B,4,5/8/2017,6/11/2017
6,B,4,6/12/2017,6/25/2017
6,B,4,6/26/2017,12/31/9999
7,B,4,1/2/2012,12/30/2012
7,B,4,12/31/2012,12/29/2013
7,B,4,12/30/2013,5/18/2014
7,B,4,5/19/2014,11/16/2014
7,B,4,11/17/2014,12/28/2014
7,B,4,12/29/2014,3/8/2015
7,B,4,3/9/2015,1/18/2016
7,B,4,1/19/2016,2/19/2016
8,B,6,12/31/2012,11/3/2013
8,B,4,11/4/2013,12/29/2013
8,B,4,12/30/2013,1/26/2014
8,B,4,1/27/2014,5/18/2014
8,B,4,5/19/2014,11/16/2014
8,B,4,11/17/2014,12/28/2014
8,B,4,12/29/2014,3/22/2015
8,B,4,3/23/2015,12/27/2015
8,B,4,12/28/2015,7/1/2016
10,B,4,10/3/2011,12/18/2011
10,B,4,12/19/2011,12/30/2012
10,B,4,12/31/2012,2/23/2013
10,B,4,2/24/2013,11/20/2014
11,B,4,2/1/2011,2/27/2011
11,B,4,2/28/2011,5/1/2011
12,B,4,9/15/2012,12/31/2012
12,B,4,9/15/2012,12/31/2012
12,B,4,1/1/2013,12/31/2013
12,B,4,1/1/2013,4/30/2014
12,B,4,1/1/2014,4/30/2014
12,B,4,5/1/2014,11/16/2014
12,B,4,5/1/2014,12/28/2014
12,B,4,11/17/2014,11/30/2014
12,B,4,12/1/2014,12/28/2014
12,B,4,12/29/2014,12/27/2015
12,B,4,12/29/2014,12/30/2016
12,B,4,12/28/2015,12/30/2016
12,B,4,12/31/2016,12/31/2016
12,B,4,1/1/2017,6/11/2017
12,B,4,6/12/2017,6/25/2017
12,B,4,6/26/2017,7/9/2017
12,B,19,7/10/2017,12/31/9999
13,B,4,12/28/2015,9/4/2016
13,B,4,9/5/2016,12/25/2016
13,B,4,12/26/2016,6/11/2017
13,B,4,6/12/2017,6/25/2017
13,B,4,6/26/2017,12/31/9999
14,B,4,1/12/2015,12/27/2015
14,B,4,12/28/2015,12/25/2016
14,B,4,12/26/2016,6/11/2017
14,B,4,6/12/2017,6/25/2017
14,B,4,6/26/2017,12/31/9999
16,B,4,9/14/2015,10/19/2015
17,B,6,8/22/2016,12/25/2016
17,B,6,12/26/2016,5/7/2017
17,B,4,5/8/2017,6/11/2017
17,B,4,6/12/2017,6/25/2017
17,B,4,6/26/2017,7/9/2017
17,B,19,7/10/2017,12/31/9999
18,B,6,9/12/2016,12/25/2016
18,B,6,12/26/2016,5/7/2017
18,B,13,5/8/2017,6/11/2017
18,B,13,6/12/2017,6/25/2017
18,B,13,6/26/2017,7/9/2017
18,B,19,7/10/2017,12/31/9999
19,B,4,7/10/2017,12/31/9999
Empl ID is a unique employee number. Mgr ID references the Empl ID of the employee's manager at the desired point in time (Measure.LastMonth). Emp Attr is an attribute of the employee, such as level.
Does anyone have any ideas on how to create a measure that will count the occurrences of Empl ID in Mgr ID? Ideally, if I am creating a visual in Power BI and I filter based on Empl Attr ="A", can the resulting measure value give me the result = 3 -> the empl id "1" occurs 3 times in the mgr id column.
I need this to be a measure and not a calculated column so that I can trend the results over time (end of month on X axis in trend visual).
Thanks for the help and let me know if you have any questions!
Edit - Updating basically the entire answer due to new information about the problem.
I feel that your core problem is trying to create a relationship between two tables based on the evaluation of an expression (End of Month between Start Date and End Date). From my experience, the easiest way to workaround this is to CROSSJOIN the two tables and then filter it down based on whatever expression you would like. For this, I created a new table with this formula.
Results = DISTINCT(
SELECTCOLUMNS(
FILTER(
CROSSJOIN('Data Table', EOM),
'Data Table'[Start Date] <= EOM[End of Month] && 'Data Table'[End Date] >= EOM[End of Month]
),
"Emp ID", [Emp ID],
"End of Month", [End of Month]
)
)
One more piece before finally making the relationships, we need a list of unique employee IDs. That can easily be obtained by creating a new table with this formula.
Employees = DISTINCT(
SELECTCOLUMNS('Data Table',
"Emp ID", 'Data Table'[Emp ID]
)
)
From here, create relationships between all of the tables as shown in the image below.
I know you asked for a measure, but bear with me as I feel confident that this will get you what you want. Add a new column to the Results table with this formula.
DirectReports = CALCULATE(
COUNTROWS('Data Table'),
FILTER(ALL('Data Table'),
'Data Table'[Mgr ID] = EARLIER(Results[Emp ID]) &&
'Data Table'[Start Date] <= EARLIER(Results[End of Month]) &&
'Data Table'[End Date] >= EARLIER(Results[End of Month])
)
)
At this point, I would hide the Emp ID and End of Month from the results table and any other field(s) you desire. From there, make your visuals. For example, you said you wanted to show direct report count over time, so I made this simple line chart and card.

How to generate each day of backlog for a ticket

Hi I'm trying to create a procedure for calculating the backlog for each day.
For example: I have a ticket with ticket_submitdate on 12-sep-2015 and resolved_date on 15-sep-2015 in one table. This ticket should come as a backlog in the backlog_table because it was not resolved on the same day as the ticket_submitdate.
I have another column date_col in the backlog_table where the date on which the ticket was a backlog is displayed,i.e, it should be there in the ticket_backlog table for dates 13-sep-2015 and 14-sep-2015 and the date_col column should have this ticket for both these dates.
Please help.
Thanks in advance.
Here is some test data:
create table backlog (ticket_no number, submit_date date, resolved_date date);
insert into backlog values (100, date '2015-09-12', date '2015-09-15');
insert into backlog values (200, date '2015-09-12', date '2015-09-14');
insert into backlog values (300, date '2015-09-13', date '2015-09-15');
insert into backlog values (400, date '2015-09-13', date '2015-09-16');
insert into backlog values (500, date '2015-09-13', date '2015-09-13');
This query generates a list of dates which spans the range of BACKLOG records, and joins them to the BACKLOG.
with dt as ( select min(submit_date) as st_dt
, greatest(max(resolved_date), max(submit_date)) as end_dt
from backlog)
, dt_range as ( select st_dt + (level-1) as date_col
from dt
connect by level <= ( end_dt - st_dt ))
select b.ticket_no
, d.date_col
from backlog b
cross join dt_range d
where d.date_col between b.submit_date and b.resolved_date
and b.submit_date != b.resolved_date
order by b.ticket_no
, d.date_col
/
Therefore it produces a list of TICKET_NOs with all the dates when they are live:
TICKET_NO DATE_COL
---------- ---------
100 12-SEP-15
100 13-SEP-15
100 14-SEP-15
100 15-SEP-15
200 12-SEP-15
200 13-SEP-15
200 14-SEP-15
300 13-SEP-15
300 14-SEP-15
300 15-SEP-15
400 13-SEP-15
400 14-SEP-15
400 15-SEP-15
14 rows selected.
SQL>
The result set does not include ticket #500 because it was resolved on the day of submission. You will probably need to tweak the filters to fit your actual business rules.
I m not sure I understood your question, if you are looking for all dates between two date range then you can use below query -
select trunc(date_col2+lv) from
(select level lv from dual connect by level < (date_col1-date_col2-1) )
order by 1

Sum and condition in oracle on update

am working on oracle database system , and i have to do the following:
All employees got a salary increase depending on their hiring dates as follows: (use one SQL statement)
10% salary increase if the employee was hired after 2001.
7% salary increase if the employee was hired between 1997 and 2000.
4% salary increase if the employee was hired before 1996.
and i used the following code
update employee set salary = case
when hire_date>'1-1-2001' then salary=salary+salary*0.1
when hire_date between ('1-1-1997','1-1-2000') then salary=salary+salary*0.07
when hire_date < '1-1-1996' then salary=salary+salary*0.04
end
what is wrong with that ???
Try:
update stackoverflow_employee set salary = case
when hire_date>'1-Jan-2001' then salary+salary*0.1
when hire_date between ('1-Jan-1997') AND ('1-Jan-2000') then salary+salary*0.07
when hire_date < '1-Jan-1996' then salary+salary*0.04
end
Points to remember:
Correct use of between operator Between X AND X
Date Format is 1-Jan-2012
While you are using SET salary = 'YOUR case statements', don't use salary= salary+salary*0.04 wihin the case.
Hope now your query is working :)
I would write this as:
update employee
set salary = (salary * (case when hire_date >= '2001-01-01' then 1.10
when hire_date >= '1997-01-01' then 1.07
else 1.04
end)
);
This fixes the obvious problems with the salary = and funky between clause. It also moves into the case statement the logic that only needs to be there -- the amount of the increase. Tt removes the between by taking advantage of the fact that case statements return the first match. Finally, it uses ISO standard date formats.

Resources