SQR Procedure parameters/arguments - peoplesoft

I'm trying to understand more about how arguments/parameters are used when calling and executing procedures. I understand that procedures that contain an parameter with a leading colon (:) pass back values to the calling DO command, however I think what it a little confusing is it appears that the variables names from what the calling DO command issues, and what the procedure (called by DO) returns don't necessarily have to be the same name. If someone could help shed some light on the following examples and explain what values are being either passed to/from or how their referenced by the issuing DO command that would be helpful.
It looks like the call to run the Get-Recursive-Reports-To (do Get-Recursive-Reports-To($Recursive_Line, $_POSITION_NBR, #_Global_Counter) is issuing these 3 variables as parameters to the procedure Get-Recursive-Reports-To, however as I look through the Get-Recursive-Reports-To procedure, I do not see any references of the variable $Recursive_Line within this procedure, so is the procedure actually using it or what is the purpose of including it? Similar question with $_val_Position_NBR, where is this variable getting it's value assigned from?
And then within the Get-Recursive-Reports-To procedure I see a parameter - :$var_Next_EMPLID that I believe is being passed back to the calling DO in the Run-Recursion procedure, but I can not figure out how/where it uses the value passed back to it...
begin-procedure Run-Recursion
let #_Global_Counter = 0
let $Recursive_Line = ''
let $Data_Line_EMPL = ''
let $Data_Line_EMPL = $_BUSINESS_UNIT || '|' || $_BUSINESS_UNIT_DESCR || '|' || '2019' || '|' ||
$_EMPLID || '|' || $_NAME || '|' || $_DEPTID || '|' || $_DEPT_DECSR || '|' || $_JOBCODE || '|'
do Get-Recursive-Reports-To($Recursive_Line, $_POSITION_NBR, #_Global_Counter)
let $Data_Line_EMPL = $Data_Line_EMPL || $Recursive_Line
do Write-Data-Line($Data_Line_EMPL)
end-procedure
begin-procedure Get-Recursive-Reports-To(:$val_Data_Line, $val_Current_Position_Nbr, #Recursion_Counter)
let #Recursion_Counter = #Recursion_Counter + 1
do Get-the-ReportsTo-for-the-Current-Position($val_Current_Position_Nbr, $Next_Position_Nbr, $Next_EMPLID)
do Check-For-Stop($Stop_Recursion, $val_Current_Position_Nbr, $Next_Position_Nbr, #Recursion_Counter)
if $Stop_Recursion = 'N'
let $val_Data_Line = $val_Data_Line || $Next_EMPLID || '|'
do Get-Recursive-Reports-To($val_Data_Line, $Next_Position_Nbr, #Recursion_Counter)
end-if
end-procedure
begin-procedure Get-the-ReportsTo-for-the-Current-Position($_val_Position_NBR, :$var_ReportsTo, :$var_Next_EMPLID)
let #local_counter = 0
begin-select
G.REPORTS_TO &G.Reports_to
let $var_ReportsTo= &G.Reports_To
from PS_POSITION_DATA G
WHERE G.POSITION_NBR = $_val_Position_NBR
and G.EFF_STATUS = 'A'
AND (G.EFFDT =
(SELECT MAX(G_ED.EFFDT) FROM PS_POSITION_DATA G_ED
WHERE G.POSITION_NBR = G_ED.POSITION_NBR
AND G_ED.EFFDT <= $_As_OF_Date))
end-select
begin-select
H.EMPLID &H.EMPLID
Z.NAME &Z.NAME
let $var_Next_EMPLID= &H.EMPLID
let #local_counter = #local_counter + 1
from PS_JOB H !, PS_EMPLOYEES Z
WHERE H.POSITION_NBR = $var_ReportsTo
and H.EMPL_STATUS not in ('D', 'R', 'T')
and (H.EFFDT =
(SELECT MAX(H_ED.EFFDT) FROM PS_JOB H_ED
WHERE H.EMPLID = H_ED.EMPLID
AND H.EMPL_RCD = H_ED.EMPL_RCD
AND H_ED.EFFDT <= $_As_Of_Date))
end-select
if #local_counter > 1
let $var_Next_EMPLID = $local_counter || ' ' || 'Employees in this Position'
end-if
if #local_counter = 0
let $var_Next_EMPLID = 'Position Vacant'
end-if
end-procedure

The FIRST call to Get-Recursive-Reports-To from the main procedure uses the $Recursive_Line variable. It doesn't have to be referenced again because it's internal to the procedure.
Once IN procedure Get-Recursive-Reports-To, the name of this variable becomes $val_Data_Line and is used to call back to the same procedure Get-Recursive-Reports-To which can change it. I guess ergo the name, Get-Recursive-Reports-To.
Recursion is a pain in the neck but it looks like it's being used to go up an organizational chain, finding the reports to employee id, then getting the name of that person.
Variable $var_Next_EMPLID is passed from Get-the-ReportsTo-for-the-Current-Position back to the caller as variable $Next_Emplid which is then used again to call Get-Recursive-Reports-To.
I can't tell when or why the Recursion stops since you didn't include the procedure "Check-For-Stop". Looks like there is some check - perhaps if the $Val_Current_Position_Nbr or $Next_Position_Nbr is blank or equal or something.

Related

Will this SQLite GROUP_CONCAT() order by work?

I did some tests on my db (SQLite 3.21.0) and its seems that my solution works, but I am afraid that its just luck and it may fail in some cases, I would like to know if my solution is valid...
It seems like GROUP_CONCAT() using primary key or if there is no primary key it will try to find numeric column and will use it for the concatenation order, so I wanted to check my theory and decided to use WITH clause to generate "new" table, this way I will set the order that GROUP_CONCAT will have to use
Here is what I did: (simplified query)
WITH data AS (SELECT cID,
pType || ':' || pID || ':' || pTotal || ':' ||
(CASE WHEN pDate IS NULL OR pDate = '' THEN '0' ELSE pDate END) fees
FROM Pay2015 AS A WHERE cID = A.cID AND pType > 1 ORDER BY pType)
SELECT A.cID, GROUP_CONCAT(data.fees, '|') fees FROM Pay2015 AS A
LEFT OUTER JOIN data ON A.cID = data.cID
WHERE A.cID = 98 AND pType = 0
WITH data will have table with first column as cID and it will be 98 for every row, it will be used in join, GROUP_CONCAT will also have to use it because its the only numeric column, and ORDER BY will set rows order that I need
Now the main query will execute GROUP_CONCAT on this new table (data) and because data.cID is the same for every row it will concatenate it as it
For comparison here is regular query that has the order problem:
SELECT cID, GROUP_CONCAT(pType || ':' || pID || ':' || pTotal || ':' ||
(CASE WHEN pDate IS NULL OR pDate = '' THEN '0' ELSE pDate END), '|') fees
FROM Pay2015 AS A WHERE cID = 98 AND pType > 1 ORDER BY pType
Pay2015 table data: (pID is primary key)
And this is the results when ordering by pType: (I use | to split by, removed all columns but pType for simplicity)
As you can see, the results order is the same as pID order in regular query
What do you think?
The implementation of group_concat() processes the rows in whatever order the database happens to read them.
The only way to enforce an order is to read the rows from a subquery (or view, or CTE) that actually has a specified order:
SELECT ... group_concat(...) ... FROM (SELECT ... ORDER BY ...);
Please note that that subquery must be the only data source in the FROM clause; if you are joining it with any other table, the join might cause the database to read rows in some other order. If you need to join, you must do it inside the subquery.

Are dynamic variables possible in SQR (not dynamic SQL)

I'm writing an SQR program to send a vendor a file containing employee info. The file contains a number of fields for which I've assigned the variables
$Code_1
$Code_2
$Code_3
....
Each code has an associated rate, and I've assigned similar variables ($Rate_1, $Rate_2, etc...)
I have a lookup table that has the columns EMPLID, JOBCODE, HOURLY_RT. I need to loop through for each employee to get all of the codes/rates. It's possible that some employees will have more/fewer than others. Is it possible to have "dynamic" variables, like we do for dynamic sql? For example, something like $Code_[$i]? The thought was to do something like this:
let #i = 1
begin-select
EC.JOBCODE
EC.HOURLY_RT
let $Code_[$i] = &EC.JOBCODE
let $Rate_[$i] = &EC.HOURLY_RT
let #i = #i + 1
FROM PS_ACME_LOOKUP EC
WHERE EC.EMPLID = &J.EMPLID
end-select
This doesn't work, but I wondering if there's a similar (or better) way to accomplish this. I suppose I could do an evaluate of the counter: when #i = 1, $Code_1 = ... when #i=2, $Code_2 =... But I'm hoping there's a better way.
Thanks
Edit - Just for added clarification, for each employee, a single line will be written to a file, with the fields for each of these values (populated or not) - so the line will have:
$EMPLID $Code_1 $Code_2 $Code_3.....$Rate_1 $Rate_2 $Rate_3
For further clarification the lookup table will have multiple rows for each employee, so the table might look like this:
EMPLID JOBCODE HOURLY_RT
0001 ABC 10.50
0001 DEF 9.75
0001 GHI 9.50
When I populate the variables, looping through the table, I would want $Code_1 = 'ABC', $Rate_1 = 10.50, $Code_2 = 'DEF', Rate_2 = 9.75 etc...
You can use arrays in SQR.
To set up the array:
Create-Array Name=WorkArray Size = 100
Field=Code
Field=Rate
Let #NumCodesForEmp = 0
To add data in your Select Block - also use on-break before and after procedures:
Begin-Select
EC.Emplid () on-break print=never before=Init-Emp After=Process-Emp
Let $Emplid = &EC.Emplid
add 1 to #NumCodesForEmp
Put &EC.JobCode &EC.Rate into WorkArray(#NumCodesForEmp) Code Rate
Write the before procedure to initialize:
Begin-Procedure Init-Emp
Let #NumCodesForEmp = 0
End-Procedure
When done with the employee:
Begin-Procedure Process-Emp
Let #I = 1
Let $OutputLine = $Emplid
While #I <= #NumCodesForEmp
Get $Code $Rate From WorkArray(#I) Code Rate
Let $OutputLine = $Outputline || ',' || $Code || ',' || $Rate
add 1 to #I
End-While
! This assumes that file number 10 is open
Write #10 from $OutputLine
End-Procedure
However, I think you could do everything without an array - use the before and after procedures as so:
Begin-Procedure Init-Emp
Let $OutputLine = &EC.Emplid
End-Procedure
Begin-Procedure Process-Emp
Write #10 from $OutputLine
End-Procedure
Then the Select Block would look like this:
Begin-Select
EC.Emplid () on-break print=never before=Init-Emp After=Process-Emp
EC.JobCode
EC.Rate
Let $OutputLine = $OutputLine || ',' || &EC.Jobcode || ',' || &EC.Rate
When using on-break, make sure you sort by emplid. This is much simpler if your need is just to write a file from data from a table.

Simple Procedure raise ORA-06502

There's the simplified version of my code who keep raise me ORA-06502:
declare
p_filter varchar2(300) := '2012';
p_value varchar2(300) := '12345.000';
w_new_value number(13,3) := null ;
w_count number(4) := null ;
BEGIN
SELECT count(*)
INTO w_count
FROM dual
where p_filter = p_filter;
--- more filters
if w_count != 0 then
w_new_value := p_value / w_count;
else
w_new_value := p_value;
end if;
-- do something
end;
/
Someone can give me a help?
DataBase Details
nls_language = italian
nls_territory = italy
nls_currency = �
nls_iso_currency = italy
nls_numeric_characters = ,.
nls_calendar = gregorian
nls_date_format = dd-mon-rr
nls_date_language = italian
nls_characterset = we8iso8859p15
nls_sort = west_european
nls_time_format = hh24:mi:ssxff
nls_timestamp_format = dd-mon-rr hh24:mi:ssxff
nls_time_tz_format = hh24:mi:ssxff tzr
nls_timestamp_tz_format = dd-mon-rr hh24:mi:ssxff tzr
nls_dual_currency = �
nls_nchar_characterset = al16utf16
nls_comp = binary
nls_length_semantics = byte
nls_nchar_conv_excp = false
First, this is always going return a value of 1.
SELECT count(*)
INTO w_count
FROM dual
It doesn't matter what the qualifier is.
Lastly, I just ran your simplified code example in Oracle 11R2 and it didn't throw an exception.
I added the following statement in place of your "do something" comment:
dbms_output.put_line('w_new_value: ' || w_new_value || '. w_count: ' || w_count);
The result was:
w_new_value: 12345. w_count: 1
So, I think you've simplified your example into oblivion. You need to provide something that actually shows the error.
Good luck.
I found myself the ansewer and i think is useful for other know.
The real problem of the script for my DB is the language.
The italian "version" of Oracle accept , instead of the . for translate the VARCHAR2 into NUMBER unlike the most of other country.
For make the code running well the solution is
w_new_value := replace(p_value,'.',',') / w_count;
This trick finally allows the DB use my VARCHAR2 param like a NUMBER

Parsing large text in xml format in Pl/Sql

I have a log table and the table has a varchar2 field which holds xml string like below:
In this example ClientName attribute did not change but Clientsurname changed.
I want to capture changed columns and their previous and new values.
The log table contains millions of records.
Which method can you suggest for parsing this data in an efficient way?
<r>
<columntag nameattribute="ClientName">
<new_value>Jeffrey</new_value>
<previous_value>Jeffrey</previous_value>
</columntag>
<columntag nameattribute="ClientSurname">
<new_value>Dijk</new_value>
<previous_value>Disk</previous_value>
</columntag>
</r>
Thank you
not 100% sure the below is what you are after but it should give you some ideas about how to go about it. Hope it is helpfull
CREATE TABLE "RM4SERV"."LOG_TEST" ( "TESTLOG" VARCHAR2(4000 BYTE))
Insert into RM4SERV.LOG_TEST (TESTLOG) values ('<r><columntag nameattribute="ClientName"><new_value>Jeffery</new_value><previous_value>Jeffery</previous_value> </columntag><columntag nameattribute="ClientSurname"><new_value>Dijk</new_value><previous_value>Disk</previous_value></columntag></r>');
Insert into RM4SERV.LOG_TEST (TESTLOG) values ('<r><columntag nameattribute="ClientName"><new_value>Jeffery</new_value><previous_value>Jeffery</previous_value> </columntag><columntag nameattribute="ClientSurname"><new_value>Disk</new_value><previous_value>Disk</previous_value></columntag></r>');
Insert into RM4SERV.LOG_TEST (TESTLOG) values ('<r><columntag nameattribute="ClientName"><new_value>Jeffery</new_value><previous_value>Jim</previous_value> </columntag><columntag nameattribute="ClientSurname"><new_value>Dijks</new_value><previous_value>Diskett</previous_value></columntag></r>');
declare
v_logrec varchar2(4000) := null;
v_recnum number := 0;
cursor c_logs is
select testlog from log_test;
cursor c_records is
select extractValue(x.column_value, '/columntag/#nameattribute') as column_name,
extractValue(x.column_value, '/columntag/new_value') as new_value,
extractValue(x.column_value, '/columntag/previous_value') as previous_value
from TABLE(XMLSequence(extract(xmltype.createxml(v_logrec), '//columntag'))) x
where extractValue(x.column_value, '/columntag/new_value') != extractValue(x.column_value, '/columntag/previous_value');
begin
for v_log in c_logs loop
v_logrec := v_log.testlog;
v_recnum := v_recnum + 1;
dbms_output.put_line(v_recnum);
for v_rec in c_records loop
SYS.dbms_output.put_line(v_rec.column_name || ' : *' || v_rec.new_value || '* : *' || v_rec.previous_value || '*');
end loop;
end loop;
end;
This would give you the below output (so Surname different in first record, nothing different in the second and both different in the third)...
1
ClientSurname : Dijk : Disk
2
3
ClientName : Jeffery : Jim
ClientSurname : Dijks : Diskett

Where and how to use NULLIF(X,Y) function of SQLITE?

I know that NULLIF(X,Y) function of SQLITE work equivalent to:
CASE
WHEN
X = Y
THEN
NULL
ELSE
X
END
and IFNULL(X,Y) function work equivalent to:
CASE
WHEN
X IS NULL
THEN
Y
ELSE
X
END
IFNULL(X,Y) function of SQLITE is used for replacing the NULL values of X to the Y but I can't understand the use of NULLIF(X,Y) function of SQLITE.
Please explain with examples, so it is more useful.
The IFNULL function is used when the database contains NULL values, but you want to handle those values as something else; for example:
SELECT Name, IFNULL(Age, 'unknown') AS Age FROM People
The NULLIF function is used when the database contains special values that are not NULL, but that you want to handle as NULL.
This is useful especially for aggregate functions. For example, to get the number of employees that get bonuses, use:
SELECT COUNT(NULLIF(Bonus, 0)) FROM Employees
This is the same as:
SELECT COUNT(*) FROM Employees WHERE Bonus != 0
In practice, NULLIF is not used as often as IFNULL.
I use NULLIF() when UPDATing or INSERTing rows containing NULLable fieds.
Basically with PHP :
$value1 = 'foo';
$value2 = '';
$sql = 'INSERT INTO table(field1, field2) '
. "VALUES(NULLIF('$value1', ''), NULLIF('$value2', ''))";
// => INSERT INTO table(field1, field2) VALUES(NULLIF('foo', ''), NULLIF('', ''))
// => INSERT INTO table(field1, field2) VALUES('foo', NULL)
It saves me doing things like :
$value1forSQL = ($value1 === '' || $value1 === NULL) ? 'NULL' : "'$value1'";
...
$sql = ...
. "VALUES($value1forSQL, ...)";

Resources