Download as pdf or txt
Download as pdf or txt
You are on page 1of 12

openSAP

A First Step Towards SAP HANA Query


Optimization
Week 1 Unit 1

00:00:05 Hello, and welcome to the openSAP course A First Step Towards SAP HANA Query
Optimization.
00:00:12 My name is Helen Shin and I'm an SAP HANA database product specialist, and together
with my colleague, Jinyeon Lee, I will present to you this course.
00:00:25 Let's have a look at our course overview. This course consists of four weeks, and at the end
of the course you will have a final exam.
00:00:36 In this course, you will get insights into the techniques to improve SQL performance and
methods for query performance analysis in SAP HANA.
00:00:48 I will tell you about more in detail in later slides. As a target audience, this course is for SAP
HANA developers, consultants,
00:00:59 and anyone interested in learning about SAP HANA query performance. As a course
requirement, we ask you to have basic knowledge of the SQL language.
00:01:13 As I mentioned earlier, this course consist of four weeks. In week one, you will learn about
how an SQL query is processed in SAP HANA.
00:01:25 In week two, you will discover an SAP HANA-specific feature, which is column search, and
also other analysis tools, such as useful traces.
00:01:37 In week three, you will learn about query performance analysis methods, for example, how
to narrow down the issue
00:01:45 and what other facts you can check out for performance analysis. In week four, there are
case studies and you can try.
00:01:55 So with the knowledge from week one to week three, you'll be able to work on case studies.
Since the main goal of this course is
00:02:05 for you to fully benefit from the SAP HANA capabilities in terms of query performance, there
are several hands-on exercises in every week.
00:02:15 You can access the system using a cloud provider and SAP Cloud Appliance Library. You
can find more information on the system information page of this course.
00:02:29 In each week, there will be seven units, and at the end of each video, there will be a self-
test to help you measure your learning progress.
00:02:38 At the end, you will, as always, have the chance to take the final exam. With that, we come
to the end of the course introduction.
00:02:50 In the next unit, I will present to you the topic SQL query processing in SAP HANA. Looking
forward to meeting you there.
00:03:00 Goodbye.
Week 1 Unit 2

00:00:05 Hello again. Today is unit two of week one,


00:00:08 where we will focus on SQL query processing in SAP HANA. First of all, we'll discover
HANA query processors
00:00:17 and then check how SQL query is processed with these query processors. We'll also learn
about SQL plan cache.
00:00:27 This diagram shows HANA query processors. When SQL comes into HANA, it goes through
SQL front end,
00:00:35 SQL optimizer, then, depending on the generated plan, it goes through row store or column
store engine.
00:00:44 Let's find out how a simple SQL statement is processed in SAP HANA. Here is a simple
SQL statement.
00:00:52 It has a simple INNER JOIN between table 1 and table 2. When the query comes into HANA
from the application,
00:01:02 firstly, it looks up SQL plan cache to see whether there is any stored plan. We'll discover
more about SQL plan cache in a later slide.
00:01:12 After that, it goes to SQL front end. At SQL front end, the statement is syntactically and
semantically checked.
00:01:21 If you see a syntactic error or not authorized, data type mismatch, object does not exist
errors,
00:01:28 that is the case when the statement fails in the SQL front end, even before it gets into SQL
optimizer.
00:01:37 Most of the optimization is done on the query optimizer tree, and it builds the execution
plan, which is later stored in the SQL plan cache.
00:01:48 The degree of its optimization is bound to premade rules and arithmetic cost of computation,

00:01:55 and this is a restricted by subsequent optimization conducted inside execution engine.
Depending on the query execution plan, a row or column store engine takes over the job.
00:02:10 As a row store engine, there is SQL Engine. As column store engines, there are calculation
engine, OLAP engine, and JOIN engine.
00:02:21 Those engines have their own optimizers and caches. That is why the logical plan does not
tell you much about
00:02:29 how the execution actually goes at the end, especially when there are multiple JOINs
involved.
00:02:37 Now we'll discover how a simple statement is processed with a case when the plan cache is
not found.
00:02:47 As you can see, when there is no plan cache entry found, at the plan cache lookup stage, it
goes to SQL front end
00:02:55 and checks whether the statement is syntactically and semantically correct. If everything is
okay, then it goes to SQL optimizer.
00:03:06 At the SQL optimizer stage, it goes to query optimizer tree and query execution plan. The
optimized plan is generated at this step.
00:03:18 With the generated plan, the appropriate execution engine is chosen. Now we are going to
look at simple SQL statement processing,
00:03:31 or the case when there is plan cache entry found. Once the plan cache entry is found, it
does not have to go through the optimizer.
00:03:43 It just calls the stored plan and uses the plan to execute the statement. After that,
depending on the plan,
00:03:50 it goes to row store engine or column store engine. Now, let's look at more about SQL plan
cache.
00:04:00 Its SQL statement is compiled to a plan before execution. Once the plan is compiled,

2
00:04:07 it is better to use the same plan instead of compiling plans every time. Whenever execution
of the statement is requested,
00:04:16 HANA checks the SQL plan cache to see if there is plan already compiled or not. If a match
is found, we use the cached plan,
00:04:25 otherwise, SQL is compiled and the generated plan is cached. There is a monitoring view
called M_SQL_PLAN_CACHE,
00:04:36 and you can find useful information in the monitoring view. As plan cache identifiers, there
are USER_NAME, SESSION_USER_NAME,
00:04:45 SCHEMA_NAME, STATEMENT_STRING, and STATEMENT_HASH. As information
columns, there are EXECUTION_COUNT, PREPARATION_COUNT,
00:04:55 LAST_EXECUTION_TIMESTAMP, and LAST_PREPARATION_TIMESTAMP. So these
columns are very useful when you encounter perfomance issues,
00:05:06 especially related to compilation. By looking up M_SQL_PLAN_CACHE monitoring view,
00:05:14 you can find when the plan was compiled and executed and whether the plan is still valid or
not.
00:05:22 And this is an example of a usage of monitoring view M_SQL_PLAN_CACHE. If you know
the specific statement, user name, or host,
00:05:33 you can search the plan cache using the wildcard. And please be aware that your search
string does not include angle brackets
00:05:41 when you search the plan cache. The angle brackets in this example is just to show that
00:05:48 you can replace the string as your search string. With the information from
M_SQL_PLAN_CACHE,
00:05:55 you can search other useful information in other monitoring views, such as
M_EXPENSIVE_STATEMENT or M_EXECUTED_STATMENT.
00:06:06 That was about SQL query processing in SAP HANA. In the next unit, I'll talk about the topic
SQL query order of execution.
00:06:18 Thank you for your attention. See you.

3
Week 1 Unit 3

00:00:05 Welcome to unit three of week one. In this unit, I will present SQL query order of execution
00:00:13 and explain how we can convert queries in the optimizer tree. Let's find out the general SQL
query execution order.
00:00:23 In general SQL query execution order, firstly FROM and JOIN are handled in order to get
the base data by selecting and joining the tables.
00:00:35 After that, WHERE is processed, that is, it filters the base data.
00:00:41 And GROUP BY clause is performing in order to aggregate the base data. After that,
HAVING clause is to filter the aggregated data,
00:00:52 then SELECT clause returns the final data. And DISTINCT removes the duplicated values
marked as DISTINCT.
00:01:01 Lastly, using ORDER BY sorts the final data. Now we'll check out the execution order with
an example query.
00:01:12 Based on the SQL execution order, you get the base data, FROM and JOINs are processed
first.
00:01:20 Here, there is the main FROM clause, INNER JOIN, LEFT OUTER JOIN, and INNER JOIN.
Those operators are persisted at the very beginning.
00:01:32 Next, the WHERE clause is handled in order to filter the base data. Here, the filter is
A.COL3=20.
00:01:44 After that, the SELECT clause is processed in order to get final data. Now, we know that
SQL execution for the given query is done in the following order,
00:01:56 FROM, JOIN, WHERE, and SELECT clauses. Here, we are going to draw the optimizer tree
with the previous information.
00:02:09 We express INNER JOIN as IJ, table as T, LEFT OUTER JOIN as LOJ,
00:02:14 and GROUP BY as G. Please keep these in mind and let's draw the optimizer tree based on
SQL execution order.
00:02:26 We know that FROM and JOINs are process first of all, so here in this example, the first
JOIN is INNER JOIN between table 1 and 3
00:02:38 with a JOIN key A.COL3 = D.COL2. Now we'll draw the second JOIN,
00:02:46 which is LEFT OUTER JOIN between table 2 and another subquery. And there is next the
INNER JOIN between table 3 and subquery
00:02:58 with the JOIN key E.COL2 = G.COL2. Now there is the remaining part, which is GROUP
BY,
00:03:08 F.COL1 and F.COL2 from table 4. Now, we can finalize the optimizer tree with the
information of SQL execution order.
00:03:20 As you can see, there are three JOINs involved and we can tell the data is generated from
the bottom.
00:03:28 That's it for unit three. Please join in unit four, my colleague, Jinyeon Lee,
00:03:33 will explore a hands-on session on query execution with you. Thank you for your attention.
00:03:40 Goodbye.

4
Week 1 Unit 4

00:00:05 Hello, and welcome to unit four of week one. My name is Jinyeon Lee and I'm an SAP
HANA database product expert.
00:00:13 I will present a hands-on session about simple query execution. For our hands-on exercise,

00:00:19 you can access the system using a cloud provider and SAP Cloud Appliance Library, you
can find more information on that in the system information page of this course.
00:00:33 We will create a sample table first and run a simple query. After that, we will check
EXPLAIN_PLAN and M_SQL_PLAN_CACHE monitoring view.
00:00:48 Here are SQL commands to create tables. When you set the schema name,
00:00:53 please be aware that your schema name does not include angle brackets. This is the SQL
statement that you will run.
00:01:05 Now let's have a look at the explain plan of this query. As you can see, the query was
processed through the multiple JOINs
00:01:15 and the filter on column COL3 was applied, and it was executed by the column engine.
Now, let's a look at M_SQL_PLAN_CACHE.
00:01:29 In order to check M_SQL_PLAN_CACHE, I use this statement. In this statement, statement
hashes is a hash value of our query string
00:01:39 and it is very useful information for other monitoring views. The execution count shows how
many times the query was executed.
00:01:49 And preparation count shows how many times it was compiled. With the last execution
timestamp,
00:01:55 we could know the timeline of when the query was last executed. And with the last
preparation timestamp,
00:02:03 we also know when the query was last compiled. You can find the result of your query easily
by specifying
00:02:12 schema name and statement string filters and ORDER BY clause of last preparation
timestamp.
00:02:22 As you can see, in the results of M_SQL_PLAN_CACHE, both the execution and
preparation counts are 1,
00:02:31 and the parameter count is 0, since this is a literal query. That is, it was compiled once
00:02:39 and the compiled query plan was successfully stored in SQL plan cache. Now we will learn
about how the SQL hint
00:02:50 IGNORE_PLAN_CACHE works in query execution. You might be familiar with this SQL hint
while you are investigating issues.
00:03:00 We are going to run this query with the IGNORE_PLAN_CACHE hint and check
M_SQL_PLAN_CACHE.
00:03:11 Okay, before we look at the result of M_SQL_PLAN_CACHE with the SQL hint
IGNORE_PLAN_CACHE,
00:03:17 let's find out how this hint works. The diagram on the left-hand side shows general query
processing.
00:03:25 So when a query comes into HANA, it checks SQL plan cache to see whether its plan cache
entry exists.
00:03:33 If yes, it goes to the execution engine straight away to execute the query with the stored
plan. If not, it goes through SQL front end
00:03:45 and checks the query semantics and goes to SQL optimizer to generate a plan. Then it
goes to the execution engine to execute the query.
00:03:56 Now, let's look at the right-hand side diagram. When the query comes into HANA with the
SQL hint IGNORE_PLAN_CACHE,
00:04:07 it does not check plan cache, that is it skips the plan cache lookup. It directly goes through
the SQL front end and goes to SQL optimizer.

5
00:04:20 When the query is generated, the plan is not stored in plan cache and goes to proper
execution engines.
00:04:30 Now, let's have a look at the results of a query with IGNORE_PLAN_CACHE hint. As you
can see, there is no entry found in M_SQL_PLAN_CACHE.
00:04:41 It is because the plan was not stored in plan cache when the plan was generated. Thank
you for your attention.
00:04:51 For later training in unit five, my colleague Helen Shin will explore SQL optimizer
architectural overview with you.

6
Week 1 Unit 5

00:00:05 Hello, and welcome to unit five, SQL optimizer – architectural overview. My name is Helen
Shin, and I will present this unit to you.
00:00:15 To recap the HANA processors, HANA checks whether there is any stored plan in SQL plan
cache first
00:00:24 and it parses to SQL front end if there is no stored plan cache entry. And if there is no plan
cache entry found,
00:00:34 the query string is checked, to see whether it is syntactically and semantically okay at the
SQL front end layer, after that it goes to SQL optimizer.
00:00:47 SQL optimizer tries to make the optimal plan within a certain amount of time. Then, based
on the plan, the execution engines handle the execution.
00:01:00 Let's move on to how the optimizer generates the enumerate alternatives. So, the query
comes into HANA.
00:01:11 If there is an entry stored in SQL plan cache, it uses the stored plan. Otherwise, it goes to
the SQL front end layer
00:01:19 in order to check the query string is syntactically and semantically okay. If everything's fine,
then it goes to SQL optimizer.
00:01:34 I've said that SQL optimizer generates the optimal plan within a certain amount of time. And
in SQL optimizer, there are two optimization steps,
00:01:46 which are rule-based optimization and cost-based query optimization. In the rule-based
optimization step, the predefined, proven rules are applied
00:01:58 in order to simplify the plan and lower the costs. However, in rule-based optimization,
00:02:06 the size estimation and cost comparison are not conducted. And as part of the query
simplification,
00:02:15 there are filter pushdown, JOIN removal, or simplify GROUP BY. Filter pushdown is to push
down filters.
00:02:27 JOIN removal is to remove unnecessary JOINs. And simplify GROUP BY is also to remove
unnecessary GROUP BYs.
00:02:39 Applying various predefined rules can be
repeated several times during one query compilation.
00:02:48 Also, the simplification may include other measures, like adding more filters or removing
operators
00:02:57 and even adding more operators to make the optimization more efficient. Therefore, the
query plan can be either
00:03:08 shorter or longer while the later optimization steps are being compiled. In cost-based query
optimization,
00:03:19 it finds candidates from the plan generated in rule-based optimization. The cost of every
alternative is calculated
00:03:30 and the one with the best cost, which is the cheapest one, is chosen. Now let's have a look
at how the optimizer generates the optimal plan.
00:03:44 Firstly, it goes through predefined rules, which is sequentially applied to the initial tree.
00:03:57 Let's assume this is the initial tree. In rule-based optimization, various predefined rules are
applied.
00:04:05 For example, if filter pushdown is applied, then, like in this case, the filter is pushed down.
00:04:16 When the simplify GROUP BY is applied, an unnecessary GROUP BY is removed.
00:04:24 And when the remove JOIN rule is applied, an unnecessary JOIN is removed. After all these
predefined rules are applied, a rewritten tree is generated.
00:04:42 Now this tree goes to cost-based query optimization. As I mentioned, the tree from the rule-
based optimization

7
00:04:53 is sent to cost-based query optimization. At the cost-based query optimization stage,
various enumerators
00:05:07 such as A_THRU_B or PRE A_BEFORE_B are applied and various plans can be
generated.
00:05:17 These are all different query plans. We can also control enumerators by applying hint.
00:05:31 A_THRU_B enumerators literally moved the first operator, A, through B and positions A
below B in the plan tree.
00:05:44 This is very effective when A reduces the data more than B does. There are
AGGR_THRU_JOIN, JOIN_THRU_JOIN,
00:05:56 and JOIN_THRU_AGGR, and so on. For example, let's suppose that there is an
aggregation over a JOIN.
00:06:06 And this JOIN is very heavy because it is a LEFT OUTER JOIN with a range of JOIN
conditions on multiple timestamp columns.
00:06:16 Then the SQL optimizer tries to push down the aggregation through the JOIN so that it can
reduce the data set of one of the JOIN candidates.
00:06:28 This is AGGR_THRU_JOIN. and JOIN_THRU_JOIN basically switches the order of the
JOIN.
00:06:39 How effective this is will depend on the data size involved. The most well-known enumerator
of this
00:06:49 type of PRE A_BEFORE_B is PREAGGR_BEFORE_JOIN. PREAGGR_BEFORE_JOIN is
a preaggregation.
00:07:01 This means it adds a preaggregation to one of the JOIN candidates so that the size of the
JOIN can be reduced.
00:07:11 In this case, the aggregation of the JOIN is called post aggregation. Okay, let's go back to
cost-based query optimization.
00:07:24 Let's assume the costs are as follows. The coin icons represent the cost for the plan.
00:07:36 As you can see, among those plans, we know that plan 3 is the most inexpensive one.
00:07:46 Therefore, plan 3 is selected out of all this optimizations. That's SQL Optimizer architectural
overview.
00:07:58 In the next unit, I will talk about the topic parameter optimization. Thank you for your
attention.
00:08:07 Looking forward to meeting you in the next unit. See you.

8
Week 1 Unit 6

00:00:05 Hello, and welcome to unit six of week one. Today, I will talk about the topic of the
parameter-aware optimization.
00:00:16 There are important check points regarding parameterized queries. The first thing is the
parameterized query is compiled twice, not like a literal query.
00:00:29 So the first compilation is called precompilation and second compilation is called
recompilation.
00:00:38 A precompiled plan is a compiled plan without a bind variable, and a recompiled plan is a
compiled plan with a bind variable.
00:00:50 The most important thing here is the precompiled plan is not equal to the recompiled plan.
00:00:57 So what we have to see is a recompiled plan. This is very important in performance issue
analysis
00:01:06 because sometimes we investigate the issue with the precompiled plan but it is not the
correct one that caused the issue.
00:01:16 Therefore, we always have to check the recompiled plan. Another important point is
different plans can be generated
00:01:27 depending on which values are used for the compilation. Let's assume there is a
parameterized query.
00:01:39 When the plan is compiled with the bind variable A, as you can see, the left-hand side plan
is generated.
00:01:46 However, when the plan is compiled with the bind variable B, the right-hand side plan is
generated.
00:01:56 Now we're going to look at the parameterized query processing. We'll look into two cases
like simple query processing.
00:02:05 The first case is when there is no plan cache found. Up to this step, it is similar to simple
query processing.
00:02:20 Now there is a difference, which is precompilation. As I mentioned earlier, at precompilation
step, the plan is compiled without a bind variable.
00:02:36 After it is precompiled, the bind variable is entered. Here is the important thing.
00:02:42 The plan is now recompiled. When the parameterized query is recompiled, it is recompiled
only once
00:02:52 and there will be no more recompilation even for other values. That is, for this case, when
the plan is compiled, the bind variable A is used.
00:03:04 Now the plan with the bind variable is stored in SQL plan cache. This plan will be used for
other bind variables.
00:03:15 This is why we often have a slow-running parameterized query, especially with different
parameter values.
00:03:23 Therefore, it is very important to consider which values are used when it is compiled for the
parameterized query.
00:03:35 Now, let's look at the case when there is plan cache entry is found and And the plan was
compiled with the bind variable A.
00:03:46 Like simple SQL processing case, it doesn't have to go through the optimizer. Firstly, it
checked the plan cache lookup and found plan cache entry.
00:03:58 Then it used the stored plan to execute the parameterized query. Let's look at the case
when bind variable B is entered.
00:04:13 Even though the bind variable B is entered, in the SQL plan cache, the plan with bind
variable A is stored,
00:04:22 Therefore, the execution is done with the plan with bind variable A, not B. So, to summarize,
as a parameterized query is a feature
00:04:34 used to execute the same or similar SQL statements repeatedly with high efficiency, it takes
a form of template where the bind values are substituted during each execution.

9
00:04:49 Unlike literal query, the parameterized query compiles twice. First the compilation is without
bind variables,
00:04:59 and recompilation is with bind variables. Let's look at the example.
00:05:07 Here is a parameterized query with a bind variable 20. And when you check the PlanViz,
you can also check bind variable, as shown.
00:05:17 To check the precompilation and recompilation status, we can use monitoring view
M_SQL_PLAN_CACHE
00:05:26 by searching execution count and preparation count. We know the exact statement string,
so we can search
00:05:37 M_SQL_PLAN_CACHE with statement string. So here is the statement to find and check
precompilation and recompilation status.
00:05:50 We know the parameterized query compiles twice, and as you can see, the preparation
count is 2.
00:05:59 However, the execution was done only once, therefore the execution count is 1. Okay, let's
try a different bind variable for the same parameterized query.
00:06:16 Here we use the bind variable of 30. Now after execution, let's check monitoring view again.

00:06:26 Previously, the preparation count was 2 and the execution count was 1. Now, this is the
result of the parameterized query with the bind variable of 30.
00:06:40 Here, the preparation count is 2 and the execution count is also 2. So that means the
previous parameterized query plan is stored M_SQL_PLAN_CACHE,
00:06:55 and while we are executing this parameterized query with bind variable of 30, it does plan
cache lookup and uses the stored plan to execute the query.
00:07:11 With that, we come to the end of unit six. Thank you for your attention, and please join in
unit seven,
00:07:19 my colleague Jinyeon Lee will explore a hands-on session about understanding parameter-
aware optimization with you.
00:07:30 Bye.

10
Week 1 Unit 7

00:00:05 Hello, and welcome to unit seven, the last unit of week one. I'm Jinyeon Lee and I will
present a hands-on session
00:00:13 about understanding parameter-aware optimization. Like the previous hands-on exercise,
00:00:22 you will use a cloud provider and SAP Cloud Appliance Library. Firstly, you will run a
parameterized query.
00:00:30 Then check EXPLAIN PLAN and monitoring view M_SQL_PLAN_CACHE. We will use
tables created in unit four, so you don't have to create them again.
00:00:45 There is one thing that you should know when executing a parameterized query. Our
recommendation when the user runs the parameterized query
00:00:55 is to make the parameterized query into a single line. This is because different carriage
returns across the interfaces
00:01:04 often interfere with the usage of plan cache. The easiest way to make one single line of a
parameterized query
00:01:13 is to just copy and paste the parameterized query into the navigation bar on an Internet
browser.
00:01:24 This is a single-line parameterized query. You can easily make a query into a single line
using a browser's address bar.
00:01:34 You can copy the parameterized query and paste it into a browser's address bar, and re-
copy it again to execute it.
00:01:44 Then the parameterized query will be displayed like this single line query. Here you can find
out the characteristics of a parameterized query.
00:02:01 The parameterized query is compiled twice, which is precompilation and recompilation. The
first compilation is to compile the query without bind variables.
00:02:14 And the second compilation is to compile the query with bind variables. Here you can see,
the precompiled plan is different from the recompiled plan.
00:02:29 So if you are seeing the first EXPLAIN PLAN, you are not looking at the correct EXPLAIN
PLAN for the parameterized query.
00:02:38 Therefore, it is very important to see the recompiled plan for a parameterized query when
you analyze the parameterized query performance issue.
00:02:49 Now, let's have a look at M_SQL_PLAN_CACHE for parameterized query. Like the previous
simple query hands-on case,
00:02:59 you can search the statement by statement string since you are aware of the query string.
00:03:08 This is the result of a parameterized query in M_SQL_PLAN_CACHE. If you see the
preparation count is 1,
00:03:16 then you are looking at the result of a precompiled parameterized query in monitoring view
M_SQL_PLAN_CACHE.
00:03:25 Since a parameterized query is compiled twice, precompilation and recompilation, the
preparation count should be 2.
00:03:35 Please make sure you see the recompiled plan for performance issue analysis. With this, I
will come to the end of week one.
00:03:48 Thank you for your attention. Next week, we will discover column search and analysis tools
in HANA.
00:03:56 Looking forward to meeting you. Bye.

11
www.sap.com/contactsap

© 2020 SAP SE or an SAP affiliate company. All rights reserved.


No part of this publication may be reproduced or transmitted in any form or for any purpose without the express permission of SAP SE or an SAP affiliate company.

The information contained herein may be changed without prior notice. Some software products marketed by SAP SE and its distr ibutors contain proprietary software components of other software vendors.
National product specifications may vary.

These materials are provided by SAP SE or an SAP affiliate company for informational purposes only, without representation or warranty of any kind, and SAP or its affiliated companies shall not be liable
for errors or omissions with respect to the materials. The only warranties for SAP or SAP affiliate company products and serv ices are those that are set forth in the express warranty statements
accompanying such products and services, if any. Nothing herein should be construed as constituting an additional warranty.

In particular SAP SE or its affiliated companies have no obligation to pursue any course of business outlined in this document or any relat ed presentation, or to develop or release any functionality
mentioned therein. This document, or any related presentation, and SAP SE’s or its affiliated companies’ strategy and possibl e future developments, products, and/or platform directions and functionality are
all subject to change and may be changed by SAP SE or its affiliated companies at any time for any reason without notice. The information in this document is not a commitment, promise, or legal obligation
to deliver any material, code, or functionality. All forward-looking statements are subject to various risks and uncertainties that could cause actual results to differ materially from e xpectations. Readers are
cautioned not to place undue reliance on these forward-looking statements, and they should not be relied upon in making purchasing decisions.

SAP and other SAP products and services mentioned herein as well as their respective logos are trademarks or registered trade marks of SAP SE (or an SAP affiliate company) in Germany and other
countries. All other product and service names mentioned are the trademarks of their respective companies. See www.sap.com/copyright for additional trademark information and notices.

You might also like