SlideShare a Scribd company logo
SQL Macros – Game Changing Feature
für SQL Entwickler?
Andrej Pashchenko
@Andrej_SQL https://blog.sqlora.com
About me
• Working at Trivadis Germany, Düsseldorf
• Focusing on Oracle:
• Data Warehousing
• Application Development
• Application Performance
• Course instructor „Oracle New Features for
Developers“
@Andrej_SQL blog.sqlora.com
SQL Macros - Game Changing Feature for SQL Developers?
Why SQL macros?
Motivation
It’s a good idea to encapsulate
some complex logic and make
it reusable
We have enough tools to do
so, don’t we?
• Views
• WITH-Subqueries
• PL/SQL Functions
• Polymorphic Table Functions
Why do we need something
else?
Motivation
• Performance
• Context switch with PL/SQL functions
• The same data structures are often accessed from different PL/SQL functions called
from the same SQL
• The optimizer having no clue what happens within PL/SQL
• Lack of control
• No parameters for views
• Binds or literals used
• Read-Consistency:
• SQL calling PL/SQL calling SQL calling PL/SQL calling SQL calling PL/SQL …
Motivation
• SQL Macros are functions where your (SQL) logic goes in
• will NOT be executed at runtime (no context switch!)
• will be executed once at parse time and return a piece of SQL code which will be
incorporated in your SQL query.
• enable to create reusable building blocks used to build complex SQL statements
• hiding the unnecessary complexity from developer
• but exposing all useful information to the optimizer
Motivation
SQL Macro Basics
• SQL macro is just a function returning text (CHAR, VARCHAR2, CLOB)
• Whatever your complex logic inside the function is, you return a piece of SQL code at the end
SELECT e.*
, FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12) as years
FROM emp e
WHERE FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12) > 38;
CREATE OR REPLACE FUNCTION job_duration RETURN VARCHAR2
SQL_MACRO(SCALAR) AS
BEGIN
RETURN 'FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12)';
END;
First SQL Macro
• How can I hide the calculation?
SQL> SELECT ename
, job_duration
as years
FROM emp e
WHERE job_duration > 38
First SQL Macro
• Calling SQL macro function
SQL> SELECT ename
, FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12)
as years
FROM emp e
WHERE FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12 > 38
ENAME YEARS
---------- ----------
SMITH 39
ALLEN 39
WARD 39
3 rows selected.
SQL you write
SQL macro
executed while
parsing
SQL is
translated
into
SQL actually executed
SQL> SELECT ename
, FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12) as years
, FLOOR(MOD(MONTHS_BETWEEN (SYSDATE, hiredate),12)) as months
FROM (SELECT * FROM emp WHERE job = 'ANALYST' ) e
WHERE FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12) > 38
GROUP BY ename
, FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12)
, FLOOR(MOD(MONTHS_BETWEEN (SYSDATE, hiredate),12))
HAVING SUM(sal) > 1000
ORDER BY FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12)
+ FLOOR(MOD(MONTHS_BETWEEN (SYSDATE, hiredate),12))
SQL macro 1
SQL macro 2
SQL macro 3
SQL macro 4
Where and how do I call SQL Macro?
• In general, everywhere in a SQL statement where a function call is allowed
Where and how do I call SQL Macro?
• Give the function meaningful names and here you go:
SQL> SELECT ename
, duration_years as years
, duration_months as months
FROM emp_only_analysts e
WHERE duration_years > 38
GROUP BY ename
, duration_years
, duration_months
HAVING sum_sal > 1000
ORDER BY duration_years
+ duration_months
Won’t work!
Column alias,
condition
Won’t work!
More than one
expression
Won’t work!
Condition
Where and how do I call SQL Macro?
• You cannot “break” the structure
• Only take up as much of SQL as a normal function
SQL> SELECT ename
, FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12) as years
, FLOOR(MOD(MONTHS_BETWEEN (SYSDATE, hiredate),12)) as months
FROM (SELECT * FROM emp WHERE job = 'ANALYST' ) e
WHERE FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12) > 38
GROUP BY ename
, FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12)
, FLOOR(MOD(MONTHS_BETWEEN (SYSDATE, hiredate),12))
HAVING SUM(sal) > 1000
ORDER BY FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12) +
FLOOR(MOD(MONTHS_BETWEEN (SYSDATE, hiredate),12))
SQL> SELECT ename
, job_duration as years
FROM emp e
WHERE job_duration > 38
ENAME YEARS
---------- ----------
SMITH 39
ALLEN 39
WARD 39
3 rows selected.
What Types of SQL Macros are there?
• Used in SELECT, WHERE, GROUP BY, …
SELECT e.hash_diff
, e.empno
, e.ename
FROM add_hash_columns (emp) e;
HASH_DIFF EMPNO ENAME
---------- ---------- ----------
42CB6932B4 7369 SMITH
AA63299F72 7499 ALLEN
27332DD16B 7521 WARD
3 rows selected.
• Used in FROM clause
Scalar SQL macro Table SQL macro (default)
CREATE FUNCTION job_duration (<optional parameters>)
RETURN VARCHAR2 | CHAR | CLOB
SQL_MACRO(SCALAR|TABLE)
AS
BEGIN
<.. Your business logic here .. >
RETURN 'FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12)';
END;
How to define a SQL macro function?
• The function must return CHAR, VARCHAR2 or CLOB
• Using a new keyword SQL_MACRO
This makes a
regular function
a SQL macro
The result of a function becomes
a part of the calling SQL
TABLE is default
and can be
omitted
When can I use SQL Macros?
• SQL Macros have been introduced in Oracle 20c
• Only Preview Version of 20c in Oracle Cloud as of now
• (Only) Table SQL Macros have been backported to 19c (19.8)! You can start to test them!
• Officially documented and supported
• You cannot specify the type of a macro, but since TABLE is default – no problem migrating
to 20c later
CREATE FUNCTION job_duration (<optional parameters>)
RETURN VARCHAR2 | CHAR | CLOB
SQL_MACRO(SCALAR|TABLE)
AS
BEGIN
RETURN 'FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12)';
END;
Parameters, Parsing
Passing Parameters
• You can pass scalar parameters and use them in return string
• But don’t concatenate
CREATE FUNCTION duration (p_date IN DATE)
RETURN VARCHAR2
SQL_MACRO(SCALAR)
AS
BEGIN
RETURN 'FLOOR(MONTHS_BETWEEN (SYSDATE, duration.p_date)/12)';
END;
exec dbms_output.put_line( duration (DATE '2020-02-26') );
PL/SQL procedure successfully completed.
FLOOR(MONTHS_BETWEEN (SYSDATE, duration.p_hiredate)/12)
What is a parameter inside a
quoted string good for?
Calling outside of SQL it just
makes no sense, but…
Passing Parameters
• When calling in SQL, the parameter you pass seems just to be substituted
in the return statement of the function (result string)
SQL> SELECT ename, duration (hiredate) as years
FROM emp e
WHERE duration (hiredate) > 38
...
SQL> select * from dbms_xplan.display_cursor()
...
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 3 (100)| |
|* 1 | TABLE ACCESS FULL| EMP | 1 | 14 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(FLOOR(MONTHS_BETWEEN(SYSDATE@!,INTERNAL_FUNCTION("HIREDATE"))/12)>38)
Passing Parameters
• Passing a column name
SQL> CREATE OR REPLACE FUNCTION new_func (p_param IN VARCHAR2)
RETURN VARCHAR2 SQL_MACRO(SCALAR) AS
BEGIN
RETURN 'UPPER(p_param)';
END;
Function created.
SQL> SELECT count(*) FROM emp e WHERE new_func(ename) = 'SCOTT'
...
SQL> select * from dbms_xplan.display_cursor()
...
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(UPPER("ENAME")='SCOTT')
Passing Parameters
• Passing a literal: the optimizer may see that the condition can never be true
SQL> SELECT count(*) FROM emp e WHERE new_func('ADAMS') = 'SCOTT'
...
SQL> select * from dbms_xplan.display_cursor()
--------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | 1 (100)| |
| 1 | SORT AGGREGATE | | 1 | | |
|* 2 | FILTER | | | | |
| 3 | INDEX FULL SCAN| PK_EMP | 14 | 1 (0)| 00:00:01 |
--------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(NULL IS NOT NULL)
Passing Parameters
• Passing bind variables: the bind variable is used in a calling SQL
SQL> SELECT count(*) FROM emp e WHERE new_func(:bind) = 'SCOTT'
...
SQL> select * from dbms_xplan.display_cursor()
--------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | 1 (100)| |
| 1 | SORT AGGREGATE | | 1 | | |
|* 2 | FILTER | | | | |
| 3 | INDEX FULL SCAN| PK_EMP | 14 | 1 (0)| 00:00:01 |
--------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(UPPER(:BIND)='SCOTT')
Passing Parameters
• don’t concatenate function parameter into the result string like
RETURN 'UPPER('||p_param||')'
• don’t use parameter as bind variable in a result string like
RETURN 'UPPER(:p_param)'
• just reference the parameter in a literal string to be returned, you can optionally
prefix it with a function name if conflicting with other names
RETURN 'UPPER(new_func.p_param)'
• this reference will be replaced with column name, bind variable, function or literal,
whatever was used to invoke a SQL macro function
Parameters inside a function
• To protect against SQL injection all string parameters are NULL inside a function
CREATE OR REPLACE FUNCTION f_test_par (p_num NUMBER, p_str VARCHAR2)
RETURN VARCHAR2 SQL_MACRO(SCALAR) IS
BEGIN
DBMS_OUTPUT.PUT_LINE('p_num '||p_num);
DBMS_OUTPUT.PUT_LINE('p_str '||p_str);
RETURN 'p_str||to_char(p_num)' ;
END;
/
SELECT f_test_par(20, 'Oracle ') FROM dual;
F_TEST_PA
---------
Oracle 20
p_num 20
p_str <-- the string 'Oracle’ is not visible inside the function
SQL Macros and Parsing
• What does it mean?
• SQL Macro function will not be called every time the SQL is executed
• It will be executed occasionally when SQL statement gets hard parsed, which you cannot control:
• DDL on involved database objects
• SQL aged out from shared pool, etc.
• You must ensure its deterministic behavior
• Don’t try to make the return string depend on the state in the database
• More on this: https://blog.sqlora.com/en/sql-macros-part-2-parameters-and-parsing/
Table SQL Macros
Table SQL Macros
CREATE OR REPLACE FUNCTION filter_dept RETURN VARCHAR2 SQL_MACRO(TABLE)
IS
BEGIN
RETURN 'SELECT * FROM scott.emp WHERE deptno = 30';
END;
/
• Table SQL macros can be called in FROM clause only
SELECT empno, deptno
FROM filter_dept();
SELECT empno, deptno
FROM (SELECT * FROM emp WHERE deptno = 30);
• Acts just like inline view in this query
• Or even like a regular view because the query is saved in the database
• But in contrast to regular views we can use parameters!
Table SQL Macros – Using Scalar Parameters
CREATE OR REPLACE FUNCTION filter_dept( p_deptno IN NUMBER)
RETURN VARCHAR2 SQL_MACRO(TABLE)
IS
BEGIN
RETURN 'SELECT * FROM emp WHERE deptno = p_deptno';
END;
/
• Just use function parameters in the return string
SELECT empno, deptno
FROM filter_dept(10);
SELECT empno, deptno
FROM (SELECT * FROM emp WHERE deptno = 10);
A literal substitution
of parameters in the
return string
Table SQL Macros – Examples Using Scalar Parameters
SELECT empno, deptno
FROM filter_dept(10)
SELECT empno, deptno FROM
(SELECT * FROM emp WHERE deptno = 10)
SELECT empno, deptno
FROM filter_dept(empno)
SELECT empno, deptno FROM
(SELECT * FROM emp WHERE deptno = empno)
SELECT empno, deptno
FROM filter_dept(:P_DEPT)
SELECT empno, deptno FROM
(SELECT * FROM emp WHERE deptno =:P_DEPT)
SELECT empno, deptno
FROM filter_dept(my_func(100))
SELECT empno, deptno FROM
(SELECT * FROM emp WHERE deptno = my_func(100))
SELECT empno, deptno
FROM filter_dept((SELECT 10
FROM dual))
SELECT empno, deptno
FROM (SELECT * FROM emp
WHERE deptno = ((SELECT 10 FROM dual))
How do I know what’s actually executed?
Table SQL Macros – How to See the Real SQL Statement
SQL> DECLARE
2 l_clob CLOB;
3 BEGIN
4 DBMS_UTILITY.expand_sql_text (
5 input_sql_text => 'SELECT empno, deptno FROM filter_dept(:P_DEPT)',
6 output_sql_text => l_clob );
7 DBMS_OUTPUT.put_line(l_clob);
8 END;
9 /
SELECT "A1"."EMPNO" "EMPNO","A1"."DEPTNO" "DEPTNO" FROM (SELECT "A2"."EMPNO"
"EMPNO","A2"."ENAME" "ENAME","A2"."JOB" "JOB","A2"."MGR" "MGR","A2"."HIREDATE"
"HIREDATE","A2"."SAL" "SAL","A2"."COMM" "COMM","A2"."DEPTNO" "DEPTNO" FROM (SELECT
"A3"."EMPNO" "EMPNO","A3"."ENAME" "ENAME","A3"."JOB" "JOB","A3"."MGR"
"MGR","A3"."HIREDATE" "HIREDATE","A3"."SAL" "SAL","A3"."COMM" "COMM","A3"."DEPTNO"
"DEPTNO" FROM "SCOTT"."EMP" "A3" WHERE "A3"."DEPTNO"=:B1) "A2") "A1"
PL/SQL procedure successfully completed.
• You can use DBMS_UTILITY.expand_sql_text
• But only for table macros!
Table SQL Macros – Polymorphic Views
Using parameters for views is
fine, but must the query
always remain hardcoded?
No! Just use table and
column parameters to create
polymorphic views!
Polymorphic Views and
Polymorphic Table Functions
– is it the same?
No, don’t confuse them. PTF means PL/SQL
execution at runtime, Polymorphic Views via
SQL macros are only executed at parse time
So no need to learn about the
PTF’s right now?
At least you should get to know the table
and column parameters, because they were
introduced and documented in the context
of PTF's
Table SQL Macros – Polymorphic Views
• Pass tables (views, named subqueries) as parameter of type DBMS_TF.TABLE_T
a table of
• E.g. to get the data type of the first
column:
<Param>.column(1).description.type
SELECT *
FROM my_sql_macro_func( emp, COLUMNS(EMPNO, ENAME));
Table SQL Macros – Polymorphic Views
• Pass column lists as a parameter of type DBMS_TF.COLUMNS_T
• The right way to do it is using a variadic pseudo-operator COLUMNS (18c)
Visible as TABLE_T structure
with all column description
etc.
Visible as array "EMPNO",
"ENAME"
CREATE OR REPLACE FUNCTION top_n (p_tab IN DBMS_TF.TABLE_T, p_limit IN NUMBER
, p_order IN DBMS_TF.COLUMNS_T)
RETURN VARCHAR2 SQL_MACRO IS
v_order_list VARCHAR2(2000);
BEGIN
-- turn PL/SQL table to comma separated list for ORDER BY clause
SELECT LISTAGG(column_value,',') INTO v_order_list FROM TABLE (p_order);
RETURN 'SELECT * FROM top_n.p_tab ORDER BY '||v_order_list||
' FETCH FIRST top_n.p_limit ROWS ONLY';
END;
Table SQL Macros – Polymorphic Views
• Implement reusable SQL macro to get Top-N rows from any table using defined sort order
SQL> SELECT deptno, dname, loc
2 FROM top_n(scott.dept, 2, COLUMNS(loc));
DEPTNO DNAME LOC
---------- -------------- -------------
40 OPERATIONS BOSTON
30 SALES CHICAGO
SQL> SELECT empno, ename
2 FROM top_n(scott.emp, 2, COLUMNS(ename));
EMPNO ENAME
---------- ----------
7876 ADAMS
7499 ALLEN
Use Cases
SELECT e.hash_diff
, e.empno
, e.ename
FROM add_hash_columns (emp) e;
HASH_DIFF EMPNO ENAME
---------- ---------- ----------
42CB6932B4 7369 SMITH
AA63299F72 7499 ALLEN
27332DD16B 7521 WARD
3 rows selected.
Generate Hash Keys
• Build MD5 digest of the whole row, often used for row comparison during ETL processes
• There are complex rules to follow, thus one PL/SQL function across all use cases is preferrable
• The most efficient way to generate MD5-Hash is STANDARD_HASH, not available in PL/SQL
CREATE OR REPLACE FUNCTION add_hash_columns(t DBMS_TF.TABLE_T
, key_cols DBMS_TF.COLUMNS_T)
RETURN VARCHAR2 SQL_MACRO(TABLE)
AS
v_hdiff clob ;
v_hkey clob ;
v_str varchar2(200);
v_delimiter varchar2(9):= '||''#''||';
v_name dbms_id;
BEGIN
FOR I IN 1..t.column.count LOOP
v_name := t.column(i).description.name;
IF t.column(i).description.type = dbms_tf.type_varchar2 THEN
v_str := v_name;
ELSIF t.column(i).description.type = dbms_tf.type_number THEN
v_str := 'to_char('||v_name||')';
ELSIF t.column(i).description.type = dbms_tf.type_date THEN
v_str := 'to_char('||v_name||',''YYYYMMDD'')';
END IF;
v_hdiff := v_hdiff || v_delimiter || v_str;
IF v_name MEMBER OF key_cols THEN
v_hkey := v_hkey || v_delimiter || v_str;
END IF;
END LOOP;
v_hdiff := LTRIM(v_hdiff,'|''#');
v_hkey := LTRIM(v_hkey,'|''#');
RETURN 'SELECT STANDARD_HASH('||v_hkey||',''MD5'') hash_key, '||
' STANDARD_HASH('||v_hdiff||',''MD5'') hash_diff, '||
' t.* FROM t';
END;
https://blog.sqlora.com/en/building-hash-keys-using-sql-macros-in-oracle-20c/
IF t.column(i).description.type =
dbms_tf.type_varchar2 THEN
v_str := v_name;
ELSIF t.column(i).description.type =
dbms_tf.type_number THEN
v_str := 'to_char('||v_name||')';
ELSIF t.column(i).description.type =
dbms_tf.type_date THEN
v_str := 'to_char('||v_name||',''YYYYMMDD'')';
END IF;
Data type check at the core
Parameterized Views
• An example for querying versioned data, typical for data warehouse use cases
• Point-in-time and range queries
• PL/SQL function overloading
https://blog.sqlora.com/en/parameterized-views-in-oracle-no-problem-with-sql-macros/
Temporal Joins
• An example of hiding complex query syntax in a SQL Macro function
• Multiple input tables
• Using MATCH_RECOGNIZE
https://blog.sqlora.com/en/temporal-joins-with-sql-macros-in-oracle-20c/
Dynamic PIVOT
• One of “most wanted” features
• But a poor example for SQL Macros
• You can’t make it deterministic
• Only works in 20c
https://blog.sqlora.com/en/dynamic-pivot-with-sql-macros-in-oracle-20c/
Links
• Oracle Documentation
• https://blog.sqlora.com/en/tag/sql-macros/
• Asktom Office Hours
• SQL Macros Have Arrived in Autonomous Database
• LiveSQL
Summary
• Encapsulated reusable logic without typical performance issues
• Show the whole picture to the optimizer without having to maintain complex SQL
statements
• Power-developer can provide efficient building blocks using modern SQL for those who
aren't confident enough to go beyond SQL-92
• Full control over binds or literals used in “parameterized” views
• No read consistency issues with nested SQL-PL/SQL-SQL
• Table macros available in 19c!
Try it yourself!

More Related Content

What's hot (20)

PPT
Oracle sql joins
redro
 
PPTX
Five_Things_You_Might_Not_Know_About_Oracle_Database_v2.pptx
Maria Colgan
 
PPT
06 Using More Package Concepts
rehaniltifat
 
PDF
MySQL 8.0 Optimizer Guide
Morgan Tocker
 
PPTX
Basic sql Commands
MUHAMMED MASHAHIL PUKKUNNUMMAL
 
PDF
Building Robust ETL Pipelines with Apache Spark
Databricks
 
PDF
SQL Monitoring in Oracle Database 12c
Tanel Poder
 
PDF
MySQL for beginners
Saeid Zebardast
 
PDF
High Performance PL/SQL
Steven Feuerstein
 
PPTX
Oracle sql high performance tuning
Guy Harrison
 
PDF
Oracle sql & plsql
Sid Xing
 
PDF
SQL JOINS
Swapnali Pawar
 
PPTX
Sql subquery
Raveena Thakur
 
PDF
Advanced MySQL Query Tuning
Alexander Rubin
 
PDF
Oracle SQL Basics
Dhananjay Goel
 
PDF
MERGE SQL Statement: Lesser Known Facets
Andrej Pashchenko
 
PPTX
SQL Commands
Sachidananda M H
 
PDF
SQL
kaushal123
 
PPT
05 Creating Stored Procedures
rehaniltifat
 
PPT
Sql joins
Berkeley
 
Oracle sql joins
redro
 
Five_Things_You_Might_Not_Know_About_Oracle_Database_v2.pptx
Maria Colgan
 
06 Using More Package Concepts
rehaniltifat
 
MySQL 8.0 Optimizer Guide
Morgan Tocker
 
Basic sql Commands
MUHAMMED MASHAHIL PUKKUNNUMMAL
 
Building Robust ETL Pipelines with Apache Spark
Databricks
 
SQL Monitoring in Oracle Database 12c
Tanel Poder
 
MySQL for beginners
Saeid Zebardast
 
High Performance PL/SQL
Steven Feuerstein
 
Oracle sql high performance tuning
Guy Harrison
 
Oracle sql & plsql
Sid Xing
 
SQL JOINS
Swapnali Pawar
 
Sql subquery
Raveena Thakur
 
Advanced MySQL Query Tuning
Alexander Rubin
 
Oracle SQL Basics
Dhananjay Goel
 
MERGE SQL Statement: Lesser Known Facets
Andrej Pashchenko
 
SQL Commands
Sachidananda M H
 
05 Creating Stored Procedures
rehaniltifat
 
Sql joins
Berkeley
 

Similar to SQL Macros - Game Changing Feature for SQL Developers? (20)

PPT
Sql 3
Nargis Ehsan
 
PPTX
Understanding Query Optimization with ‘regular’ and ‘Exadata’ Oracle
Guatemala User Group
 
PPTX
Oracle dbms_xplan.display_cursor format
Franck Pachot
 
PPTX
Oracle 12c SPM
Anton Bushmelev
 
PDF
On Seeing Double in V$SQL_Thomas_Kytepdf
cookie1969
 
PDF
Performance Schema for MySQL Troubleshooting
Sveta Smirnova
 
DOC
SQLQueries
karunakar81987
 
PDF
SQL Functions and Operators
Mohan Kumar.R
 
PDF
EvolveExecutionPlans.pdf
PraveenPolu1
 
PPT
Beg sql
Karthik Perumal
 
PPT
Beg sql
KPNR Jan
 
PDF
Oracle 11g caracteristicas poco documentadas 3 en 1
Ronald Francisco Vargas Quesada
 
PDF
12c SQL Plan Directives
Franck Pachot
 
PDF
All on Adaptive and Extended Cursor Sharing
Mohamed Houri
 
PPTX
Writing efficient sql
j9soto
 
PDF
COIS 420 - Practice 03
Angel G Diaz
 
PPT
Do You Know The 11g Plan?
Mahesh Vallampati
 
PPTX
Dimensional performance benchmarking of SQL
Brendan Furey
 
PPTX
Developers' New features of Sql server express 2012
Ziaur Rahman
 
Understanding Query Optimization with ‘regular’ and ‘Exadata’ Oracle
Guatemala User Group
 
Oracle dbms_xplan.display_cursor format
Franck Pachot
 
Oracle 12c SPM
Anton Bushmelev
 
On Seeing Double in V$SQL_Thomas_Kytepdf
cookie1969
 
Performance Schema for MySQL Troubleshooting
Sveta Smirnova
 
SQLQueries
karunakar81987
 
SQL Functions and Operators
Mohan Kumar.R
 
EvolveExecutionPlans.pdf
PraveenPolu1
 
Beg sql
KPNR Jan
 
Oracle 11g caracteristicas poco documentadas 3 en 1
Ronald Francisco Vargas Quesada
 
12c SQL Plan Directives
Franck Pachot
 
All on Adaptive and Extended Cursor Sharing
Mohamed Houri
 
Writing efficient sql
j9soto
 
COIS 420 - Practice 03
Angel G Diaz
 
Do You Know The 11g Plan?
Mahesh Vallampati
 
Dimensional performance benchmarking of SQL
Brendan Furey
 
Developers' New features of Sql server express 2012
Ziaur Rahman
 
Ad

Recently uploaded (20)

DOCX
Cat_Latin_America_in_World_Politics[1].docx
sales480687
 
PDF
TCU EVALUATION FACULTY TCU Taguig City 1st Semester 2017-2018
MELJUN CORTES
 
PPTX
Model Evaluation & Visualisation part of a series of intro modules for data ...
brandonlee626749
 
PPT
Camuflaje Tipos Características Militar 2025.ppt
e58650738
 
PPTX
MENU-DRIVEN PROGRAM ON ARUNACHAL PRADESH.pptx
manvi200807
 
DOCX
Artigo - Playing to Win.planejamento docx
KellyXavier15
 
DOCX
COT Feb 19, 2025 DLLgvbbnnjjjjjj_Digestive System and its Functions_PISA_CBA....
kayemorales1105
 
PPTX
Data Analytics using sparkabcdefghi.pptx
KarkuzhaliS3
 
PPTX
727325165-Unit-1-Data-Analytics-PPT-1.pptx
revathi148366
 
PPT
Reliability Monitoring of Aircrfat commerce
Rizk2
 
PDF
Blood pressure (3).pdfbdbsbsbhshshshhdhdhshshs
hernandezemma379
 
DOCX
brigada_PROGRAM_25.docx the boys white house
RonelNebrao
 
PDF
Business Automation Solution with Excel 1.1.pdf
Vivek Kedia
 
PDF
Exploiting the Low Volatility Anomaly: A Low Beta Model Portfolio for Risk-Ad...
Bradley Norbom, CFA
 
PPTX
25 items quiz for practical research 1 in grade 11
leamaydayaganon81
 
PDF
624753984-Annex-A3-RPMS-Tool-for-Proficient-Teachers-SY-2024-2025.pdf
CristineGraceAcuyan
 
PDF
Informatics Market Insights AI Workforce.pdf
karizaroxx
 
PDF
11_L2_Defects_and_Trouble_Shooting_2014[1].pdf
gun3awan88
 
PPTX
一比一原版(TUC毕业证书)开姆尼茨工业大学毕业证如何办理
taqyed
 
PPTX
PPT2 W1L2.pptx.........................................
palicteronalyn26
 
Cat_Latin_America_in_World_Politics[1].docx
sales480687
 
TCU EVALUATION FACULTY TCU Taguig City 1st Semester 2017-2018
MELJUN CORTES
 
Model Evaluation & Visualisation part of a series of intro modules for data ...
brandonlee626749
 
Camuflaje Tipos Características Militar 2025.ppt
e58650738
 
MENU-DRIVEN PROGRAM ON ARUNACHAL PRADESH.pptx
manvi200807
 
Artigo - Playing to Win.planejamento docx
KellyXavier15
 
COT Feb 19, 2025 DLLgvbbnnjjjjjj_Digestive System and its Functions_PISA_CBA....
kayemorales1105
 
Data Analytics using sparkabcdefghi.pptx
KarkuzhaliS3
 
727325165-Unit-1-Data-Analytics-PPT-1.pptx
revathi148366
 
Reliability Monitoring of Aircrfat commerce
Rizk2
 
Blood pressure (3).pdfbdbsbsbhshshshhdhdhshshs
hernandezemma379
 
brigada_PROGRAM_25.docx the boys white house
RonelNebrao
 
Business Automation Solution with Excel 1.1.pdf
Vivek Kedia
 
Exploiting the Low Volatility Anomaly: A Low Beta Model Portfolio for Risk-Ad...
Bradley Norbom, CFA
 
25 items quiz for practical research 1 in grade 11
leamaydayaganon81
 
624753984-Annex-A3-RPMS-Tool-for-Proficient-Teachers-SY-2024-2025.pdf
CristineGraceAcuyan
 
Informatics Market Insights AI Workforce.pdf
karizaroxx
 
11_L2_Defects_and_Trouble_Shooting_2014[1].pdf
gun3awan88
 
一比一原版(TUC毕业证书)开姆尼茨工业大学毕业证如何办理
taqyed
 
PPT2 W1L2.pptx.........................................
palicteronalyn26
 
Ad

SQL Macros - Game Changing Feature for SQL Developers?

  • 1. SQL Macros – Game Changing Feature für SQL Entwickler? Andrej Pashchenko @Andrej_SQL https://blog.sqlora.com
  • 2. About me • Working at Trivadis Germany, Düsseldorf • Focusing on Oracle: • Data Warehousing • Application Development • Application Performance • Course instructor „Oracle New Features for Developers“ @Andrej_SQL blog.sqlora.com
  • 5. Motivation It’s a good idea to encapsulate some complex logic and make it reusable We have enough tools to do so, don’t we? • Views • WITH-Subqueries • PL/SQL Functions • Polymorphic Table Functions Why do we need something else?
  • 6. Motivation • Performance • Context switch with PL/SQL functions • The same data structures are often accessed from different PL/SQL functions called from the same SQL • The optimizer having no clue what happens within PL/SQL • Lack of control • No parameters for views • Binds or literals used • Read-Consistency: • SQL calling PL/SQL calling SQL calling PL/SQL calling SQL calling PL/SQL …
  • 7. Motivation • SQL Macros are functions where your (SQL) logic goes in • will NOT be executed at runtime (no context switch!) • will be executed once at parse time and return a piece of SQL code which will be incorporated in your SQL query. • enable to create reusable building blocks used to build complex SQL statements • hiding the unnecessary complexity from developer • but exposing all useful information to the optimizer
  • 10. • SQL macro is just a function returning text (CHAR, VARCHAR2, CLOB) • Whatever your complex logic inside the function is, you return a piece of SQL code at the end SELECT e.* , FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12) as years FROM emp e WHERE FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12) > 38; CREATE OR REPLACE FUNCTION job_duration RETURN VARCHAR2 SQL_MACRO(SCALAR) AS BEGIN RETURN 'FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12)'; END; First SQL Macro • How can I hide the calculation?
  • 11. SQL> SELECT ename , job_duration as years FROM emp e WHERE job_duration > 38 First SQL Macro • Calling SQL macro function SQL> SELECT ename , FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12) as years FROM emp e WHERE FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12 > 38 ENAME YEARS ---------- ---------- SMITH 39 ALLEN 39 WARD 39 3 rows selected. SQL you write SQL macro executed while parsing SQL is translated into SQL actually executed
  • 12. SQL> SELECT ename , FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12) as years , FLOOR(MOD(MONTHS_BETWEEN (SYSDATE, hiredate),12)) as months FROM (SELECT * FROM emp WHERE job = 'ANALYST' ) e WHERE FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12) > 38 GROUP BY ename , FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12) , FLOOR(MOD(MONTHS_BETWEEN (SYSDATE, hiredate),12)) HAVING SUM(sal) > 1000 ORDER BY FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12) + FLOOR(MOD(MONTHS_BETWEEN (SYSDATE, hiredate),12)) SQL macro 1 SQL macro 2 SQL macro 3 SQL macro 4 Where and how do I call SQL Macro? • In general, everywhere in a SQL statement where a function call is allowed
  • 13. Where and how do I call SQL Macro? • Give the function meaningful names and here you go: SQL> SELECT ename , duration_years as years , duration_months as months FROM emp_only_analysts e WHERE duration_years > 38 GROUP BY ename , duration_years , duration_months HAVING sum_sal > 1000 ORDER BY duration_years + duration_months
  • 14. Won’t work! Column alias, condition Won’t work! More than one expression Won’t work! Condition Where and how do I call SQL Macro? • You cannot “break” the structure • Only take up as much of SQL as a normal function SQL> SELECT ename , FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12) as years , FLOOR(MOD(MONTHS_BETWEEN (SYSDATE, hiredate),12)) as months FROM (SELECT * FROM emp WHERE job = 'ANALYST' ) e WHERE FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12) > 38 GROUP BY ename , FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12) , FLOOR(MOD(MONTHS_BETWEEN (SYSDATE, hiredate),12)) HAVING SUM(sal) > 1000 ORDER BY FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12) + FLOOR(MOD(MONTHS_BETWEEN (SYSDATE, hiredate),12))
  • 15. SQL> SELECT ename , job_duration as years FROM emp e WHERE job_duration > 38 ENAME YEARS ---------- ---------- SMITH 39 ALLEN 39 WARD 39 3 rows selected. What Types of SQL Macros are there? • Used in SELECT, WHERE, GROUP BY, … SELECT e.hash_diff , e.empno , e.ename FROM add_hash_columns (emp) e; HASH_DIFF EMPNO ENAME ---------- ---------- ---------- 42CB6932B4 7369 SMITH AA63299F72 7499 ALLEN 27332DD16B 7521 WARD 3 rows selected. • Used in FROM clause Scalar SQL macro Table SQL macro (default)
  • 16. CREATE FUNCTION job_duration (<optional parameters>) RETURN VARCHAR2 | CHAR | CLOB SQL_MACRO(SCALAR|TABLE) AS BEGIN <.. Your business logic here .. > RETURN 'FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12)'; END; How to define a SQL macro function? • The function must return CHAR, VARCHAR2 or CLOB • Using a new keyword SQL_MACRO This makes a regular function a SQL macro The result of a function becomes a part of the calling SQL TABLE is default and can be omitted
  • 17. When can I use SQL Macros? • SQL Macros have been introduced in Oracle 20c • Only Preview Version of 20c in Oracle Cloud as of now • (Only) Table SQL Macros have been backported to 19c (19.8)! You can start to test them! • Officially documented and supported • You cannot specify the type of a macro, but since TABLE is default – no problem migrating to 20c later CREATE FUNCTION job_duration (<optional parameters>) RETURN VARCHAR2 | CHAR | CLOB SQL_MACRO(SCALAR|TABLE) AS BEGIN RETURN 'FLOOR(MONTHS_BETWEEN (SYSDATE, hiredate)/12)'; END;
  • 19. Passing Parameters • You can pass scalar parameters and use them in return string • But don’t concatenate CREATE FUNCTION duration (p_date IN DATE) RETURN VARCHAR2 SQL_MACRO(SCALAR) AS BEGIN RETURN 'FLOOR(MONTHS_BETWEEN (SYSDATE, duration.p_date)/12)'; END; exec dbms_output.put_line( duration (DATE '2020-02-26') ); PL/SQL procedure successfully completed. FLOOR(MONTHS_BETWEEN (SYSDATE, duration.p_hiredate)/12) What is a parameter inside a quoted string good for? Calling outside of SQL it just makes no sense, but…
  • 20. Passing Parameters • When calling in SQL, the parameter you pass seems just to be substituted in the return statement of the function (result string) SQL> SELECT ename, duration (hiredate) as years FROM emp e WHERE duration (hiredate) > 38 ... SQL> select * from dbms_xplan.display_cursor() ... -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | | 3 (100)| | |* 1 | TABLE ACCESS FULL| EMP | 1 | 14 | 3 (0)| 00:00:01 | -------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter(FLOOR(MONTHS_BETWEEN(SYSDATE@!,INTERNAL_FUNCTION("HIREDATE"))/12)>38)
  • 21. Passing Parameters • Passing a column name SQL> CREATE OR REPLACE FUNCTION new_func (p_param IN VARCHAR2) RETURN VARCHAR2 SQL_MACRO(SCALAR) AS BEGIN RETURN 'UPPER(p_param)'; END; Function created. SQL> SELECT count(*) FROM emp e WHERE new_func(ename) = 'SCOTT' ... SQL> select * from dbms_xplan.display_cursor() ... Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter(UPPER("ENAME")='SCOTT')
  • 22. Passing Parameters • Passing a literal: the optimizer may see that the condition can never be true SQL> SELECT count(*) FROM emp e WHERE new_func('ADAMS') = 'SCOTT' ... SQL> select * from dbms_xplan.display_cursor() -------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | -------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | 1 (100)| | | 1 | SORT AGGREGATE | | 1 | | | |* 2 | FILTER | | | | | | 3 | INDEX FULL SCAN| PK_EMP | 14 | 1 (0)| 00:00:01 | -------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter(NULL IS NOT NULL)
  • 23. Passing Parameters • Passing bind variables: the bind variable is used in a calling SQL SQL> SELECT count(*) FROM emp e WHERE new_func(:bind) = 'SCOTT' ... SQL> select * from dbms_xplan.display_cursor() -------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | -------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | 1 (100)| | | 1 | SORT AGGREGATE | | 1 | | | |* 2 | FILTER | | | | | | 3 | INDEX FULL SCAN| PK_EMP | 14 | 1 (0)| 00:00:01 | -------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter(UPPER(:BIND)='SCOTT')
  • 24. Passing Parameters • don’t concatenate function parameter into the result string like RETURN 'UPPER('||p_param||')' • don’t use parameter as bind variable in a result string like RETURN 'UPPER(:p_param)' • just reference the parameter in a literal string to be returned, you can optionally prefix it with a function name if conflicting with other names RETURN 'UPPER(new_func.p_param)' • this reference will be replaced with column name, bind variable, function or literal, whatever was used to invoke a SQL macro function
  • 25. Parameters inside a function • To protect against SQL injection all string parameters are NULL inside a function CREATE OR REPLACE FUNCTION f_test_par (p_num NUMBER, p_str VARCHAR2) RETURN VARCHAR2 SQL_MACRO(SCALAR) IS BEGIN DBMS_OUTPUT.PUT_LINE('p_num '||p_num); DBMS_OUTPUT.PUT_LINE('p_str '||p_str); RETURN 'p_str||to_char(p_num)' ; END; / SELECT f_test_par(20, 'Oracle ') FROM dual; F_TEST_PA --------- Oracle 20 p_num 20 p_str <-- the string 'Oracle’ is not visible inside the function
  • 26. SQL Macros and Parsing • What does it mean? • SQL Macro function will not be called every time the SQL is executed • It will be executed occasionally when SQL statement gets hard parsed, which you cannot control: • DDL on involved database objects • SQL aged out from shared pool, etc. • You must ensure its deterministic behavior • Don’t try to make the return string depend on the state in the database • More on this: https://blog.sqlora.com/en/sql-macros-part-2-parameters-and-parsing/
  • 28. Table SQL Macros CREATE OR REPLACE FUNCTION filter_dept RETURN VARCHAR2 SQL_MACRO(TABLE) IS BEGIN RETURN 'SELECT * FROM scott.emp WHERE deptno = 30'; END; / • Table SQL macros can be called in FROM clause only SELECT empno, deptno FROM filter_dept(); SELECT empno, deptno FROM (SELECT * FROM emp WHERE deptno = 30); • Acts just like inline view in this query • Or even like a regular view because the query is saved in the database • But in contrast to regular views we can use parameters!
  • 29. Table SQL Macros – Using Scalar Parameters CREATE OR REPLACE FUNCTION filter_dept( p_deptno IN NUMBER) RETURN VARCHAR2 SQL_MACRO(TABLE) IS BEGIN RETURN 'SELECT * FROM emp WHERE deptno = p_deptno'; END; / • Just use function parameters in the return string SELECT empno, deptno FROM filter_dept(10); SELECT empno, deptno FROM (SELECT * FROM emp WHERE deptno = 10); A literal substitution of parameters in the return string
  • 30. Table SQL Macros – Examples Using Scalar Parameters SELECT empno, deptno FROM filter_dept(10) SELECT empno, deptno FROM (SELECT * FROM emp WHERE deptno = 10) SELECT empno, deptno FROM filter_dept(empno) SELECT empno, deptno FROM (SELECT * FROM emp WHERE deptno = empno) SELECT empno, deptno FROM filter_dept(:P_DEPT) SELECT empno, deptno FROM (SELECT * FROM emp WHERE deptno =:P_DEPT) SELECT empno, deptno FROM filter_dept(my_func(100)) SELECT empno, deptno FROM (SELECT * FROM emp WHERE deptno = my_func(100)) SELECT empno, deptno FROM filter_dept((SELECT 10 FROM dual)) SELECT empno, deptno FROM (SELECT * FROM emp WHERE deptno = ((SELECT 10 FROM dual)) How do I know what’s actually executed?
  • 31. Table SQL Macros – How to See the Real SQL Statement SQL> DECLARE 2 l_clob CLOB; 3 BEGIN 4 DBMS_UTILITY.expand_sql_text ( 5 input_sql_text => 'SELECT empno, deptno FROM filter_dept(:P_DEPT)', 6 output_sql_text => l_clob ); 7 DBMS_OUTPUT.put_line(l_clob); 8 END; 9 / SELECT "A1"."EMPNO" "EMPNO","A1"."DEPTNO" "DEPTNO" FROM (SELECT "A2"."EMPNO" "EMPNO","A2"."ENAME" "ENAME","A2"."JOB" "JOB","A2"."MGR" "MGR","A2"."HIREDATE" "HIREDATE","A2"."SAL" "SAL","A2"."COMM" "COMM","A2"."DEPTNO" "DEPTNO" FROM (SELECT "A3"."EMPNO" "EMPNO","A3"."ENAME" "ENAME","A3"."JOB" "JOB","A3"."MGR" "MGR","A3"."HIREDATE" "HIREDATE","A3"."SAL" "SAL","A3"."COMM" "COMM","A3"."DEPTNO" "DEPTNO" FROM "SCOTT"."EMP" "A3" WHERE "A3"."DEPTNO"=:B1) "A2") "A1" PL/SQL procedure successfully completed. • You can use DBMS_UTILITY.expand_sql_text • But only for table macros!
  • 32. Table SQL Macros – Polymorphic Views Using parameters for views is fine, but must the query always remain hardcoded? No! Just use table and column parameters to create polymorphic views! Polymorphic Views and Polymorphic Table Functions – is it the same? No, don’t confuse them. PTF means PL/SQL execution at runtime, Polymorphic Views via SQL macros are only executed at parse time So no need to learn about the PTF’s right now? At least you should get to know the table and column parameters, because they were introduced and documented in the context of PTF's
  • 33. Table SQL Macros – Polymorphic Views • Pass tables (views, named subqueries) as parameter of type DBMS_TF.TABLE_T a table of • E.g. to get the data type of the first column: <Param>.column(1).description.type
  • 34. SELECT * FROM my_sql_macro_func( emp, COLUMNS(EMPNO, ENAME)); Table SQL Macros – Polymorphic Views • Pass column lists as a parameter of type DBMS_TF.COLUMNS_T • The right way to do it is using a variadic pseudo-operator COLUMNS (18c) Visible as TABLE_T structure with all column description etc. Visible as array "EMPNO", "ENAME"
  • 35. CREATE OR REPLACE FUNCTION top_n (p_tab IN DBMS_TF.TABLE_T, p_limit IN NUMBER , p_order IN DBMS_TF.COLUMNS_T) RETURN VARCHAR2 SQL_MACRO IS v_order_list VARCHAR2(2000); BEGIN -- turn PL/SQL table to comma separated list for ORDER BY clause SELECT LISTAGG(column_value,',') INTO v_order_list FROM TABLE (p_order); RETURN 'SELECT * FROM top_n.p_tab ORDER BY '||v_order_list|| ' FETCH FIRST top_n.p_limit ROWS ONLY'; END; Table SQL Macros – Polymorphic Views • Implement reusable SQL macro to get Top-N rows from any table using defined sort order SQL> SELECT deptno, dname, loc 2 FROM top_n(scott.dept, 2, COLUMNS(loc)); DEPTNO DNAME LOC ---------- -------------- ------------- 40 OPERATIONS BOSTON 30 SALES CHICAGO SQL> SELECT empno, ename 2 FROM top_n(scott.emp, 2, COLUMNS(ename)); EMPNO ENAME ---------- ---------- 7876 ADAMS 7499 ALLEN
  • 37. SELECT e.hash_diff , e.empno , e.ename FROM add_hash_columns (emp) e; HASH_DIFF EMPNO ENAME ---------- ---------- ---------- 42CB6932B4 7369 SMITH AA63299F72 7499 ALLEN 27332DD16B 7521 WARD 3 rows selected. Generate Hash Keys • Build MD5 digest of the whole row, often used for row comparison during ETL processes • There are complex rules to follow, thus one PL/SQL function across all use cases is preferrable • The most efficient way to generate MD5-Hash is STANDARD_HASH, not available in PL/SQL CREATE OR REPLACE FUNCTION add_hash_columns(t DBMS_TF.TABLE_T , key_cols DBMS_TF.COLUMNS_T) RETURN VARCHAR2 SQL_MACRO(TABLE) AS v_hdiff clob ; v_hkey clob ; v_str varchar2(200); v_delimiter varchar2(9):= '||''#''||'; v_name dbms_id; BEGIN FOR I IN 1..t.column.count LOOP v_name := t.column(i).description.name; IF t.column(i).description.type = dbms_tf.type_varchar2 THEN v_str := v_name; ELSIF t.column(i).description.type = dbms_tf.type_number THEN v_str := 'to_char('||v_name||')'; ELSIF t.column(i).description.type = dbms_tf.type_date THEN v_str := 'to_char('||v_name||',''YYYYMMDD'')'; END IF; v_hdiff := v_hdiff || v_delimiter || v_str; IF v_name MEMBER OF key_cols THEN v_hkey := v_hkey || v_delimiter || v_str; END IF; END LOOP; v_hdiff := LTRIM(v_hdiff,'|''#'); v_hkey := LTRIM(v_hkey,'|''#'); RETURN 'SELECT STANDARD_HASH('||v_hkey||',''MD5'') hash_key, '|| ' STANDARD_HASH('||v_hdiff||',''MD5'') hash_diff, '|| ' t.* FROM t'; END; https://blog.sqlora.com/en/building-hash-keys-using-sql-macros-in-oracle-20c/ IF t.column(i).description.type = dbms_tf.type_varchar2 THEN v_str := v_name; ELSIF t.column(i).description.type = dbms_tf.type_number THEN v_str := 'to_char('||v_name||')'; ELSIF t.column(i).description.type = dbms_tf.type_date THEN v_str := 'to_char('||v_name||',''YYYYMMDD'')'; END IF; Data type check at the core
  • 38. Parameterized Views • An example for querying versioned data, typical for data warehouse use cases • Point-in-time and range queries • PL/SQL function overloading https://blog.sqlora.com/en/parameterized-views-in-oracle-no-problem-with-sql-macros/
  • 39. Temporal Joins • An example of hiding complex query syntax in a SQL Macro function • Multiple input tables • Using MATCH_RECOGNIZE https://blog.sqlora.com/en/temporal-joins-with-sql-macros-in-oracle-20c/
  • 40. Dynamic PIVOT • One of “most wanted” features • But a poor example for SQL Macros • You can’t make it deterministic • Only works in 20c https://blog.sqlora.com/en/dynamic-pivot-with-sql-macros-in-oracle-20c/
  • 41. Links • Oracle Documentation • https://blog.sqlora.com/en/tag/sql-macros/ • Asktom Office Hours • SQL Macros Have Arrived in Autonomous Database • LiveSQL
  • 42. Summary • Encapsulated reusable logic without typical performance issues • Show the whole picture to the optimizer without having to maintain complex SQL statements • Power-developer can provide efficient building blocks using modern SQL for those who aren't confident enough to go beyond SQL-92 • Full control over binds or literals used in “parameterized” views • No read consistency issues with nested SQL-PL/SQL-SQL • Table macros available in 19c! Try it yourself!