0

I have created a CV just like this "Former Member" on this blog https://blogs.sap.com/2017/05/18/factory-calendar-transpose-in-sap-hana-studio-step-by-step/

It works like a charm!

My next requirement is to take that DATE_SAP for each record and determine the Accounting Month End; Yes I understand that that value will be the same for up to 30 days.

I have a function that works and gives me the accounting month end date but I cant seem to determine how to make it work with the HANA CV view I created.

Huge thanks in advance!

Here is the function

CREATE FUNCTION "MY_SCHEMA"."FN_DTACCTMONTHEND"
(
    -- Add the parameters for the function here
dtexp date
)
RETURNS dtwkend date

LANGUAGE SQLSCRIPT
SQL SECURITY INVOKER AS

BEGIN
    -- Declare the return variable here
    DECLARE dttemp date;
    declare cnt int;
    declare c_dtwkend date;
    
Select ADD_DAYS(to_date('19000107', 'YYYYMMDD'),(FLOOR((days_between(to_date('19000107', 'YYYYMMDD'),:dtexp) / 7)) * 7) + 7) into c_dtwkend from dummy;
cnt := 0;

    while :cnt < 6 DO
    
        dttemp := add_days(:c_dtwkend,7);
        if MONTHNAME(:dttemp) = MONTHNAME(:c_dtwkend) then
             c_dtwkend := dttemp;
        else
             dtwkend := to_date(c_dtwkend);
             return;
        end if;         
        cnt := :cnt + 1;
    end while;

    -- Return the result of the function
    dtwkend := to_date(c_dtwkend);
    return;

END;

Here is the table function:

CREATE FUNCTION "SCHEMA_NAME"."SCHEMA_NAME::FN_DTACCTMONTHEND_TEST" (DATE_SAP date ) 
    RETURNS TABLE (DTWKEND date)
    LANGUAGE SQLSCRIPT
    SQL SECURITY INVOKER AS
BEGIN

DECLARE dttemp date;
    DECLARE cnt int;
    DECLARE c_dtwkend date;
    DECLARE dtwkend date;


Select ADD_DAYS(to_date('19000107', 'YYYYMMDD'),(FLOOR((days_between(to_date('19000107', 'YYYYMMDD'),:DATE_SAP) / 7)) * 7) + 7) into c_dtwkend
from dummy;

cnt := 0;

    while :cnt < 6 DO
    
        dttemp := add_days(:c_dtwkend,7);
        if MONTHNAME(:dttemp) = MONTHNAME(:c_dtwkend) then
             c_dtwkend := dttemp;
        else
             dtwkend := to_date(c_dtwkend);
             return;
        end if;         
        cnt := :cnt + 1;
    end while;

    -- Return the result of the function

    dtwkend :=  to_date(c_dtwkend);
    RETURN
    SELECT dtwkend from dummy;

    
END;

Error received is SAP DBTech JDBC: [2]: general error: RETURN statement with expression should be defined for table function

  • Do you need to use columns from CV in the function? If you are on XSA, you can integrate TF into the CV. Otherwise you have to calculate on the side and join or on top of CV (having CV as an input parameter for your Table Function). – wounky Oct 13 '20 at 11:28
  • Yes I would like to use the DATE_SAP that is in the output from the CV I used to make the factory calendar. I have created a TF in Hana but everything I see is that you can do certain functions like insert or loop in a TF which is needed to find the accounting month end for each date in the CV – SSGSSGohanDBZ Oct 14 '20 at 17:14
  • Then you can select relevant columns in TF from CV. Calculate in TF, add an output to further views. Would be much easier in 2.0 as you could do it all within 1 CV having CV node as an input and output of TF. – wounky Oct 15 '20 at 11:39
  • Sorry I meant to say you CAN'T do functions like "insert" or loop in a TF which is needed if you looked at the function in the question above. – SSGSSGohanDBZ Oct 20 '20 at 14:05
  • You can use loops and select into in TF. You cannot use ddl or dml operations (INSERT, UPDATE, DELETE). – wounky Oct 21 '20 at 18:37
  • Can you explain the logic of your function a bit? The arbitrary reference date `19000107` and the try/fail adding of 6 weeks to see if this gets the date into the next month are not quite clear. Have you considered using a function like `LAST_DAY`? – Lars Br. Oct 26 '20 at 06:10
  • LAST_DAY will not work as that is not the accounting month end --- the try and fail looks for the last sunday of the month and its done 6 times as some month have 5 weeks in them; – SSGSSGohanDBZ Oct 29 '20 at 00:29
  • @wounky how would you go about making the function above into a TF as it errors on me with the INTO c_dtwkend I move the RETURN to the very end where I select dtwkend from dummy; then i get this error SAP DBTech JDBC: [2]: general error: RETURN statement with expression should be defined for table function i have added the TF code above – SSGSSGohanDBZ Oct 29 '20 at 00:31

2 Answers2

1

Ok, based on the comment, the requirement is to find the last Sunday of a given month.

While the OP believes that to compute this a loop is required and that the LAST_DAY function is of no use here, the opposite is true.

The last Sunday of a month can easily be computed with the help of LAST_DAY and WEEKDAY functions:

SELECT 
     current_date
   , LAST_DAY(current_date)
   , WEEKDAY (LAST_DAY(current_date)) AS weekday_of_last_day
   --
   , ADD_DAYS (LAST_DAY(current_date)
        ,  -( (WEEKDAY(LAST_DAY(current_date)) + 1)           -- how many days back to the last Sunday?
              * sign (6 - weekday(last_day(current_date)))    -- set to 0 if the day is a SUNDAY already as THIS is the last Sunday
            )
          )    AS last_sunday_of_month
FROM 
    dummy;


CURRENT_DATE|LAST_DAY(CURRENT_DATE)|WEEKDAY_OF_LAST_DAY|LAST_SUNDAY_OF_MONTH|
------------|----------------------|-------------------|--------------------|
  2020-11-01|            2020-11-30|                  0|          2020-11-29|

Note: the selected columns CURRENT_DATE, LAST_DAY(current_date), and WEEKDAY_OF_LAST_DAY are included for demonstration purposes only. They are not required for the column expression LAST_SUNDAY_OF_MONTH to work.


In this example, I use the CURRENT_DATE but one can plug in any date - the computation works the same.

First, the WEEKDAY of the LAST_DAY of the anchor month is computed. WEEKDAY yields a number between 0 and 6 representing the weekdays starting with MONDAY, i.e. SUNDAY = 6.

If the current weekday is already 6 (= Sunday) we don't need to subtract any days to get to the last Sunday of the month. This is achieved by multiplying the offset by the SIGN of the difference between 6 (the weekday number for Sundays) and the weekday of the current date. If this difference is positive (i.e. between 1 and 6), the offset is multiplied by 1 and if the difference is zero, the offset is multiplied by 0.

This means, ADD_DAYS only "goes back to the last Sunday" when the current date is not already a Sunday.

And that's it: no loops and no SQLScript code required. This SQL expression can be used in plain SQL or in Calculation Views.

Lars Br.
  • 9,949
  • 2
  • 15
  • 29
  • I will definitely try to implement this. Thank you very much!!! – SSGSSGohanDBZ Oct 31 '20 at 13:04
  • what if the current_date is already past the last sunday of the month -- if it is it would need to go to the next month's last sunday as that would be correct sunday for that record to group by the correct accounting month – SSGSSGohanDBZ Dec 03 '20 at 18:29
  • It would’ve good if you’d explained what your requirement specifically is. The switching to the next „accounting period“ is not a universally known standard concept. For this to work you can add a different code path via `CASE` where you check for the condition and return the last Sunday of this month or of the next month. Still no procedure required. – Lars Br. Dec 03 '20 at 19:18
0

RETURN is reserved for the table function. Use BREAK for the while loop instead. Please find an example below.

DROP FUNCTION "SCHEMA"."FN_DTACCTMONTHEND_TEST";

CREATE FUNCTION "SCHEMA"."FN_DTACCTMONTHEND_TEST" (DATE_SAP date ) 
    RETURNS TABLE (DTWKEND date)
    LANGUAGE SQLSCRIPT
    SQL SECURITY INVOKER AS
BEGIN

    DECLARE dttemp date;
    DECLARE cnt int := 0;
    DECLARE c_dtwkend date;
    DECLARE dtwkend date := ADD_DAYS(to_date('19000107', 'YYYYMMDD'),(FLOOR((days_between(to_date('19000107', 'YYYYMMDD'),:DATE_SAP) / 7)) * 7) + 7);


    while :cnt < 6 DO
    
        dttemp := add_days(:c_dtwkend,7);
        if 
            MONTHNAME(:dttemp) = MONTHNAME(:c_dtwkend) 
        then
             c_dtwkend := dttemp;
        else
             dtwkend := to_date(c_dtwkend);
             BREAK;
        end if;         
        
        cnt := :cnt + 1;
    
    end while;


    -- Return the result of the function
    RETURN
        SELECT 
            to_date(c_dtwkend) AS DTWKEND
        FROM
            dummy;

    
END;
wounky
  • 97
  • 1
  • 12
  • In SQLScript the `SELECT ... INTO... FROM DUMMY` should *not* be used. Instead, simply *assign* the value to the variable. E.g. `c_dtwkend := ADD_DAYS(to_date('19000107', 'YYYYMMDD'),(FLOOR((days_between(to_date('19000107', 'YYYYMMDD'),:DATE_SAP) / 7)) * 7) + 7)` This saves the whole SQL parsing/optimising effort for this assignment. – Lars Br. Oct 31 '20 at 04:21
  • Thanks Lars, unfortunately it is not supported on SPS12 – wounky Nov 02 '20 at 09:05
  • What exactly? The direct assignment? That definitely is supported with SPS12. Are you using the latest patch for SPS12? – Lars Br. Nov 02 '20 at 09:08
  • 1
    My bad, I tried your syntax again. I have made a mistake in my previous attempt! Sorry for the confusion and thanks for your response! – wounky Nov 02 '20 at 09:24