Monday, September 12, 2016

DELETE SQL performance

Delete Performance and ROWID 

We discovered slow performance on a DELETE statement that was using an EXISTS joining a related table and temporary table.  The problem is the entire subselect is executed returning a lot of  data to be evaluated only to delete a few rows from the source table.

To speed this up I added the original source table to the join and the driving table that gave me the smallest set of data.  In this case it was to join SAD_EXT_CRS_COM to EXT_COURSE followed by joining to the temporary table.  Now the the optimizer also help the subselect run extremely fast.  At this point we just need to convert the EXISTS to an IN statement and use the oracle pseudocolumn ROWID which is the fastest way to access a single row. The delete query now executes in no time.

Slow version using WHERE EXISTS

 DELETE FROM %Table(SAD_EXT_CRS_COM) c 
WHERE EXISTS ( SELECT NULL FROM %Table(EXT_COURSE) p 
                     , %Table(M_ET_PST_TMP) t 
               WHERE t.process_instance = %Bind(process_instance) 
                 AND t.oprid = %OperatorId 
                 AND p.emplid = t.emplid 
                 AND p.ext_org_id = t.ext_org_id 
                 AND p.ls_data_source = 'OET' 
                 AND p.ext_data_nbr = t.ext_data_nbr 
                 AND c.emplid = p.emplid 
                 AND c.ext_org_id = p.ext_org_id 
                 AND c.EXT_COURSE_NBR = p.EXT_COURSE_NBR ) 

Faster version using ROWID IN

DELETE FROM %Table(SAD_EXT_CRS_COM) c 
WHERE ROWID IN ( SELECT C.ROWID FROM %Table(SAD_EXT_CRS_COM) C 
                   JOIN %Table(EXT_COURSE) P ON C.EMPLID = P.EMPLID 
                    AND C.EXT_ORG_ID = P.EXT_ORG_ID 
                    AND C.EXT_COURSE_NBR = P.EXT_COURSE_NBR 
                   JOIN %Table(M_ET_PST_TMP) T ON P.EMPLID = T.EMPLID 
                    AND P.EXT_ORG_ID = T.EXT_ORG_ID 
                    AND P.EXT_DATA_NBR = T.EXT_DATA_NBR 
                   WHERE P.LS_DATA_SOURCE = 'OET' 
                     AND T.PROCESS_INSTANCE = %Bind(process_instance) 
                     AND T.OPRID = %OperatorId ) 

Friday, May 27, 2016

Peoplesoft Menu Listing or Search

SQL for Menu Searching

Here is a very handy way to either export a full listing of the PeopleSoft Menu or search for a specific menu item.
----  Menu Look-up
SELECT SRCH.PORTAL_LABEL LABEL
   , NVL(SRCH.PORTAL_URI_SEG2, ' ') COMPNOENT
   , SRCH.PORTAL_OBJNAME
  --L1.PORTAL_LABEL,  L2.PORTAL_LABEL,  L3.PORTAL_LABEL,  
  --L4.PORTAL_LABEL,  L5.PORTAL_LABEL,  L6.PORTAL_LABEL
  ,'Main Menu > ' || L1.PORTAL_LABEL || decode(L1.PORTAL_REFTYPE, 'F', ' > ')
  || L2.PORTAL_LABEL  || DECODE(L2.PORTAL_REFTYPE, 'F', ' > ')
  || L3.PORTAL_LABEL  || DECODE(L3.PORTAL_REFTYPE, 'F', ' > ')
  || L4.PORTAL_LABEL  || DECODE(L4.PORTAL_REFTYPE, 'F', ' > ')
  || L5.PORTAL_LABEL  || DECODE(L5.PORTAL_REFTYPE, 'F', ' > ')
  || L6.PORTAL_LABEL NAV_PATH,
  decode(SRCH.PORTAL_REFTYPE, 'F', ' ', 'C', SRCH.DESCR254) DESCR
FROM PSPRSMDEFN L1
  LEFT JOIN PSPRSMDEFN L2
  ON L2.PORTAL_NAME         =L1.PORTAL_NAME
  AND L2.PORTAL_PRNTOBJNAME = L1.PORTAL_OBJNAME
  LEFT JOIN PSPRSMDEFN L3
  ON L3.PORTAL_NAME         =L2.PORTAL_NAME
  AND L3.PORTAL_PRNTOBJNAME = L2.PORTAL_OBJNAME
  LEFT JOIN PSPRSMDEFN L4
  ON L4.PORTAL_NAME         =L3.PORTAL_NAME
  AND L4.PORTAL_PRNTOBJNAME = L3.PORTAL_OBJNAME
  LEFT JOIN PSPRSMDEFN L5
  ON L5.PORTAL_NAME         =L4.PORTAL_NAME
  AND L5.PORTAL_PRNTOBJNAME = L4.PORTAL_OBJNAME
  LEFT JOIN PSPRSMDEFN L6
  ON L6.PORTAL_NAME         =L5.PORTAL_NAME
  AND L6.PORTAL_PRNTOBJNAME = L5.PORTAL_OBJNAME
  LEFT JOIN PSPRSMDEFN SRCH
  ON SRCH.PORTAL_NAME       = 'EMPLOYEE'
  AND SRCH.PORTAL_OBJNAME   
    = NVL(L6.PORTAL_OBJNAME,NVL(L5.PORTAL_OBJNAME,NVL(L4.PORTAL_OBJNAME, 
           NVL(L3.PORTAL_OBJNAME,(NVL(L2.PORTAL_OBJNAME,NVL(L1.PORTAL_OBJNAME,' ')))))))
WHERE L1.PORTAL_NAME ='EMPLOYEE' AND L1.PORTAL_PRNTOBJNAME = 'PORTAL_ROOT_OBJECT'
--*************************************************************************************--
--  Search for Items
--*************************************************************************************--
  --AND SRCH.PORTAL_URI_SEG2    = 'MCM_ADV_SUMMARY'     -- Component
  --AND SRCH.PORTAL_LABEL       LIKE 'PeopleTools%'     -- Label
  --AND L1.PORTAL_OBJNAME       = 'PT_PEOPLETOOLS'      -- Folder/Ref Object Name
ORDER BY L1.PORTAL_REFTYPE DESC,  L1.PORTAL_LABEL,
  L2.PORTAL_REFTYPE DESC,  L2.PORTAL_LABEL,  L3.PORTAL_REFTYPE DESC,  L3.PORTAL_LABEL,
  L4.PORTAL_REFTYPE DESC,  L4.PORTAL_LABEL,  L5.PORTAL_REFTYPE DESC,  L5.PORTAL_LABEL,
  L6.PORTAL_REFTYPE DESC,  L6.PORTAL_LABEL ;

Tuesday, December 8, 2015

Component Verification SQL

Component Verification

This query was built as part of a script to review projects.  The objective was a quick way to confirm all the components in your project are following the company standards. Peoplesoft will often use a Binary bit mapping to turn several flags into a single decimal value.  In this case the column SHOWTBAR is a numeric SUM of 6 binary values 111111 = 1+2+4+8+16+32 = 63.  If you experiment with these you will notice some odd behavior like the Disable Toolbar when checked doesn't add 1 to the decimal but Disable Pagebar when checked adds the value 2.

Columns
  1. What is the default Search setting for the component
  2. What flags are set for "Multi Page Navigation"
  3. Disable Toolbar Flag
  4. Disable Pagebar Flag
  5. Disable Help URL
  6. Disable Copy URL
  7. Disable New Window
  8. Disable Customize Page
Binary Mapping for SHOWTBAR
  • +1 Disable Toolbar is Unchecked
  • +2 Disable Pagebar is Checked
  • +4 Help Link is Uncheked
  • +8 Copy URL Link is Unchecked
  • +16 New Window Link is Unchecked
  • +32 Customize Page Link is Unchecked



select PNLGRPNAME as Component,
CASE DFLTSRCHTYPE WHEN 0 THEN 'BASIC SEARCH *ERROR*' 
                  WHEN 1 THEN 'ADVANCED SEARCH' 
                  ELSE to_char(DFLTSRCHTYPE) END as SEARCH_TYPE,
CASE PNLNAVFLAGS  WHEN 0 THEN 'MULTI-PAGE NAV OFF' 
                  WHEN 1 THEN 'FOLDERS (TOP)' 
                  WHEN 2 THEN 'HYPERLINKS (BOTTOM)' 
                  WHEN 3 THEN 'FOLDER + LINKS NAV ON' 
                  ELSE TO_CHAR(PNLNAVFLAGS) END AS NAVIGATION_TYPE
,DECODE(BITAND(PSPNLGRPDEFN.SHOWTBAR,1),1,'N','Y') AS DISABLE_TOOLBAR
,DECODE(BITAND(PSPNLGRPDEFN.SHOWTBAR,2),2,'Y','N') AS DISABLE_PAGEBAR
,DECODE(BITAND(PSPNLGRPDEFN.SHOWTBAR,4),4,'N','Y') AS SHOW_HELP_URL
,DECODE(BITAND(PSPNLGRPDEFN.SHOWTBAR,8),8,'N','Y') AS SHOW_COPY_URL
,DECODE(BITAND(PSPNLGRPDEFN.SHOWTBAR,16),16,'N','Y') AS SHOW_NEW_WIN
,DECODE(BITAND(PSPNLGRPDEFN.SHOWTBAR,32),32,'N','Y') AS SHOW_CUSTOMIZE_PAGE
FROM PSPNLGRPDEFN WHERE PNLGRPNAME IN (SELECT PI.OBJECTVALUE1 FROM PSPROJECTITEM PI WHERE PI.PROJECTNAME = 'MY_PROD_NAME' AND PI.OBJECTTYPE=7);

Wednesday, August 26, 2015

String to Boolean


I needed a way to convert a string to a boolean. Specifically, I wanted to use a message in the message catalog to store a variable to allow us to easily change the flow in a specific page. This turned out to be much easier than I expected.

Using the following short hand returns a true if the string matches or false if it does not.

(&theString = "Y")

Here is how I used it with the message catalog text:
Local array of number &openMonths = CreateArray(1, 5, 9);
Local boolean &openSeason = (MsgGetText(21027, 8, "false") = "true");
...
If (&openMonths.Find(Month(&today)) > 0 And
      Day(&today) < 16) Or
      &openSeason Then
       ...
end-if;
     

Tuesday, April 28, 2015

Peoplesoft Styles Demo

View Peoplecode Styles

SQL


Run this SQL against your database and copy the output.
SELECT '<div class="' || STYLECLASSNAME || '">' || STYLECLASSNAME || '</div>'
FROM PSSTYLECLASS WHERE STYLESHEETNAME = 'PTSTYLEDEF'

Page

Temporary add an HTML area to any page and paste your SQL output into the value constant of your HTML area. View your page and you'll see a demo of every style for that Style sheet name in the query you ran.

Extra

If you want to create a more permanent page in your development environment that dynamically loads the HTML area with any style sheet you choose from the system you can do the following:

 Record

Create a new derived record (MY_RECORD) and add the fields
  • STYLESHEETNAME  (prompt table edit : EOPP_STSHEET_VW)
  • HTMLAREA

Page

Create a new page and add your both your derived record fields to it.  

Component

Create a component add your new page to it and add the following Peoplecode to the STYLESHEETNAME FieldChange event.

Local string &qry, &qoutput, &html;
Local SQL &sql;
Local array &AAny = CreateArrayAny();

&qry = "SELECT '<div class=' || STYLECLASSNAME || '>' || STYLECLASSNAME || '</div>'";
&qry = &qry | " FROM PSSTYLECLASS WHERE STYLESHEETNAME = :1 order by STYLECLASSNAME ";

&html = "";

&sql = CreateSQL(&qry, MY_RECORD.STYLESHEETNAME);
While &sql.Fetch(&AAny)
   &html = &html | &AAny [1];
End-While;

MY_RECORD.HTMLAREA.Value = &html;

Register your component to the menu and load your new page.

Thursday, April 23, 2015

PeopleCode Reference Links

Reference Links

Google is one of a programmers best friends and after a while you build up a small collection of great resources.  Here are a couple of my favorite places to find People code solutions and examples:

PeopleCode Language Reference 8.53

  • Built-in Functions
  • Meta-SQL
  • System Variables
  • Meta-HTML

PeopleCode API Reference 8.53

Every class in a great Tree view that includes a quick link to the class details such as:
  • Understanding
  • Using
  • Declaring
  • Built-in Functions
  • Methods
  • Properties
  • etc....

PeopleCode Developers Guild 8.43


  • Operators
  • Data Types
  • Expressions
  • Variables
  • Editors
  • Events
  • Debugging
  • Short Cuts

PeopleCode Built-in Functions 8.50

Listing of every built in function in one place sorted by category.  
With Frames version.

Toolbox.com 

Message board with many users who post solutions.and is most often referenced when doing a Peoplecode google search.


I have a few other links in my links and references page.

Wednesday, April 22, 2015

Custom PeopleSoft Exceptions

Application Package

I created an application package with a couple classes but I soon realized I needed different exceptions for different behaviours.  This is easy in JAVA you just build a custom exception that extends the exception class.  I figured there must be similar way to do this in poeplesoft but couldn't find an example in peoplebooks they seem to always just use the "CreateException" method.  This example will give you the ability to throw and catch different custom exceptions.


Application Package

MY_PERSON_SYNC
  • MY_PersonBuilder
  • MY_PersonSync
  • MYPersonException

Exception Class

This first step is create your Exception.  I put mine as a different class in the same package that will be throwing it.  This class will extend exception and only have the basic constructor method in it.  This constructor will create the super class Exception using the common peoplesoft method CreateException.  I accepted only the message as a string but you could accept your own custom message numbers as well if you like.


class MYPersonException extends Exception
   method MYPersonException(&MSG As string);
end-class;

method MYPersonException
   /+ &MSG as String +/
   %Super = CreateException(0, 0, &MSG);
end-method;

Person Builder Class
Next in my class MY_PersonBuilder I need I have 2 different errors.  The first is one that I will issue a standard exception that should really stop the process all together.  The second is for when the problem could only be related to a single individual and if multiple transactions are being processed only reject this one transaction.


import MY_PERSON_SYNC:MYPersonException;
...
/* If it is an SQL error you might throw standard Exception */
&recResult = &rec.Update();
If &recResult = false Then
    throw CreateException(0, 0, "update to table **NOT** successful for %1", &rec.ID.Value);
End-If;

/* If my error was specific to a single transaction I would throw my custom Exception */
throw create MY_PERSON_SYNC:MYPersonException("Person missing a Xref value. ");

Person Sync Class
The class MY_PersonSync actually implements the class PS_PT:Integration:INotificationHandler so it has a OnNotify method where the try catch will exist. This is the location that determines if an error should be thown stopping the entire process or just drop a single transaction and continue to process the remaining ones.

import MY_PERSON_SYNC:MYPersonException;
...
   try
      &personBuilder.AddPersonAudit();
      &psXrefId = &personBuilder.AddXrefPS();
      &newid = &personBuilder.GenerateNewID("", False);
      &EMSTXrefId = &personBuilder.AddXrefEMST();
      &alias = &personBuilder.AddIdAlias(&newid);
   catch MY_PERSON_SYNC:MYPersonException &exPerson
      MessageBox(0, "", 0, 0, &exPerson.ToString());
   catch Exception &exError
      Error &exError.ToString();
   end-try