If I would have received a cent for each time in my career I have seen a select within a select within a select, I would probably be in the same financial situation as I am now. However, if I would receive a cent for every time someone executes a subselect in a subselect in the whole world – then I would definitely be in a different financial position.
But what if there was a way to avoid the draconian query in query? wait… that exists ! allow me to present – the anonymous SQL block.
What is it?
An anonymous block is a stored procedure that it is not persisted in the database. it is a procedure which is created at the start of the runtime and destroyed at the end. Because it does not get stored, it does not require a name – making them “anonymous”
Anyone who has worked with stored procedures knows that they are a very strong database tool, which can simplify SQL queries quite a bit. it would however be overkill to create a “named” stored procedure when you are just hunting for some data – That’s where the anonymous block comes in
What can I do with it?
One strong functions of an anonymous block is that it allows you to store intermediate results in a temporary variable on top of which you can launch another select.
practical example
The finance department has a table with the amount which needs to be payed by a certain date. This table can also contain amounts for future dates.
TO_BE_PAYED_DATE (YYYYMMDD) | AMOUNT |
---|---|
20210101 | 50 |
20210102 | 60 |
20210103 | 70 |
20210104 | 80 |
now they want to see the data a bit different:
1. if the TO_BE_PAYED_DATE is older then yesterday they want to see the 75% of the amount
2. if the TO_BE_PAYED_DATE is yesterday they want to see the 75% of the amount
3. if the TO_BE_PAYED_DATE is on today, they want to see half the amount
4. if the TO_BE_PAYED_DATE is on a future date, they want to 0 instead
As with any programming language (yes, using the term liberally here 🙂 ), it is not rocket science. There are a different ways we could achieve this result. A simple case statement would suffice here:
select
TO_BE_PAYED_DATE,
AMOUNT,
case
when TO_BE_PAYED_DATE < ADD_DAYS(current_date,-1)
when TO_BE_PAYED_DATE = ADD_DAYS(current_date,-1) then AMOUNT
when TO_BE_PAYED_DATE = current_date() then AMOUNT/ 2
when TO_BE_PAYED_DATE > current_date() then 0
else 0 -- always supply a default case
end case as NEW_AMOUNT
from PAYMENTS_EXPECTED;
Quick & easy!
You give the results to your finance department and they are happy. But seeing this code you have the linger feeling: “Can I make it look cleaner?“.
let’s see how we can write it cleaner in an anonymous block by:
– Declaring a variable to store yesterdays date
– Declaring a variable to store todays date
– Using these variables in the SQL script
to start, we need to declare our anonymous block
DO
BEGIN
--- code goes here ---
END;
now let’s fill it up with our code:
DO
BEGIN
-- declare and fill our date variables
declare today date = current_date;
declare yesterday date = ADD_DAYS(current_date,-1);
-- selecting our end result
select
TO_BE_PAYED_DATE,
AMOUNT,
case
when TO_BE_PAYED_DATE < :yesterday then amount
when TO_BE_PAYED_DATE = :yesterday then AMOUNT * 0.75
when TO_BE_PAYED_DATE = :today then AMOUNT * 0.5
when TO_BE_PAYED_DATE > :today then 0
else 0 -- always supply a default case
end case as NEW_AMOUNT
from PAYMENTS_EXPECTED;
END;
Notice how the code instantly becomes more readable!
It doesn’t stop with readability, it can also make your life easier. Lets demonstrate that in our next example based of our current result.
The finance department comes back and ask us if we can create “buckets” for the values (after conversion):
– bucket for amount between < 25
– bucket for amount between 25 and 50
– bucket for amounts > 50
In the classic SQL example we would have to start working with subqueries. I’m not going to write the classic example out here, because then I would owe myself a cent :).
Lets see how we can solve this with our Anonymous block!
we can simply store the result of our previous query in a virtual temporary table and do a query on top of that virtual temporary table.
DO
BEGIN
-- declare and fill our date variables
declare today date = current_date;
declare yesterday date = ADD_DAYS(current_date,-1);
-- store the result in table variable (tv)
tv_temp_table =
select
TO_BE_PAYED_DATE,
AMOUNT,
case
when TO_BE_PAYED_DATE < :yesterday then amount
when TO_BE_PAYED_DATE = :yesterday then AMOUNT * 0.75
when TO_BE_PAYED_DATE = :today then AMOUNT * 0.5
when TO_BE_PAYED_DATE > :today then 0
else 0 -- always supply a default case
end case as AMOUNT_NEW
from PAYMENTS_EXPECTED ;
-- select from tv_temp_table and create buckets
select
TO_BE_PAYED_DATE,
AMOUNT,
AMOUNT_NEW,
case
when AMOUNT_NEW < 25 then '<25'
when AMOUNT_NEW <= 50 then '25-50'
else '>50'
end as BUCKET
from :tv_temp_table;
END;
So what are we doing here?
- we calculate todays date and we store it in the variable called “today”
- we calculate yesterdays date and we store it in the variable called “yesterday
- we apply the logic initially provide by our finance departments based on the date
- we store the result of the query in a temporary virtual table called “tv_temp_table”
- because the result is stored in a variable, it will not be outputted to the screen
- we do a new select on the data in the “tv_temp_table” and put the amounts from our initial query in buckets
- as we do not store this result in a variable, it will be outputted on the screen
The final result will look like this if today is 2021-01-03:
TO_BE_PAYED_DATE | AMOUNT | AMOUNT_NEW | BUCKET |
---|---|---|---|
20210101 | 50 | 50 | >50 |
20210102 | 60 | 45 (60*0.75) | 25-50 |
20210103 | 70 | 35 (70*0.5) | 25-50 |
20210104 | 80 | 0 | <25 |
closing word
Hopefully this simple example demonstrates the power of using Anonymous blocks. It can be used not only to make code more readable, but is also a powerful way of wrangling data and avoiding nested selects in a query. It can also be used to test BW AMDP transformations, but more on that another time.
Further reads
Official HANA documentation on anonymous blocks:
https://help.sap.com/viewer/4fe29514fd584807ac9f2a04f6754767/2.0.03/en-US/52103853351841f6a1e87f40b2686da9.htm
Tutorial (in HANA Studio):
http://www.bestsaphanatraining.com/how-to-run-sap-hana-sql-script-without-a-function-or-a-procedure-t4.html
Tutorial (WEB IDE)
https://developers.sap.com/tutorials/xsa-sqlscript-anonymous.html