Chapter 06 · Advanced SQL: Analytic Functions, MODEL, PIVOT, MATCH_RECOGNIZE, and SQL Macros
SQL Macros and Reusable SQL Abstractions, Optimizer Visibility, and Maintainability
Create scalar and table SQL macros with current 26ai syntax, observe optimizer-visible expansion, and compare macros with views, PL/SQL functions, and dynamic SQL.
Learning outcomes
ServiceHub repeats the same priority classification and open-work-order filtering in dashboards, APIs, and ad-hoc SQL. Copy/paste drifts, while wrapping everything in opaque procedural or dynamically constructed SQL can hide predicates from review and create injection risk. SQL macros provide reusable SQL text that is expanded into the calling statement, preserving optimizer visibility—but their privilege model and restrictions must be understood.
Create a scalar SQL macro with exact 26ai SQL_MACRO(SCALAR) syntax.
Create a table SQL macro and call it as a parameterized table expression.
Observe macro metadata and optimizer-visible base-table predicates.
Explain definer-rights macro-text construction versus invoker-rights evaluation and view-owner behavior.
Choose among macros, views, ordinary PL/SQL functions, and dynamic SQL based on semantics, security, and maintainability.
SQL macros are documented features in the current Oracle AI Database 26ai PL/SQL/SQL references. The mandatory lab assumes a working 26ai Free database and requires no COMPATIBLE change. Oracle documentation for SQL_MACRO does not prescribe a chapter-specific COMPATIBLE bump; do not alter COMPATIBLE merely to run this lesson. For older estates, verify that release’s own documentation and patch level rather than extrapolating 26ai behavior.
Mandatory labs target a disposable ServiceHub schema in Oracle AI Database Free 26ai. This chapter was reviewed against July 2026 RU 23.26.3, SQL Developer 26.2, and SQLcl 26.2.1. No Diagnostics/Tuning Pack, RAC, Data Guard, Exadata, GoldenGate, or paid cloud service is required. Free remains capped at two processing cores, 2 GB RAM for SGA+PGA, and 12 GB user data and receives no Oracle patches or Support SRs, so it is a learning baseline rather than a production recommendation.
1. What a SQL macro is
A SQL macro is a PL/SQL function whose return value is SQL text.
When called from SQL, Oracle expands that text into the calling
statement. The macro function itself must return
VARCHAR2, CHAR, or CLOB.
TABLE is the default macro kind;
SCALAR must be declared explicitly.
CREATE OR REPLACE FUNCTION sh_priority_label(p_priority NUMBER) RETURN VARCHAR2 SQL_MACRO(SCALAR)ISBEGIN RETURN q'{ CASE WHEN p_priority >= 90 THEN 'CRITICAL' WHEN p_priority >= 60 THEN 'HIGH' ELSE 'NORMAL' END }';END;/
SELECT work_order_id, priority_score, sh_priority_label(priority_score) AS priority_labelFROM servicehub_sql_macro_woORDER BY work_order_id;
2. Table macros are parameterized table expressions
CREATE OR REPLACE FUNCTION sh_open_work_orders(p_min_cost NUMBER) RETURN VARCHAR2 SQL_MACRO(TABLE)ISBEGIN RETURN q'{ SELECT work_order_id, asset_id, status_code, estimated_cost, priority_score FROM servicehub_sql_macro_wo WHERE status_code = 'OPEN' AND estimated_cost >= p_min_cost }';END;/
SELECT *FROM sh_open_work_orders(500)ORDER BY estimated_cost DESC, work_order_id;
The formal parameter name appears in the returned SQL text. Oracle binds the macro call’s argument into the expanded expression; do not concatenate untrusted user strings into returned SQL.
3. Deliberately wrong: call a table macro as a scalar function
SELECT sh_open_work_orders(500)FROM dual;-- ORA-64629: table SQL macro can only appear in FROM clause of a SQL statement
The repair is structural: table macros belong in the
FROM clause. Conversely, a scalar macro cannot be
used as a table expression. These are different contracts, not
interchangeable wrappers.
4. Observe that Oracle knows the object is a macro
SELECT object_name, procedure_name, sql_macroFROM user_proceduresWHERE object_name IN ('SH_PRIORITY_LABEL','SH_OPEN_WORK_ORDERS')ORDER BY object_name, procedure_name;
Current 26ai dictionary metadata reports SCALAR or
TABLE for SQL macro units. This proves object type
metadata; it does not prove that a particular query uses the
plan you expect.
EXPLAIN PLAN SET STATEMENT_ID='SH06L5'FORSELECT work_order_id, asset_id, estimated_costFROM sh_open_work_orders(500)WHERE asset_id = 10;SELECT *FROM TABLE(DBMS_XPLAN.DISPLAY(NULL,'SH06L5','BASIC +PREDICATE'));
Inspect predicate information: the expanded SQL exposes normal relational predicates to optimization. Exact access paths remain data/statistics-dependent.
5. Security is subtle: construction rights and evaluation rights differ
Oracle documents that AUTHID cannot be specified
for a SQL macro. The macro function body executes with definer’s
rights while constructing the SQL text, but the resulting SQL
expression is evaluated with invoker’s rights. A macro
referenced in a view is processed with the view owner’s
privileges. These boundaries mean a macro is not a generic
privilege-escalation wrapper.
Never accept a free-form table name, column name, ORDER BY fragment, predicate, or SQL clause and concatenate it into the returned macro text. SQL macro parameters should represent values/expressions in a reviewed SQL template. If identifiers genuinely must be dynamic, validate them against an allow-list and consider whether dynamic SQL is actually the right abstraction.
6. Macros versus views, PL/SQL functions, and dynamic SQL
| Abstraction | Best fit | Key tradeoff |
|---|---|---|
| View | Stable parameter-free relational contract | Simple governance; no macro parameters. |
| Table SQL macro | Parameterized relational template | Expansion is optimizer-visible; privilege/restriction rules matter. |
| Scalar SQL macro | Reusable SQL expression | 26ai also has SQL Transpiler support for many ordinary PL/SQL functions, so macros are not the only way to avoid runtime switching. |
| Ordinary PL/SQL function | Procedural computation/API | May introduce SQL↔PL/SQL overhead unless transpiled/inlined; semantics differ. |
| Dynamic SQL | Structure truly unknown until runtime | Powerful but higher injection/parse/governance risk. |
7. Hands-on lab
DROP TABLE servicehub_sql_macro_wo IF EXISTS PURGE;CREATE TABLE servicehub_sql_macro_wo ( work_order_id NUMBER PRIMARY KEY, asset_id NUMBER NOT NULL, status_code VARCHAR2(20) NOT NULL, estimated_cost NUMBER(12,2) NOT NULL, priority_score NUMBER NOT NULL);INSERT INTO servicehub_sql_macro_wo VALUES (601,10,'OPEN',900,95);INSERT INTO servicehub_sql_macro_wo VALUES (602,10,'OPEN',300,65);INSERT INTO servicehub_sql_macro_wo VALUES (603,20,'CLOSED',800,70);INSERT INTO servicehub_sql_macro_wo VALUES (604,20,'OPEN',550,40);COMMIT;
CREATE OR REPLACE FUNCTION sh_priority_label(p_priority NUMBER) RETURN VARCHAR2 SQL_MACRO(SCALAR)ISBEGIN RETURN q'{CASE WHEN p_priority >= 90 THEN 'CRITICAL' WHEN p_priority >= 60 THEN 'HIGH' ELSE 'NORMAL' END}';END;/CREATE OR REPLACE FUNCTION sh_open_work_orders(p_min_cost NUMBER) RETURN VARCHAR2 SQL_MACRO(TABLE)ISBEGIN RETURN q'{SELECT work_order_id, asset_id, status_code, estimated_cost, priority_score FROM servicehub_sql_macro_wo WHERE status_code = 'OPEN' AND estimated_cost >= p_min_cost}';END;/
SELECT work_order_id, sh_priority_label(priority_score) AS priority_labelFROM sh_open_work_orders(500)ORDER BY work_order_id;SELECT object_name, sql_macroFROM user_proceduresWHERE object_name IN ('SH_PRIORITY_LABEL','SH_OPEN_WORK_ORDERS')ORDER BY object_name;
DROP FUNCTION sh_open_work_orders;DROP FUNCTION sh_priority_label;DROP TABLE servicehub_sql_macro_wo PURGE;
8. Production judgment and chapter bridge
Use macros when reuse benefits from being expressed as visible SQL rather than hidden procedural calls. Keep returned SQL static and reviewable, treat privileges as part of the API contract, and test the expanded semantics on every supported database release. Do not add undocumented hints or hidden parameters to force macro behavior. For simple parameter-free reuse, a view may be superior; for procedural logic, an ordinary function/package can be clearer.
Chapter 06 closes with a reusable-query mechanism that preserves relational visibility. Chapter 07 turns to DML, transactions, read consistency, undo, locks, and concurrency—where correctness is no longer only about query result semantics but also about what concurrent sessions can safely change.
Check your understanding
- What return data types are allowed for a SQL macro function?
- Where can a TABLE SQL macro be called?
- Why does the plan of a table macro query still expose base-table predicates?
- Can a SQL macro specify AUTHID CURRENT_USER?
- Why is a SQL macro not a safe excuse to concatenate arbitrary user-provided SQL fragments?
Review the answers
Oracle documents character return types such as VARCHAR2, CHAR, or CLOB for macro functions.
As a table expression in the FROM clause.
Oracle expands the macro text into the calling SQL, so the optimizer can reason about the resulting relational expression.
No. AUTHID is not permitted for SQL macros; their construction/evaluation privilege model is defined by Oracle.
Concatenated structure can create injection and governance failures. Keep returned SQL reviewed and use parameters for values/expressions rather than untrusted SQL text.
Authoritative references
- SQL_MACRO Clause — SCALAR/TABLE syntax, restrictions and privilege semantics
- ALL_PROCEDURES / USER_PROCEDURES metadata — SQL_MACRO metadata column
- PL/SQL Release Changes — 26ai SQL Transpiler context
- SELECT — table-expression usage and SQL macro examples