AMDP Code To Data Book
AMDP Code To Data Book
ABAP to AMDP
Hope that the book covers a wide range of topics and provides a good kick-
start on the code-to-data paradigm. This is a free book and you can
forward it to your friends and colleagues. Please feel free to send any
comments or feedback to me using the URL
https://docs.google.com/forms/d/e/1FAIpQLScPa4BtiuNinMFI_6ScsNyzay
7DRhaK0VniHRzsJbIhWpXpeg/viewform .
Introduction
From release NW7.4, SAP provides ABAP support for optimizing ABAP
applications running on SAP HANA. Transparent optimizations are
nothing but improvements in the ABAP stack so that your ABAP code can
benefit HANA advantages directly without any code adjustments. As an
application developer, you cannot see transparent optimizations. For
example, SAP improved the implementation of the SELECT FOR ENTRIES
statement. SAP uses a new data exchange protocol between the ABAP stack
and the database release 7.4 onwards. Using advanced OpenSQL and SAP
HANA native features, you can improve performance. The code-to-data
options can correlate to the potential gain in performance but they may also
increase the complexity of the code.
Code-To-Data
Pre-ABAP 7.4, SAP provides very limited support for the code-pushdown
approach. SAP introduces the bottom-up approach on ABAP 7.4 SP02 and
the approach enables to consumption of HANA views and procedures in
the ABAP. With the release of ABAP7.4 SP05, SAP enables code pushdown
with ABAP for SAP HANA, the top-down approach, enabling the
developer to continue to work with the ABAP environment. The developer
can manage the modeling in the ABAP (application layer) and
corresponding objects are created in the HANA database.
SAP recommends the top-down approach. A CDS (Core Data Service)
View is an ABAP dictionary object that has two layers semantic layer and
an SQL view. CDS is a powerful tool to design virtual data models. You
need to define the CDS view in Eclipse (it has no classic editor in SAP GUI).
The CDS view carries more semantics than its SQL view. Note SQL view is
visible in the ABAP dictionary. AMDP (ABAP Managed Database
procedures) is a simple ABAP class method that contains native HANA
SQLScript. AMDP methods deploy database procedures and functions.
The CDS plays a vital role in the code pushdown concept. The main focus
of the books is how to use the AMDP methods in the code pushdown
concept. By implementing AMDP methods, you can see a big paradigm
shift in your programming. For simple SQL select statements, you can
ABAP OpenSQL statements instead of the AMDP method. The OpenSQL
statements are getting powerful and you can handle any complex query.
When you are moving the SQL statements in the database, you can see a
significant performance improvement. The simple rule is that do not use
simple select statements in the AMDP. The main purpose of the AMDP is
to handle multiple and complex queries.
Open SQL
WITH
+vendors21 ( SELECT lifnr, name1 FROM LFA1 where ktokk = ‘Z021’ )
SELECT ekko.ebeln, ekko.bukrs, ekko.bstyp, ekko.bsart, ekko.lifnr,
+vendors21.name1
FROM ekko
INNER JOIN ON ekko.lifnr = +vendors21.lifnr
INTO TABLE @DATA( lt_tab ).
CTE is highly integrated with the CDS so that you can publish CDS
associations for subsequent queries of a WITH statement. CTE is always
challenging your code pushdown. You can still implement complex queries
directly in ABAP OpenSQL instead of the AMDP method. You can use
AMDP for any SQL statement or function which is not supported directly
by OpenSQL.
The following table lists all OpenSQL features and their availability.
OpenSQL Features Version
The right-hand side of join can be a join (other than table or From NW 7.4
view)
Non-Equi operators on ‘ON’ conditions (not just equal) From NW 7.4
WHERE condition of a left outer join can use any other From NW 7.4
operator (not just equal)
RIGHT OUTER JOIN From NW 7.4
ON Condition with IS or IS NOT NULL From NW 7.5
Support of UNION statement From NW 7.5
Common Table Expression From NW 7.51
Numeric Functions – ABS, CEIL, FLOOR, DIV, MOD From NW 7.4
Numeric Function – ROUND From NW 7.5
CAST function From NW 7.4
String operator && (allows more than two operands) From NW 7.4
String Functions – CONCAT, LPAD, LENGTH, LTRIM, From NW 7.5
REPLACE, RIGHT, RTRIM, SUBSTRING. Note that CONCAT
allows only two operands
Common Table Expression From NW 7.51
CROSS Join (Cartesian product) From NW 7.51
OpenSQL vs SQLScript
You can create proxy objects to consume SAP HANA objects. There are two
important proxy objects viz., the external views and database procedure
proxies. ADT eclipse is required to create an external view and a database
procedure proxy. The external view is a data dictionary view which refers
to the HANA database view. The external views are just proxies for the
HANA views. You can view only the field names only not any join
conditions. Note that the SAP HANA view interface requires manual
synchronization. You need to handle the syntax error (missing the HANA
view) and runtime error when does not match the external view. You
cannot trace the selection criteria on the ABAP trace.
Before ABAP 7.4 SP05, the AMDP is not supported and SAP provides the
option to create a database procedure proxy that refers to the HANA
database procedure. Note that all parameters of the procedure proxy are
mandatory. Like the external view, the database procedure proxy requires
manual synchronization and possible potential runtime errors. And there is
no extensibility for the proxy procedure like for AMDPs.
In a relational database, the operations act always on a complete set of
records. Some applications or requirements cannot work always effectively
with the entire result set as a unit. The database cursor enables you to
work with one row or a small block of rows at a time. Like ABAP,
SQLScript supports the database cursor. In SQLScript, you can
parameterize the cursor with input parameters. SAP recommends avoiding
cursors in SQLScript code.
ABAP database connectivity (ADBC) provides ABAP APIs for native SQL
calls. You can directly access the native SQL statements. The object-based
flexible ADBC technique supports error handling. One of the main ADBC
classes, CL_SQL_STATEMENT enables executing SQL (HANA SQLScript)
directly. You can see the implementation of the JSON document store
example. ADBC is purely based on dynamic SQL statements and SAP
recommends using ADBC only for short native SQL calls otherwise, use the
AMDP.
AMDP Basics
SAP released the AMDP functionality in Release 7.40 and SP05 allowed
developers to create the AMDP procedure. AMDP is used as a top-down
approach to create and call stored procedures on the SAP HANA database.
Using the AMDP technique, you can perform lifecycle management of
database procedures/functions on the ABAP application server. AMDP
technique enables you to edit and manage the procedures, syntax check,
and automatic deployment of stored procedures.
The chapter intends to explain the AMDP basics such as data types, date
and string functions, array processing, and session management. The string
functions include concatenation, regular expression, map function, and
Unicode normalization. SQLScript built-in libraries are discussed with the
scenarios. Also, the chapter discusses the full-text search features. The
SQLScript error handling process and AMDP enhancements are discussed
in detail. At the end of the chapter, you can provide the basic functions of
the AMDP.
You cannot develop the AMDP class using the ABAP workbench but you
can view the AMDP class in SAP GUI. You need to use the ABAP
development tool (ADT) Eclipse for AMDP development. Eclipse provides
a brand-new ABAP development experience on top of the Eclipse platform.
After long years of SAP GUI usage, you may feel difficulty working with
the adaption of Eclipse. With Eclipse, you benefit from the usability, speed,
and flexibility of Eclipse along with proven ABAP workbench features.
Eclipse IDE (integrated development environment) is a modern IDE and is a
widely used development environment. Some advantages of the Eclipse
are:
• Eclipse framework supports multiple programming languages
programming.
• Connect to multiple ABAP systems so you can work parallelly and switch
between systems easily.
• Eclipse provides session failover (that reduces the impact of lost
connections) compare with the timeout of the SAP GUI issue.
• You can work offline. You can cache your local development and it can be
synchronized when there is a connection with the server. So, you do not
lose your development work.
• Quick Fix option (Ctrl-1) that enables the automated adaptation of custom
code
• Navigation history (Alt+Left or Right) enables you to jump between
different objects.
• The latest ABAP debugger is highly integrated with Eclipse debug
framework.
Before your AMDP development, you can verify whether your current
database supports AMDP procedures using the class method
CL_ABAP_DBFEATURES=>USE_FEATURES. If the method does not
return true, then you cannot develop AMDP code in your system.
IF NOT cl_abap_dbfeatures=>use_features(
EXPORTING
requested_features =
VALUE #( ( cl_abap_dbfeatures=>call_amdp_method ) ) ).
cl_demo_output=>display(
`Current database system does not support AMDP procedures` ).
RETURN.
ENDIF.
PUBLIC SECTION.
interfaces IF_AMDP_MARKER_HDB .
methods AMDP_INVENTORY
importing
value(IV_PAYMENT_DATE) type D
value(IV_CLNT) type SY-MANDT
exporting
value(ET_INVOICE_HEADER) type TT_INVOICE_HEADER
raising
CX_AMDP_ERROR.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
You can invoke the AMDP method like any other ABAP method. The
AMDP methods are executed like static methods irrespective of method
definition (instance or static). When you execute the AMDP method, the
ABAP kernel calls corresponding database procedures. The method body
is implemented using SQLScript code. SAP strongly recommends using the
character set 7-bit-ASCII when you are implementing an AMDP method.
AMDP method creates a database procedure with a mark BY DATABASE
PROCEDURE in the method implementation. Note that THE AMDP
framework does not require any additional SAP HANA database user to
create database procedures. It uses your ABAP user. THE AMDP creates a
database function with the mark BY DATABASE FUNCTION. You need to
understand the user scalar and table-defined functions. The scalar user-
defined function (UDF) returns the scalar value(s). Table user-defined
returns table of contents. The scalar UDF can return multiple scalar values
but the table UDF can return only one table-typed variable.
The AMDP function uses RETURNING parameter instead of EXPORTING
parameters. The main advantage of the table function is that you can use it
as a data source in other AMDP methods. Also, you can use it in the map
merge and map-reduce function to process the table UDF with parallel
processing. The map-merge and map-reduce are discussed in the parallel
processing section.
Debugging
DUMMY Table
You can see a common statement FROM DUMMY in the SQLScript. The
DUMMY is a public synonym table and it has one row and one column.
When you use the SELECT FROM DUMMY, it always returns a single
record. You can use the DUMMY to mock the data or populate the table-
typed variable. The DUMMY is not considered a data source so you cannot
define the DUMMY as the data source in USING clause of the AMDP
method. The following code snippet is just to demonstrate the AMDP code
to populate the internal table type using the DUMMY. Note that SQLScript
does not support the range field in the IN operator.
ABAP Code:
DATA lt_tab TYPE RANGE OF i.
lt_tab = VALUE #( sign = 'I' option = 'BT' ( low = 1 high = 10 )
( low = 21 high = 30 )
( low = 41 high = 50 )
option = 'GE' ( low = 61 ) ).
AMDP Code:
lt_tab = SELECT ‘I’ AS sign, ‘BT’ as option, cast(1 as int) as low, cast(10 as int) as high
from DUMMY
UNION SELECT ‘I’ AS sign, ‘BT’ as option, cast(21 as int) as low, cast(30 as int) as
high from DUMMY
UNION SELECT ‘I’ AS sign, ‘BT’ as option, cast(41 as int) as low, cast(50 as int) as
high from DUMMY
UNION SELECT ‘I’ AS sign, ‘GE’ as option, cast(61 as int) as low, null as high from
DUMMY;
--or
Data Type
The data type defines the characteristics of the data value. SQLScript
supports both scalar and composite data types. The scalar type allows you
to store exactly one value whereas the composite data type refers to table-
type data types. In SQLScript, there is a special value NULL included in
every data type. The NULL indicates the absence of the data value.
SQLScripts follows the list of supported data types.
You can see the list of ABAP data types and their equivalent SQLScript data
types.
HANA Data Type ABAP Data Type Description
SMALLINT INT2 2-Byte integer
INTEGER INT4 4-Byte integer
DECIMAL DEC Packed number
SMALLDECIMAL DEC Packed number
FLOAT FLTP Binary floating point number
VARCHAR CHAR Character string
NVARCHAR CHAR Unicode character string
VARBINARY RAW Byte string
BLOB RAWSTRING Byte string
CLOB STRING Character string
NCLOB STRING Unicode character string
SQLScript allows you to create table type explicitly via CREATE TYPE. But,
the AMDP method does not support the CREATE TYPE statement.
SQLScript provides a CAST function (supported in the OpenSQL from
NW7.4) that enables you to convert the value of an expression into a
supplied data type. Also, there are multiple HANA conversion functions to
support both implicit and explicit data type conversions. The few data type
conversion functions include TO_DATE, TO_CHAR, TO_INT,
TO_NUMBER, TO_DECIMAL, etc.
Variables
You can define local variables using the DECLARE statement. By default,
the scalar variables are initialized with NULL. You can assign a default
value using the assignment operator ‘=’ or the keyword DEFAULT.
SQLScript provides you the option to initialize with the declaration.
Variable names are case insensitive and they support alphanumeric
characters, #, $, and _. Table variables are variables with a reference to the
tabular data structure. You can directly assign the table variables from the
select statement, TABLE keyword, or array function (that converts into table
type). You can make a variable into a constant by adding the CONSTANT
keyword after the variable (applicable for both scalar and table variables).
Let's start with a simple scenario that you are required to append lines of an
internal table into another internal table with the same data type. You can
see both the ABAP code and the AMDP code.
ABAP Code:
APPEND LINES OF lt_tab1 TO lt_tab2.
AMDP Code:
:lt_tab1 = :lt_tab1 UNION :lt_tab2;
-- or you can use SELECT statement
lt_tab1 = SELECT * from :lt_tab1
UNION
SELECT * FROM :lt_tab2;
The scope of the variable is based on where they are defined. You can
define the local variables inside BEGIN-END/LOOP/WHILE/FOR/IF-
ELSE control structures. The scope of the variables is only with the control
structures.
DECLARE lv_a INT = 1;
BEGIN
DECLARE lv_a INT = 2; -- local variable a with value 2
BEGIN
DECLARE lv_a INT;
Lv_a = 3; -- value 3 and loses its scope once it is out of the block
END;
ev_out = lv_a; -- value is 2
END;
ev_out1 = lv_a; -- value is 1
Note that the UNION ALL statement does not remove duplicate records.
Whereas, the UNION statement implies the distinct semantic and removes
the duplicate record. Note that UNION and UNION ALL require the
records in the same order and a compatible type when you try to union two
different SELECT statements. The code snippet has the assumption that
both internal tables are the same data-typed.
In ABAP, you can declare the variables using the DATA statement. The
SQLScript uses the DECLARE statement to do the same. The scope of the
SQL Script variable is global when you declared the variable in the main
body of the procedure or function. Also, you can declare a local variable in
the anonymous block and the scope of the variable is within the block only.
You can define an anonymous block of code within BEGIN and END.
Anonymous Block
You can use the anonymous block like the local procedure function style by
passing the parameters. The anonymous block is an executable DML
statement with imperative or declarative statements. See the sample code
snippet.
DO (IN iv_input1 NVARCHAR(24)=> 'A',OUT lt_out TABLE (col1 INT, col2
INT ) => ?)
BEGIN
T1 = SELECT I, 10 AS J FROM TAB where z = :lv_input1;
T2 = SELECT I, 20 AS K FROM TAB where z = : lv_input1;
T3 = SELECT J, K FROM :T1 as a, :T2 as b WHERE a.I = b.I;
lt_out = SELECT * FROM :T3;
END;
You can see the table Dummy frequently in SQLScript. The dummy is a
row-based table and it can be used to return a single row. The following
example demonstrates to get the current user into the variable lv_user.
Also, you can return the value as table type with the selected information.
This is very useful to initialize the table record. The following sample code
creates a two-column record into the table lt_record variable.
The internal table lt_record will have a single record with two fields VBELN
and ZCOL1.
GUID
A Globally Unique Identifier (GUID) is a system-generated unique key to
identify the business objects. The GUID is highly used in CRM, SRM, and
new business suite products. used as the key for unique identification. The
GUID is a RAW data type with three formats GUID16, GUID22, and
GUID32. You can use the function module GUID_CREATE to generate
GUID16, GUID22, or GUID32. In SQLScript, you can generate using the
NEWUID or SYSUUID function. The NEWUID function returns
VARBINARY(16). The SYSUUID function returns VARBINARY(32). See
the following code snippet to generate GUID.
Date Function
Function Comments
TO_DATS Convert the date into ABAP date string
YYYYMMDD. Use this method to convert into
ABAP date format (sy-datum)
TO_DATE Convert the date into a required output format
ADD_DAYS Add days to the ABAP date
ADD_MONTHS Add months to the ABAP date
ADD_YEARS Add Years to ABAP date
ADD_SECONDS Add seconds to date/time
NANO100_BETWEEN Returns the difference between two dates in
nanoseconds (0.1 microseconds).
EXTRACT Get date components like a year, month, and day
Scenario: You need to calculate the last day of the month which is 2 months
from today.
You can extract the year, month, day, hour, minute, and seconds from the
date variable.
Scenario: The fiscal year is a financial year used by the company. For
example, the US federal uses the fiscal year variant (V9) that starts the
period in October and ends in September. In ABAP, you can get the fiscal
year and period using the function module
DATE_TO_PERIOD_CONVERT. See the following code snippet to get the
fiscal year and period for a given date and fiscal year variant.
Scenario: Consider the asset aging report to list out all assets with age
(based on the reference date) and age category. You can see a continuation
of this summarized aging report in the window function section. The
sample code snippet demonstrates the date function to calculate the age and
age category. The base table is ANLA and it is obsolete in the S/4 HANA.
AMDP Code:
METHOD asset_aging
BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT OPTIONS
READ-ONLY
USING anla anlc anlz.
et_out =
SELECT a.bukrs as bukrs,
DAYS_BETWEEN( a.zugdt, :iv_bdate ) as age,
( CASE WHEN DAYS_BETWEEN( a.zugdt, :iv_bdate ) > 365 THEN 'Over
365 days'
WHEN DAYS_BETWEEN( a.zugdt, :iv_bdate ) BETWEEN 181 AND 364
THEN 'Bet. 180 & 365'
WHEN DAYS_BETWEEN( a.zugdt, :iv_bdate ) BETWEEN 91 AND 180
THEN 'Bet. 90 & 180'
ELSE 'Within 90 days' END ) as age_cat,
a.posnr as posnr, z.gsber as gsber, a.anln1 as anln1, a.anln2 as anln2, z.kostl
as kostl,
a.zugdt, c.answl as acqval, 1 as count,
c.kansw, c.answl, c.gjahr
FROM anla AS a
JOIN anlc AS c
ON a.bukrs = c.bukrs AND
a.anln1 = c.anln1 AND
a.anln2 = c.anln2
JOIN anlz AS z
ON a.bukrs = z.bukrs AND
a.anln1 = z.anln1 AND
a.anln2 = z.anln2 ;
et_out = APPLY_FILTER( :et_out, :iv_where ); -- Filter the results based on
where clause
ENDMETHOD.
Time Zone
In ABAP, you can use the CONVERT statement to convert the date or time
between the local time zone and the specified time zone. The time zones
must be defined in the table TTZZ. The current time zone is stored in the
table TTZCU and you can get the current time zone using the FM
GET_SYSTEM_TIMEZONE. The following ABAP code snippet converts
the timestamp to EST time zone.
String Function
SQLScript string functions are scalar functions that manipulate input string
and output the string or numeric value based on the function. Like ABAP,
SAP SQLScript provides multiple string functions. String expression and
string concatenation are now a part of OpenSQL. The constant string can be
enclosed in a single quotation mark. You can assign the Unicode string
with the prefix N (uppercase N) for example N’unicode’.
AMDP Code:
DECLARE lv_lifnr VARCHAR(10);
SELECT ABAP_ALPHANUM(:lifnr, 10) FROM DUMMY INTO lv_lifnr;
-- or
SELECT LPAD(:lifnr, 10, ‘0’)
FROM DUMMY INTO lv_lifnr;
Concatenation
Scenario: Assume that there is an internal table with three columns viz.,
GRP (integer), ID (integer), and ZFIELD (string). You need to concatenate
all zfield into one string delimited by a comma for each unique group.
Also, the order of concatenation is based on the value of id (in ascending
order). The sample data and expected results are in the following tables.
ABAP Code:
LOOP AT it_tab INTO DATA(lv_tab)
GROUP BY ( grp = lv_tab-grp) ASCENDING ASSIGNING FIELD-
SYMBOL(<fs_grp>).
CLEAR: lv_str.
LOOP AT GROUP <fs_grp> ASSIGNING FIELD-SYMBOL(<fs_grp>).
CONCATENATE lv_str <fs_grp>-zfield CONCATENATED by ‘,’;
ENDLOOP.
lv_tab1-grp = lv_tab-grp.
DATA(lv_len) = STRLEN( lv_str ).
Lv_tab1-output = lv_str(lv_str-1).
APPEND lv_tab1 TO lt_tab1.
ENDLOOP.
AMDP Code: The solution is based on the string aggregate function.
Map Function
SQLScript provides the MAP function that searches the expression within a
set of values and returns a matched result or default result. This is a very
handy function that helps you replace a simple length CASE statement. For
example, you need the month name for the period with the fiscal year in the
accounting invoice document. See the following code snippet (for the fiscal
year starting from January) that uses the MAP function instead of the CASE
WHEN statement. The CONCAT function is used in the code snippet.
Instead of CONCAT, you can use a single row/column subquery.
Regular Expression
Pattern matching is one of the significant SQL features that enables you to
search for patterns in data. You can use the regular expression to do pattern
matching. The regular expression is a generalized expression that is used
to match patterns with various sequences of characters. It is commonly
called RegEx. The pattern can be one or more character literals, operators, or
constructs. The search pattern is based on Perl Compatible Regular
Expression (PCRE). ABAP supports the regular expression in the FIND,
FIND ALL OCCURRENCES, and REPLACE ALL OCCURRENCE
statements. Also, you can use the ABAP class CL_ABAP_REGEX.
RegEx functionality is supported in multiple SQL functions and predicates
like FIND, REPLACE, LIKE, and STRING. The functions are:
• LOCATE_REGEXPR – The locate function returns the position of a
substring within a string. The LOCATE_REGEXPR function searches a
string for a regular expression pattern and returns an integer indicating the
beginning position, or the ending position plus 1, of one occurrence of the
matched substring.
• OCCURENCES_REGEXPR – The function returns the number of matches
of a regular expression search within a string.
• REPLACE_REGEXPR – The function searches the regular expression and
replaces patterns with the replacement.
• SUBSTRING_REGEXPR – It searches a string for a regular expression
pattern and returns one occurrence of the matching substring.
• LIKE_REGEXPR – The predicate performs the regular expression matching.
You can use the predicate in the WHERE clause or any expression.
Scenario: Consider the scenario, you have a custom table with the
customer’s name, and phone number (character string without any
cleansing) and you need to list out all customer names and phone numbers
(with no alphabet). The OpenSQL statement does not support regular
expression, you need to loop through the internal table and use REPLACE
ALL OCCURRENCE to replace the string.
ABAP Code:
SELECT Name, Phone FROM ZCUST_TABLE INTO TABLE @DATA(lt_cust).
LOOP AT lt_cust ASSIGNING @<FIELD-SYMBOL>(<fs_cust>).
REPLACE ALL OCCURRENCES OF REGEX '([^\d])' IN <fs_cust>-phone WITH ''.
ENDLOOP.
AMDP Code: You can implement the same In AMDP Script as follow:
The following code snippet is to list out the vendors with the name ‘SCOTT’
or ‘AWAY’ with case-insensitive.
et_lfa1 = SELECT * FROM LFA1 WHERE text LIKE_REGEXPR ' scott|away ' FLAG i;
Built-in Libraries
Scenario: You need to split the string into table. The following code snippet
demonstrates to split the string into the table using LIB: SPLIT_TO_TABLE.
Result:
Instead of splitting to a table, you can split to the variables like ABAP
programming.
AMDP Code:
You can use the SAP HANA SQLScript library SQLSCRIPT:
FORMAT_TO_TABLE. The function formats each record in the table
variable and creates a table of strings.
METHOD sqlscript_string_format BY DATABASE PROCEDURE
FOR HDB LANGUAGE SQLSCRIPT
USING lfa1 geoloc.
et_out = LIB:FORMAT_TO_TABLE(
'{lifnr},{addrnumber},{city},{longitude},{latitude}', :lt_vendor );
ENDMETHOD.
Scenario: You need to get message text like ABAP’s MESSAGE-INTO
clause. For a given message-id, message number, and input string
(parameters into comma delimited string), you need to get message text.
See the following code snippet.
Hamming distance is how many different symbols are between two equal-
length strings. The basic purpose is to measure the minimum number of
substitutions required to change one string into the other. SAPScript
provides the function HAMMING_DISTANCE to perform a bitwise or
bytewise comparison between two arguments and returns the hamming
distance. The function returns -1 when the length of strings is different.
The HAMMING_DISTANCE supports the BINARY data type also
(compare for each bit).
DECLARE lv_hdist INTEGER;
-- first one returns 0; second returns 1 and third returns 3
SELECT HAMMING_DISTANCE('abc', 'abc') FROM DUMMY INTO lv_hdist;
SELECT HAMMING_DISTANCE('abc', 'abd') FROM DUMMY INTO lv_hdist;
SELECT HAMMING_DISTANCE('abc', 'cab') FROM DUMMY INTO lv_hdist;
Unicode Normalization
Array Processing
In ABAP, you can access the table-typed column value using lt_tab[1]-col1.
It raises the runtime error ITAB_LINE_NOT_FOUND when the array index
is out of bounds. The common practice is to use a field symbol to update
the column values in the table.
For table-type variables, you can directly assign the value through the index
access. See the following code snippet to update the table type variable.
Scenario: Consider that your input table has a column with integer values.
You need to generate a table with two columns. The first column should
have column values in the input table in ascending order and the second
column should have descending order values.
AMDP Code:
METHOD sqlscript_string_format BY DATABASE PROCEDURE
FOR HDB LANGUAGE SQLSCRIPT.
t1 = SELECT col1 from :it_tab ORDER by col1 ASC;
t2 = SELECT col1 from :it_tab ORDER by col1 DESC;
et_tab = UNNEST( :t1, :t2 ) AS ( col1, (col1 as col2) );
ENDMETHOD.
Due to the restriction of ABAP, the AMDP method does not support the
array as the parameter. AMDP methods support only table-type
Session Management
You can implement the session variables in multiple ways in ABAP. The
common ways to handle this:
• Static ABAP class and static attributes and static methods to set/read the
variables.
• Function group and methods to set/read the variables
• Import and Export to Memory statements
AMDP Code:
METHOD method1 BY DATABASE PROCEDURE
FOR HDB LANGUAGE SQLSCRIPT.
SET ‘CONTXT_VAR1’ = IM_VAL;
ENDMETHOD.
Variable Description
DATE_FORMAT Date format
TIME_FORMAT Time format
TIMESTAMP_FORMAT Timestamp format
CASE_SENSITIVE Flag for whether case sensitive
CLIENT Client number
LOCALE_SAP Login Language
Like NULL, the divide by zero error is one of the common errors. In ABAP,
you can catch the exception CX_SY_ZERODIVDE when the divisor is zero.
SQL error 304 is raised when the SQL raises the exception when the data
value is divided by zero. You can catch the error or you can avoid it using
the SQL function NDIV0. The NDIV0 function returns zero when the value
is divided by zero, otherwise, it returns the actual value.
Exception Handling
In ABAP, the exceptions are events that arise during the execution of the
program when it is not able to continue in a meaningful way. The
exception can cause the termination of the program. The exception
handling process enables you to handle the exceptions and avoid any
unnecessary short dumps.
ABAP supports two types of exceptions: classic exceptions and class-based
exceptions. The classic exceptions can be only declared in the interfaces of
methods or function modules using EXCEPTIONS. It can be raised using
the RAISE or MESSAGE RAISE statements. You can handle it using the
return codes of the system field SY-SUBRC. ABAP’s class-based exceptions
are defined by the exception classes. The exception class is created when
you raise a class-based exception and it can be further propagated by any
procedures.
SAP provides several AMDP exception classes with the prefix CX_AMDP.
Note that you cannot define any exception in the AMDP function. The base
AMDP exception class is CX_AMDP_ERROR (subclass of
CX_DYNAMIC_CHECK). The list of AMDP exception classes is in the
following table.
SQL exception handling gives you control over the SQLScript procedure.
When the exception occurs, the following SQL operations are suspended.
SQLScript provides multiple exception operators viz., EXIT HANDLER and
CONTINUE HANDLER for exception handling. The EXIT handler in
SQLScript offers a way to process exception conditions in a procedure or a
function during execution. The EXIT handler is equivalent to ABAP’s
CATCH statement. The CONTINUE handler not only allows you to handle
the error but also to continue with the execution after an exception has been
thrown. The CONTINUE is equivalent to the RESUMABLE exception.
The DECLARE EXIT HANDLER parameter allows you to define an exit
handler to process exception conditions in your procedure or function.
Note that if you have any exporting parameters (like the table type or
scalar) to pass back to the ABAP code then make sure to handle the code
while handling the exception. You can declare a condition on a user-
defined error code or an SAP HANA error code. It helps you to get better
readability of the code.
You can get a complete list of error codes using the following query.
Note that the implicit or explicit parallel execution of the SQL statements is
not supported in the EXIT or CONTINUE handler. EXIT handlers cannot be
declared within the same scope or a nested scope of a CONTINUE handler,
but CONTINUE handlers can be declared within the nested scope of an
EXIT handler.
The list of commonly used SQL Error Codes is listed in the following table.
Code Message
257 SQL syntax error
301 Unique constraint violated
304 Division by zero undefined
305 The single-row query returns more than one
row
307 Numeric value out of range
314 Numeric Overflow
315 Invalid synonym
407 Invalid date format
426 Number of columns mismatch
1299 No data found
1318 Built-in function not defined
5088 Hierarchy error
User-defined Errors
You can declare a user-defined error code, where the error code is between
10000 and 19999. SIGNAL or RESIGNAL can be used to throw a user-
defined exception in the SQL procedure. You can signal or re-signal a user-
defined error code for your business conditions.
The fuzzy search function handles full-text search capabilities with a degree
of error tolerance. The error tolerance is used to find information even if
there are slight misspellings or related words. SAP offers an error tolerance
between 0 and 1 (where 1 is a perfect match). SAP defaults it with 0.8.
Fuzzy search calculates the fuzzy score (between the search term and the
data value) to understand the error tolerance. When full-text search on the
text column, the fuzzy score uses the term frequency and inverse document
frequency (TF/IDF) score instead of the actual fuzzy score.
Selection Criteria
One of the important issues raises when you code pushdown is how to
handle select options in the AMDP procedure i.e., how can we pass the
select-options to the AMDP method? Consider that the program has
multiple select-options, and you need to pass select options to the AMDP
method to filter the select statements in SQLScript.
DATA(lv_where1) = cl_shdb_seltab=>combine_seltabs(
it_named_seltabs = VALUE #(
( name = 'EKORG' dref = REF #( s_ekorg[] ) )
( name = 'EBELN' dref = REF #( s_ebeln[] ) )
( name = 'LIFNR' dref = REF #( s_lifnr[] ) ) )
iv_client_field = 'CLIENT' ) .
The sample AMDP method code to handle the APPLY_FILTER to handle
your selection criteria.
AMDP Enhancement
Aggregate Functions
Scenario: Get records of the sales record which is the maximum for each
product group for the record. You cannot able to use the GROUP BY
statement.
ABAP Code:
Approach 1:
lt_prod[] = lt_product_sales[].
Delete adjacent duplicates from lt_prod COMPARING BY product.
SORT lt_product_sales BY product, sales DESC.
LOOP AT lt_prod INTO dat(lv_prod).
READ lt_product_sales INTO DATA(lv_product) WITH KEY product =
lv_prod.product.
LOOP AT lt_product_sales INTO data(lv_product1).
WHERE product = lv_prod.product and sales = lv_product.sales.
APPEND lv_product1 TO lt_max_sales.
ENDLOOP.
ENDLOOP.
Approach 2 with CTE: Note that the ABAP common table expression (CTE)
supports ABAP 7.51.
WITH
+prod AS (
SELECT product, max(sales) as sales FROM ZT_SALES
GROUP BY product
)
SELECT s1.* “ List your fields
FROM ZT_SALES AS s1
INNER JOIN +prod AS p
ON s1~product = p~product AND s1~sales = p~sales
INTO TABLE @DATA( lt_result ).
AMDP Code:
HANA SQL implements aggregation functions that are not just based on
the group by statement. It uses the PARTITION BY clause that divides the
result set produced by the FROM clause into partitions to which the
function is applied.
Subquery
A subquery is a nested SQL query within a SQL query. The basic purpose of
the subquery is to combine two steps. SQLScript supports the subquery in
the SELECT clause, the FROM clause, and the WHERE (including the JOIN
statement) clause. It supports up to 50 subqueries in a single query. The
ABAP OpenSQL supports only the WHERE clause subquery. The
subqueries can be used in SELECT, INSERT, UPDATE, and DELETE
statements. The subquery is always enclosed in parentheses. The
SQLScript supports the subquery types:
The subquery is very flexible so that you can implement complex business
scenarios. You can divide the complex query into multiple queries with
logical steps. Use the subquery when the result that you want requires
more than one query and each subquery provides a subset of the table
involved in the query.
The following code snippet demonstrates the usage of the subquery. The
code fetches the sales order, sales organization, and how many sales orders
at the sales organization. You can provide multiple ways to implement it.
In the WHERE clause subquery, you can use the subquery as the predicate
or the expression. SAP SQLScript supports the EXISTS and IN clauses. The
EXISTS operator is a logical operation that is used to validate the existence
of any record from the subquery and it returns TRUE if the subquery
returns at least one row otherwise, it returns FALSE. You can use the
subquery in the IN clause. You can validate that the column value is within
the values returned by the subquery. You can use multiple columns in the
IN clause. If the subquery returns a huge number of records, then do not
use the IN statement.
The following code snippet lists all the sales order items that match with a
given set of sales orders and items (as a part of input parameters it_sales).
Universal Quantifiers
METHOD univ_any_so
BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT USING VBAK
KNA1.
et_so =
SELECT vbeln, vkorg, auart, bstnk, kunnr
FROM VBAK
WHERE KUNNR = ANY ( SELECT KUNNR FROM KNA1 WHERE REGIO = 'MD'
);
ENDMETHOD.
Consider the requirement that you need to list out all sales orders of plant
PLT1 where its net price is higher than any of the sales orders of the same
material in plant PLT2.
METHOD univ_all_so
BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT USING VBAK
KNA1.
et_so1 = SELECT vbeln, posnr, matnr, netpr
FROM VBAP AS V1
WHERE WERKS = 'PLT1'
and NETPR > ALL ( SELECT NETPR FROM VBAP as v2
WHERE WERKS = 'PLT2' and v1.matnr = v2.matnr );
ENDMETHOD.
The same can be implemented with ANY statement (using the negation
rule).
METHOD univ_all_so
BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT USING VBAK
KNA1.
ENDMETHOD.
Set Operations
Set operators are used to combine or exclude the results of two or more SQL
queries. The SQL statements in the set operations must have matching
columns in the same order and of a compatible type. SAPScript supports
the following SET operators.
• UNION – Combines the result of two or more select statements and returns
the distinct result
• UNION ALL – Combines the result of two or more select statements and
returns all the result set (possible duplicate records)
• INTERSECT – Combines the result of two or more select statements and
returns all common rows
• EXCEPT – This is equal to MINUS. Takes the result from the first query
and then removes the rows selected by the selection query
Grouping Set
You can group by the number of fields in the ABAP SELECT statement to
aggregate the results using the grouping fields. You can use the grouping
set to return multiple sets of aggregation in a single result set. The
grouping set is equivalent to the UNION ALL of all aggregate results of
each group.
AMDP Code:
METHOD gl_groupset
BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT OPTIONS
READ-ONLY
USING bsis.
et_out = select bukrs, gjahr, hkont, gsber, prctr,
sum(dmbtr) as dmbtr, sum(wrbtr) as wrbtr,
sum(dmbe2) as dmbe2, sum(dmbe3) as dmbe3, count( * ) as count
from bsis
GROUP BY GROUPING SETS ( ( bukrs, gjahr), (bukrs, hkont) , (bukrs,
gsber) , (bukrs, prctr ) );
et_out = APPLY_FILTER( :ET_OUT, :iv_where );
ENDMETHOD.
Along with GROUPING SET, there are two more options viz., ROLLUP and
CUBE. The ROLLUP(Col1, Col2, Col3) is equivalent to GROUPING SETS (
(Col1, Col2, Col3), (Col1, Col2), Col1). The CUBE(Col1, Col2, Cole) is
equivalent to GROUPING SETS ( (Col1, Col2, Col3), (Col1, Col2), (Col1,
Col3), (Col2, Col3), Col1, Col2, Col3). You can use the GROUPING_ID
function with GROUPING SETS, ROLLUP, or CUBE. It returns an integer
value that groups a set of the returned row.
Window Function
The window function is one of the advanced techniques used in SQL.
Aggregate functions return a single scalar value on a set of records.
Window functions operate on a set of rows called ‘window’ and return a
single value for the underlying query somehow related to the current row.
This means that the window functions allow you to divide the result sets of
a query into groups of rows called window partitions. The window
function is executed by the OVER clause and you can express the query
expression using the PARTITION BY clause. The partitioning is horizontal
so that data sets of rows are mapped to individual partitions. Note that the
rows are not collapsed (as in the aggregate functions). Use cases of window
functions include running totals, moving averages, and identifying gaps.
Function Comments
RANK Ranking function
DENSE_RANK Ranking; no skipping ranking number
PERCENT_RANK Calculates the percentage of values that are either less
or greater than the current record value
LAG Information from previous partition record
LEAD Information from the following partition record
ROW_NUMBER The row number of the partition
NTILE Nth title of the partition record
FIRST_VALUE Returns value of the first element of an expression
LAST_VALUE Returns value of the last element of an expression
Row Number
The window ranking function enables you to sequence the number (row)
within a partition of the result. Within the partition, the first row is
assigned as 1. Note that the row number is a non-deterministic function
among tie values. The sequence order is based on the order within the
partition by clause (OVER window).
Rank
SQL Rank function is one of the aggregation functions and it returns the
rank for individual fields as per categorization or grouping. It is also
known as Window Functions. There are multiple rank functions supported
by SAP SQL Script including ROW_NUMBER, RANK, DENSE_RANK, and
NTILE.
Scenario: Consider the scenario in which you need to rank the sales for each
product name. Assume that there is only one product name and
corresponding sales. The required output is to list out the product name,
type, sales, and rank with the selection criteria. The use case is to list out all
the top 3 sales for each product category.
ABAP Code:
SELECT ProdName, Type, Sales
FROM ProductSales
ORDER BY ProdName, Type
INTO table lt_prod;
sort lt_prod by sales desc.
LV_RANK = 1.
LV_PREV_SALES = 0.
LOOP AT lt_prod INTO data(lv_prod).
Move-corresponding lv_prod TO lv_prod1.
IF lv_prev_sales > lv_prod-sales.
Lv_rank = lv_rank + 1.
ENDIF.
Lv_prod1-rank = lv_rank.
APPEND lv_prod1 TO lt_prod1.
ENDLOOP.
AMDP Code:
The DENSE_RANK function returns the ranks like the RANK() function but
with a small difference. The rank function skips the ranking order when
two records have the second rank than the following rank will be fourth.
But in the DENSE_RANK function, the rank is always sequential (no
jumping). You can see the following to show the difference between the
rank and the dense rank.
AMDP Code:
METHOD RANKING_PRODUCT BY DATABASE PROCEDURE FOR HDB
LANGUAGE SQLSCRIPT OPTIONS READ-ONLY.
ex_out = SELECT ProdName, Type, Sales,
RANK() OVER ( PARTITION BY ProdName ORDER BY Sales DESC ) AS
Rank,
DENSE_RANK() OVER ( PARTITION BY ProdName ORDER BY Sales DESC )
AS DRank,
ROW_NUMBER() OVER ( PARTITION BY ProdName ORDER BY Sales DESC
) AS Rowid
FROM ProductSales
ORDER BY ProdName, Type;
ENDMETHOD.
The lead function is used to access the data from subsequent rows along
with the current data row. The lag function is used to access the data from
previous rows along with the current data row. Consider the scenario, you
need the list of total sales per sales organization, fiscal year, and a quarter
along with the previous quarter's total sales.
ABAP Code:
Select sales org, fiscal year, case when period to quarter, sum(amount)
FROM TABLE1 group by sales org, fiscal year.
LOOP AT lt_sales INTO FIELD-SYMBOL(<fs_sales>).
DATA(lv_tabix) = sy-tabix - 1.
If lv_tabix > 0.
READ TABLE lt_sales INTO DATA(lv_psales) WITH INDEX lv_tabix.
<fs_sales>-psales = lv_psales-sales.
Endif.
ENDLOOP.
AMDP Code:
SELECT
employee_id,
employee_name,
salary,
lead(salary, 1, 0) over (order by salary desc) as next_highest_salary
FROM employees;
Moving Average
ABAP Code:
SELECT salesdt, sales INTO CORRESPONDING FIELDS of lt_sales
FROM ZSD_SALES_020
WHERE salesdt in s_dt.
AMDP Code:
SELECT salesdt, sales,
AVG(sales) OVER ( ORDER BY salesdt ROWS BETWEEN 6 PRECEDING AND
CURRENT ROW ) AS mavg7,
WEIGHTED_AVG(sales) OVER (ORDER BY salesdt ROWS BETWEEN 6
PRECEDING AND CURRENT ROW) AS wmavg7
FROM ZSD_SALES_020;
et_sales = APPLY_FILTER( :et_sales, :iv_where ).
Binning Function
Binning is a technique that allows you to put similar datasets into bins. You
can assign a bin number to each row based on the partitioning of an input
set into disjoint sets. SAP HANA SQLScript supports multiple binning
functions based on the count, width, tile count, and standard deviation.
AMDP Code:
METHOD get_binning
BY DATABASE PROCEDURE FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY USING ZT_SALES.
et_sales =
SELECT salesdt, sales,
BINNING(VALUE => sales, BIN_COUNT => 4) OVER () AS bin1,
BINNING(VALUE => sales, BIN_WIDTH => 4) OVER () AS bin2,
BINNING(VALUE => sales, TILE_COUNT => 4) OVER () AS bin3,
BINNING(VALUE => sales, STDDEV_COUNT => 4) OVER () AS bin4
FROM ZT_SALES ; -- Add your where conditions
ENDMETHOD.
Interpolation
You can interpolate based on the percentile of the distribution. You can use
SAP HANA functions PERCENTILE_CONT and PERCENTILE_DISC to
interpolate the value based on the series. The basic rule is that if value X is
at the nth percentile, then X is greater than n% of the values.
Scenario: Consider the dataset with a list of daily sales values for each
department. You need to find the 85% value and 85% percentile of the data
values for each department. The value represents the exact value and the
percentile represents the value on or after the 85% value in the data set.
ABAP Code:
AMDP Code:
METHOD interpolate
BY DATABASE PROCEDURE FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY.
lt_sales = SELECT salesdt, dept, sales,
PERCENTILE_CONT (:iv_percent) WITHIN GROUP ( ORDER BY sales )
OVER ( PARTITION BY dept ) AS pd1,
PERCENTILE_DISC (:iv_percent) WITHIN GROUP ( ORDER BY sales )
OVER ( PARTITION BY dept ) AS pd2
FROM :it_sales;
et_sales = SELECT distinct dept, pd1, pd2 FROM :it_sales;
ENDMETHOD.
Scenario: Consider you have the list of sales values and corresponding
profit values as it is in the table. You need to interpolate the profit value for
the new sales value.
Sales Profit
1,000,000 200,000
2,000,000 300,000
3,000,000 400,000
4,000,000 500,000
5,000,000 600,000
6,000,000 ?
AMDP Code:
METHOD linearapprox
BY DATABASE PROCEDURE FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY.
lt_sales = SELECT sales, profit,
LINEAR_APPROX ( profit, ‘EXTRAPOLATION_LINEAR’) OVER ( ORDER
by sales ) as approx
FROM :it_sales;
et_sales = SELECT sales, COALESCE(profit, approx) as val
FROM :it_sales;
ENDMETHOD.
The function parameters include training set size, validation set size, test set
size, and seed. When the total of all sizes is less than one, then the sizes are
expressed as fractions of the total input size. When the sizes are greater
than or equal to one, then it represents the actual size of the data set. The
random seed is used to initialize the pseudo-random number generator.
When set to zero, the seed is initialized with the current time.
AMDP Code
METHOD random_partitioning
BY DATABASE PROCEDURE FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY.
et_sales =
SELECT salesdt, sales,
( CASE RANDOM_PARTITION(0.5, 0.2, 0.3, 0) OVER (ORDER BY sales)
WHEN 1 THEN 'Training'
WHEN 2 THEN 'Validation'
WHEN 3 THEN 'Test'
ELSE 'Not Assigned' END ) AS partition
FROM :it_sales;
ENDMETHOD.
Advanced Topics
The chapter intends to cover the advanced topics you can use in your
AMDP method implementation. A few of the techniques may or may not
apply to ABAP development. The chapter covers CDS table functions,
dynamic programming, hierarchy functions, and spatial functions.
Dynamic programming allows you to generate dynamic SQL. Also, the
chapter details the parallel processing and mail merge of the custom UDF.
The series generation functions enable you to populate the series of data
values within a given interval. The chapter explains a few series of
generation functions with examples. SAP HANA includes a multilayered
spatial engine and spatial methods. The geographical calculation requires
complex algorithms to handle various geo coordinates. SQLScripts
provides multiple spatial functions. The chapter discusses a few spatial
functions with examples.
SAP HANA provides a virtual data model (VDM) feature that allows you to
create a virtual representation of data from multiple sources, such as SAP
ERP and non-SAP systems, and access it as if it were all stored in a single
location. The CDS views provide a virtual data model where it uses basic
SQL functions, joins, and unions with the association within SAP
Ecosystem. Note that the CDS cannot support complex computations like
ABAP programming. The CDS table function uses the power of
programming to provide complex business solutions. The CDS Table
function is a part of the CDS data definition. It uses the AMDP class
method as a data source. The CDS table function can be consumed directly
by CDS views and ABAP OpenSQL. Note that the CDS table function
cannot have any sqlViewName. But you can expose it in another CDS view
with the SQL view name.
The main purpose of the CDS table function is to expose the result of the
class method to a CDS view. The table function behaves like a table and it
can be consumed in any of the CDS views and ABAP OpenSQL. It
simplifies the data model of complex logic. Another advantage is that you
can debug the results using an AMDP debugger. The table function opens a
lot of gates in data modeling. The table function supports the parameters.
Each CDS table function has two components: the actual CDS entity of the
table function and the AMDP method implementation. The method must
be declared as a table function (user-defined table function). This is a part
of code pushdown capabilities in ABAP CDS. The CDS table function can
have parameters that can be passed to the AMDP method. You can
implement the implicit client handling on the CDS entity using the
annotation @Environment.systemField:#CLIENT.
Scenario: Consider the situation that you need to get a list of obligations,
adjustments, and consumption by period for a given PO. The obligation is
the initial amount created by PO. The adjustments refer to the change in the
PO amount and consumption is the invoiced amount.
Note that you need to use uppercase for AMDP class and method name.
AMDP Code:
CLASS-METHODS get_po_summary
FOR TABLE FUNCTION DEMO_CDS_PO_SUMMARY.
METHOD DEMO_CDS_PO_SUMMARY
BY DATABASE FUNCTION FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY
USING scarr spfli.
RETURN SELECT sc.mandt as client,
sc.carrname, sp.connid, sp.cityfrom, sp.cityto
FROM scarr AS sc
INNER JOIN spfli AS sp ON sc.mandt = sp.mandt AND
sc.carrid = sp.carrid
WHERE sp.mandt = :clnt AND
sp.carrid = :carrid
ORDER BY sc.mandt, sc.carrname, sp.connid;
ENDMETHOD.
AMDP Mesh
@AbapCatalog.sqlViewName: 'ZCV_CDS_SO'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Order Details'
define view ZC_SD_SO_DET
as select from ZI_SALESORDER_HEADER as head
association [1..*] to ZI_SALESORDER_ITM as _Items
on $projection.vbeln = _Items.vbeln
{
key SalesOrder,
CreationDate,
vbtyp,
OrderType,
Amount,
Currency,
_Items
}
In CDS, the path expression is a way of navigating through the data model
to select specific data using the associations. Path expressions and filtered
associations make it easier to read the CDS View definitions, as well as the
SELECT statements accessing the views. In OpenSQL, the path expression
is composed of a sequence of path segments, which are separated by the
slashes '\'.
TRY.
SELECT SalesOrder, CreationDate, OrderType, Amount, \_Items-matnr
FROM ZC_SD_SO_DET
WHERE SalesOrder in so_vbeln
INTO TABLE @DATA(lt_so).
cl_demo_output=>display(lt_so).
CATCH.
ENDTRY.
The path expression can be used in both the SELECT and FROM classes.
You can do the same with the ABAP mesh without the CDS definition.
First, you need to define all internal tables and their associations. Once you
populate the internal tables in the ABAP mesh, you can use the path
expression.
types:
t_orders TYPE SORTED TABLE OF vbak WITH UNIQUE KEY node_key .
types:
t_items TYPE SORTED TABLE OF vbap WITH UNIQUE KEY node_key
with NON-UNIQUE SORTED key parent_key
COMPONENTS parent_key.
types:
BEGIN OF MESH t_sales_order,
items TYPE t_items
ASSOCIATION _products TO products ON node_key = product_guid,
orders TYPE t_orders
ASSOCIATION _items TO items ON parent_key = node_key USING
KEY parent_key
END OF MESH t_sales_order .
DATA: lt_so TYPE t_sales_order.
There is no big difference between implementing the MESH from the ABAP
code or the AMDP code. The complexity of the structure and complex
ways of populating the internal table may determine whether you can use
ABAP code or AMDP code. Also, the parallel processing of AMDP can
improve performance. You can see the demo program
DEMO_AMDP_MESH. The following code snippet demonstrates the path
expression used in the ABAP mesh.
TRY.
DATA(lt_item) = lt_so~orders\items[ matnr = p_matnr].
CATCH.
ENDTRY.
Dynamic Programming
ABAP is a typed programming language where you can define the type of
data type and it will be known at compile-time or runtime. ABAP 7.4
features provide an inline function that avoids the explicitly typed
declaration. The runtime type services (RTTS) are used to handle types at
runtime. In ABAP, the type description object uses the type description
class and its attributes to describe the technical properties of the type. The
RTTS has two methods viz., RTTC (Runtime Type Creation) and RTTI
(Runtime Type Identification). You can execute the dynamic SQL
statement using EXEC and EXEC IMMEDIATELY statements. These
statements can be used both in ABAP and AMDP SQL Script. ABAP
manages the implementation of the AMDP database procedures, but its
contents and security are not part of ABAP’s responsibility. Whenever any
dynamic parts incur any risks that are unknown in ABAP, they cannot be
checked.
The dynamic SQL statements can be applied into the SELECT clause.
DECLARE cname1 VARCHAR(20) = ‘ZCOL1’;
DECLARE cname1 VARCHAR(20) = ‘ZCOL2’;
DECLARE lv_min INT ;
DECLARE lv_max INT;
EXEC ‘SELECT MAX(‘ || ?|| ‘) , MIN(‘ || ?|| ‘) FROM ZT_TAB WHERE col1 = ?’
INTO lv_max, lv_min USING :cname1, :cname2;
Scenario: Need to get the list of archived purchase requisitions for the given
info structure. The archived index table is dynamically generated for each
client. The following code snippet provides the dynamic programming
concept. Note that there is no big difference between the ABAP code and
the AMDP code for the selection. When you want to process a lot of
archived data, then AMDP code provides a good edge on performance.
AMDP Code:
Parallel Processing
Map Merge
AMDP code:
CLASS zcl_amdp_merge DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
TYPES:
BEGIN OF TY_po,
po type ekko-ebeln,
END OF ty_PO,
ty_po_tbl TYPE TABLE OF ty_po.
INTERFACES: if_amdp_marker_hdb.
CLASS-METHODS:
merge_fm
IMPORTING
VALUE(iv_po) TYPE ebeln
RETURNING
VALUE(et_fmioi) TYPE po_fmioi_tbl,
seq_merge
IMPORTING
VALUE(it_po) TYPE ty_po_tbl
EXPORTING
VALUE(et_fmioi) TYPE po_fmioi_tbl ,
map_merge
IMPORTING
VALUE(it_po) TYPE ty_po_tbl
EXPORTING
VALUE(et_fmioi) TYPE po_fmioi_tbl .
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
ENDMETHOD.
--Sequence method to call database function for each po values in the input table
METHOD seq_merge BY DATABASE PROCEDURE FOR HDB LANGUAGE
SQLSCRIPT
OPTIONS READ-ONLY USING
ZCL_AMDP_MERGE=>MERGE_FM .
ENDMETHOD.
--PARALLEL PROCESSING for the records in the PO internal table. You can see
a big difference based
--on the table. The complexity of functions and the size of the internal table
determine the
--performance
ENDMETHOD.
ENDCLASS.
The map-reduce function cannot be implemented in the AMDP function
due to its restriction of passing the table type in the IMPORT parameter.
The import parameter allows only elementary data types only in the
database function implementation.
Series Generation
All these functions return the columns including start, end, element, and
fraction information. It is a common use case in business intelligence
queries, you need to expand the range of dates. You can see the details of
the columns in the following table.
The scenario is that you need to create periods between the dates with the
assumption is that
METHOD test_generate_period BY DATABASE PROCEDURE FOR HDB LANGUAGE
SQLSCRIPT OPTIONS READ-ONLY.
ex_out = SELECT TO_DATS(GENERATED_PERIOD_START) as from_date,
TO_DATS(add_days( generated_period_end, -1 )) as to_date
FROM SERIES_GENERATE_DATE( 'INTERVAL 1 MONTH', :from_date, :to_date
);
ENDMETHOD.
lt_series =
SELECT SOURCE_PERIOD_START, SOURCE_PERIOD_end,
generated_period_start, generated_period_end
FROM SERIES_DISAGGREGATE_TIMESTAMP(
'INTERVAL 1 MONTH',
'INTERVAL 1 DAY',
TO_DATE('2022-10-01'),
TO_DATE('2023-11-01'));
JSON/XML
JSON and XML formats are commonly used in SAP for data exchange.
JSON (Javascript Object Notation) format is a lightweight standard text-
based format for representing structured data on Javascript object syntax.
XML (Extensible Markup Language) is a widely used text-based format for
representing structured information on plain text. ABAP provides several
tools to handle JSON and XML data. First, we can start with how can you
convert an internal table into JSON and XML format. The first code
snippets are representing how can you create JSON strings using ABAP
code and AMDP method.
ABAP Code:
lv_json_out =
/ui2/cl_json=>serialize(
data = lt_test
compress = abap_true pretty_name = /ui2/cl_json=>pretty_mode-camel_case
).
AMDP Code:
et_json = select * from :lt_test for JSON ;
The second code snippet explains how to generate XML output from an
internal table to XML format for both ABAP and AMDP codes.
ABAP Code
AMDP Code
et_xml = select * from :lt_test for
XML ('schemaloc'='http://thiscompany.com/schemalib',
'targetns'='http://thiscompany.com/samples');
SAP ABAP provides multiple tools to handle XML and JSON object
handling. XML reader tool (cl_sxml_string_reader) is used to handle both
XML and JSON data. The tools can be used to parse the JSON data and can
be converted into an internal table. The SQLScript enables you to do the
query on the JSON string to find scalar or record type values. In this
scenario, you are going to understand how to use the JSON_QUERY
function to handle the JSON string in SQLScript.
SAP HANA SQL supports JSON data management using the collection.
Like MongoDB, the collection object is used in SQLScript to store the JSON
entries. You can create the collection using the SQL statement CREATE
COLLECTION. The collection object does not have any fields. You can
insert the JSON entries directly into the collection. SQLScript provides you
with options to do a selection using the SELECT statement. In the select
clause and where clause, you can provide field names used in the JSON
structure. The fields can be used at any level of the collection. You can use
the WITH statement to join between two collections.
JSON Query
SAP SQLScript provides a few JSON functions to query on the JSON string.
The functions include JSON_VALUE, JSON_QUERY, and JSON_TABLE.
You can use the JSON_VALUE function to fetch the value of the path
expression on the JSON string. JSON_QUERY can fetch the values of the
path expression. Note that it returns a single value on the exact expression.
See the following code snippet, which tries to get the value of the path
express info.address[0].town (first address record’s town) and it returns
Allen Town as its value. The JSON_QUERY returns multiple values of
town (Allen Town, Fairfax). If you try same the path expression in
JSON_VALUE, you will get an empty value. The JSON_TABLE function is
another powerful function that can handle the JSON query on the column
table. The following code snippet demonstrates the power JSON_TABLE.
SELECT JSON_VALUE('{"info":{"address":[{"town":"Allen
Town"},{"town":"Fairfax"}]}}', '$.info.address[0].town') INTO lv_town FROM DUMMY;
SELECT JSON_QUERY('{"info":{"address":[{"town":"Paris"},{"town":"London"}]}}',
'$.info.address.town' ) INTO lv_towns FROM DUMMY;
Scenario: Consider the following JSON record for each PO Number. The
requirement is to get the list of all line items, part description, price, and
quantity along with the PO Number.
JSON String:
{
"PONumber": 1,
"Reference": "BSMITH-74635645",
"Requestor": "Barb Smith",
"User": "BSMITH",
"CostCenter": "A50",
"ShippingInstructions":
{
"name": "Barb Smith",
"Address":
{
"street": "100 Fairchild Ave",
"city": "San Diego",
"state": "CA",
"zipCode": 23345,
"country": "USA"
},
"Phone": [{"type": "Office", "number": "519-555-6310"}]
},
"SpecialInstructions": "Surface Mail",
"LineItems": [
{"ItemNumber": 1, "Part": {"Description": "Basic Kit", "UnitPrice":
19.95, "UPCCode": 73649587162}, "Quantity": 7},
{"ItemNumber": 2, "Part": {"Description": "Base Kit 2",
"UnitPrice": 29.95, "UPCCode": 83600229374}, "Quantity": 1},
{"ItemNumber": 3, "Part": {"Description": "Professional",
"UnitPrice": 39.95, "UPCCode": 33298003521}, "Quantity": 8},
{"ItemNumber": 4, "Part": {"Description": "Enterprise",
"UnitPrice": 49.95, "UPCCode": 91827739856}, "Quantity": 8},
{"ItemNumber": 5, "Part": {"Description": "Unlimited",
"UnitPrice": 59.95, "UPCCode": 22983303876}, "Quantity": 8}
]
}
When you pass the JSON string, you can get all line items.
METHOD JSON_QUERY
BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY.
ENDMETHOD.
NESTED PATH enables you to repeat the array in the path expression.
Multiple NESTED PATH returns the cartesian product of the path results.
You can see the result as follows:
Document Store
Document store enables you to work with SQL relational tables and
schema-less JSON collections. The feature eliminates a separate NO-SQL
document database. If you want to store JSON documents for long-term
processing or intermediate processing, the document store is highly
beneficial. You can create a collection object directly from ADT Eclipse or
SAP GUI. You need to have the HANA Database tool. Or you can use
AMDP or CL_SQL_STATEMENT ABAP object to create using the dynamic
SQL statement. Note that document store is an optional feature of the SAP
HANA procedure. Your system administrator needs to enable DocStore
per tenant database (by ALTER DATABASE <db> ADD ‘docstore’ ).
TRY.
DATA(o_sql) = NEW cl_sql_statement( ).
DATA(lv_statement) = |create collection json.qry1;|.
o_sql->execute_ddl( lv_statement ).
lv_statement = `insert into json.qry1 values({ "item1":1, "item2":2 } );`.
o_sql->execute_update( lv_statement ).
* Do the query
lv_statement = `select * from json.qry1 where item2 = 2 );`.
DATA(lv_statement) = |drop collection json.qry1;|.
o_result = sql->execute_query( lv_statement ).
o_result->set_param_table( REF #( lt_results ) ).
o_result->next_package( ).
cl_demo_output=>display( lt_results ).
o_sql->execute_ddl( lv_statement ). “ Drop collection
CATCH cx_sql_exception INTO DATA(lo_cx).
cl_demo_output=>display( lo_cx->get_text( ) ).
ENDTRY.
XML Query
SAP HANA SQLScript provides a few XML functions to query the XML
string. The functions are:
• XMLEXTRACTVALUE function returns the XML value that matches with
XPath.
• XMLEXTRACT function returns an XML element for a given XPath query.
• XMLTABLE function creates the relational table from an XML string and
you can query on them like JSONTABLE. The returned value can be a
value or XML string.
METHOD XML_QUERY
BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT OPTIONS
READ-ONLY.
Hierarchy
Hierarchy data is referred to a set of data items that are related to each other
by hierarchical relationships. SAP HANA supports two types of
hierarchical data viz., parent-child and level-based hierarchical data. The
parent-child hierarchy is a simple hierarchy created between two
dimensions while level-based hierarchies can have multiple dimensions
with more complex drilling options. In the parent-child hierarchy, each
record references a parent record within the same table. The employee
manager is a good example of the parent-child hierarchy. In the level-based
hierarchy, each record contains complete individual path information
through the hierarchy. Common examples are address data (with country,
state, and city) and time-based data (Year, Month, Date).
Terminologies
Terminology Description
Hierarchy Hierarchy generator function result
Node Basic Element of Hierarchy
Parent Node has either one or no parent but has at
least one child node
Child All descendant node
Root The top one with a hierarchy parent rank is
zero
Orphan Node is not reachable from any node
Edge Relationship between two nodes
SAP HANA offers four hierarchy generator functions and four navigation
functions.
• HIERARCHY Generator function – Generating a hierarchy from a recursive
parent-child source data structure.
• HIERARCHY_SPANTREE Generator function – Uses the same source and
generates only a minimal spanning tree, which is useful when the source
data contains many multiple parent edges.
• HIERARCHY_TEMPORAL Generator function – Generating a hierarchy
from a recursive parent-child source for a specific time interval.
• HIERARCHY_LEVELED Generator function – Generates an analogous
output structure based on source data where individual source columns
correspond to hierarchy levels and source rows define paths from a root to
a leaf node.
• HIERARCHY_ANCESTORS Navigation function – returns all ancestors of
a set of start nodes in the hierarchy.
• HIERARCHY_ANCESTORS_AGGREGATE – The function performs an
aggregate operation on the ancestors of a given node in a hierarchical table.
• HIERARCHY_DESCENDANTS_AGGREGATE – The function performs an
aggregate operation on the descendants of a given node in a hierarchical
table.
• HIERARCHY_DESCENDANTS – returns all descendants of a set of start
nodes in the hierarchy.
• HIERARCHY_SIBLINGS – retrieves all the siblings of a given node in a
hierarchical table.
Column Description
HIERARCHY_RANK The hierarchy rank of the node
HIERARCHY_PARENT_RANK Parent hierarchy rank
HIERARCHY_ROOT_RANK Root hierarchy rank
HIERARCHY_TREE_SIZE The tree size from the node
HIERARCHY_LEVEL The hierarchy level of the node
HIERARCHY_IS_CYCLE Whether hierarchy is cyclic
HIERARCHY_IS_ORPHAN Whether the node is an orphan or not
From the hierarchical data, you can traverse data either bottom-up or top-
down. You can use the hierarchical aggregate functions on the hierarchical
data. The analytical application uses the hierarchy to provide a high-level
overview with the option to drill down the branches. The hierarchy
generator function generates a hierarchy from a recursive parent-child
source of the data source. You can refer SAP hierarchy developer guide:
http://tinyurl.com/2272sf7w .
Scenario:
In this section, you can see how the hierarchy generator works for the WBS
element hierarchy as a use case. Consider the WBS hierarchy configuration
that has four levels of hierarchies. The hierarchy starts from the project and
continues with WBS elements (hierarchy of WBS elements based on
configuration), and continues to the Network data. When you define the
project and WBS element, you can see the WBS elements’ hierarchy data in
the table PRHI.
AMDP Code :
* Output Type:
begin of ty_ps_out,
parent_id type CHAR12,
node_id type CHAR12,
hierarchy_level type I,
object TYPE char01,
end of ty_ps_out,
ty_ps_out_tbl TYPE TABLE of ty_ps_out.
* Definition
CLASS-METHODS:
get_project_hierarchy
IMPORTING
VALUE(posnr) TYPE EDI_POSNR
EXPORTING
VALUE(et_out) TYPE ty_ps_out_tbl
RAISING cx_amdp_error.
* Implementation method
METHOD get_project_hierarchy BY DATABASE PROCEDURE
FOR HDB LANGUAGE SQLSCRIPT
USING prhi aufk.
lt_wbs_hier =
SELECT *
FROM HIERARCHY (
source ( select * from :lt_prlist ) SIBLING ORDER BY posnr ORPHAN ROOT
);
lt_desc = select * from HIERARCHY_DESCENDANTS(
SOURCE :lt_wbs_hier
START WHERE node_id = :lv_proj
)
ORDER BY hierarchy_rank ;
et_out = select parent_id, node_id, hierarchy_level, object from :lt_desc;
ENDMETHOD.
Tips: You can use the distance so that you can provide how deep you can
go. You can get ancestors using the following code:
Hashing
Hashing is the process of scrambling raw data to the extent that it cannot
reproduce back to its original form. The primary applications of hashing
include password and sensitivity data hashes, and integrity verification.
SHA stands for Secure Hash algorithm and SHA 256 is a part of the SHA
family of algorithms where the hash value will always be 256 bits
irrespective of the size of plain text. This section explains how to get
SHA256 hashed string for a given plaintext.
ABAP Code:
Data: lv_string TYPE string,
lv_hash TYPE string.
cl_abap_message_digest=>calculate_hash_for_char(
EXPORTING
if_algorithm = 'sha256'
if_data = lv_string
IMPORTING
ef_hashstring = lv_hash
).
Or
DATA: lv_xstring TYPE xstring,
lv_hash TYPE hash160.
" Calculate the hash value for the entire buffer/table
CALL FUNCTION 'CALCULATE_HASH_FOR_RAW'
EXPORTING
data = lv_xstring
IMPORTING
hash = lv_hash
EXCEPTIONS
unknown_alg = 1
param_error = 2
internal_error = 3
OTHERS = 4.
AMDP Code:
HANA SQLScript provides a function that returns hashed values for a
given string. See the following sample code.
Similarly, you can use the function HASH_MD5 to return a 32-byte hash
value. The hash value is calculated using an MD5 (Message Digest Method
5) algorithm.
SQL Injection
Password Generation
Random Number
The solution is provided to get the nearest points for a given input table of
latitude and longitude. The code uses WGS84 (SRID 4326) as a spatial
reference system. The SRID 4326 provides a spheroidal reference surface
for the Earth and is used by the Global positioning system (GPS). The
assumption is that the vendors’ address is associated with the GEOLOC
table. In AMDP, you can handle the same without a permanent table
(GEOLOC) but you need to populate the longitude and latitude for the
vendor and pass it as an internal table. AMDP can use the internal table as
the temporary table. You can use SQL query to do the same.
TYPES:
begin of ty_supp,
lifnr TYPE lfa1-lifnr,
addrnumber type ADRNR,
city type ORT01_GP,
state type REGIO,
latitude type geolat,
longitude type geolon,
distance type f,
end of TY_SUPP.
DATA: lt_nsupplier TYPE TABLE of ty_supp.
ENDMETHOD.
AMDP code uses the internal table as the reference (instead of the geoloc
table).
METHOD get_nearest_suppliers
BY DATABASE PROCEDURE FOR HDB
LANGUAGE SQLSCRIPT
OPTIONS READ-ONLY.
ENDMETHOD.
SAP provides a transparent table GEOLOC where you can enter latitude,
and longitude values for the address number. SAP provided the ABAP
class CL_GEOLOC to fetch latitude and longitude. You can use the
program UPLOAD_ZIP5GOLD to load geo-related information.
ABAP Code
The following ABAP code snippet to fetch the list of the suppliers for the
given latitude, longitude, and distance (in miles).
form get_suppliers_within_miles USING iv_miles type ICL_VAL_DISTANCE
iv_latitude TYPE GEOLAT iv_longitude TYPE GEOLON TABLES et_supplier
TYPE TY_SUPPLIER.
data:
lv_ngeo TYPE geolon,
lv_sgeo TYPE geolon,
lv_egeo TYPE geolat,
lv_wgeo TYPE geolat.
endform.
In HANA SQLScript, spatial data types are defined as object-oriented types.
You need to instantiate the data type using the clause NEW. Each data
types have several methods and you can access the methods from
instantiated data type. Most of the methods (such as ST_DISTANCE,
ST_INTERSECTS) are defined at ST_GEOMETRY The list of a few spatial
data types is listed in the table.
lt_kmean_clust =
SELECT ST_ClusterId() AS cluster_id,
ST_ClusterCentroid() AS centroid,
COUNT(*) AS number_of_households,
AVG(INCOME) AS average_cluster_income
FROM ZT_HOUSEHOLDS
GROUP CLUSTER BY LOCATION USING KMEANS CLUSTERS 100;
Note that you may get the SQL error code 7 when the spatial feature is not
supported by your version. For more references, check the SAP help
https://tinyurl.com/4wd486vj.
The Author