A payroll query that repeats the same allowance calculation in ten COBOL programs is hard to test and easy to change in one place only. A Db2 user-defined function can move that calculation behind one SQL routine, so the application calls PAYROLL.NET_PAY(...) instead of carrying copy-pasted expression logic through every cursor.
This refresh keeps the original topic, but narrows it to what a Db2 for z/OS developer needs: when a UDF belongs in SQL, how scalar and table functions differ, when an external COBOL or C routine is justified, and which options can affect performance or security.
What Is a Db2 User-Defined Function?
A user-defined function, or UDF, is a Db2 routine that you create with CREATE FUNCTION and then call from SQL. IBM's Db2 for z/OS documentation describes CREATE FUNCTION as the statement that registers a user-defined function with the database server. The function can return a single scalar value or a table, depending on how it is defined.
From an application point of view, the most common use is a scalar call in a SELECT, WHERE, or VALUES statement.
SELECT EMPNO,
PAYROLL.NET_PAY(SALARY, BONUS, TAX_CODE) AS NET_AMOUNT
FROM PAYROLL.EMP_PAY
WHERE PAYROLL.ACTIVE_EMP(STATUS, TERM_DATE) = 'Y';
That example hides the rule behind a named function. It also creates a contract. If the rule changes, the DBA and development team can review the function definition instead of searching every batch program for a similar expression.
Use a UDF for Reusable SQL Logic
A UDF is a good fit when the result belongs inside a SQL expression. Date normalization, account-number formatting, small code translations, financial rounding rules, and common eligibility checks are typical examples.
| Use case | Good UDF fit? | Reason |
|---|---|---|
| Return a normalized branch code for each account row. | Yes | The result is a scalar value used directly in SQL. |
| Calculate net pay from salary, allowance, and tax code. | Usually | The same expression is reused by reports and batch programs. |
| Post an accounting transaction across several tables. | No | That is transaction logic. Use a stored procedure or application service. |
| Return a filtered set of rows from a table-like routine. | Maybe | An SQL table function can work, but compare it with a view or direct query. |
Scalar, Table, SQL, External, and Sourced Functions
The old post listed UDF categories but did not explain when each matters. Db2 supports several function forms, and the choice affects how the function is written, invoked, secured, and tuned.
| Function type | What it returns | Typical use |
|---|---|---|
| SQL scalar function | One value | Reusable expression written in SQL PL or a single return expression. |
| SQL table function | A row set | Table-like result that can be referenced in a query. |
| External scalar function | One value | Logic implemented in COBOL, C, Java, PL/I, or Assembler. |
| External table function | A row set | External routine returns rows to the invoking SQL statement. |
| Sourced function | Depends on source function | Reuse an existing built-in or user-defined function with a distinct type or different signature. |
For a COBOL application team, start with a SQL scalar function when the rule can be stated in SQL. Move to an external function only when the logic already exists in a tested language routine or needs facilities that SQL cannot express cleanly.
Create a Simple SQL Scalar UDF
The simplest UDF is a scalar SQL function. The function below standardizes a status value before the calling program compares it. A rule like this is small enough to keep in SQL and easy to test with VALUES.
CREATE FUNCTION APP.CLEAN_STATUS
(P_STATUS CHAR(1))
RETURNS CHAR(1)
LANGUAGE SQL
DETERMINISTIC
NO EXTERNAL ACTION
RETURN
CASE
WHEN P_STATUS IN ('A', 'I', 'S') THEN P_STATUS
WHEN P_STATUS IS NULL THEN 'U'
ELSE 'X'
END;
Test it before wiring it into a batch cursor.
VALUES APP.CLEAN_STATUS('A');
VALUES APP.CLEAN_STATUS(' ');
VALUES APP.CLEAN_STATUS(CAST(NULL AS CHAR(1)));
If the function is deterministic, tell Db2. If it reads tables, calls an external service, or depends on the current time, do not mark it deterministic just because that looks faster. The declaration should match the real behavior.
Call a UDF from COBOL SQL
A COBOL program calls a Db2 UDF inside embedded SQL the same way it calls many built-in functions: qualify the function when needed, pass host variables, and fetch the result into a compatible host variable.
EXEC SQL
SELECT APP.CLEAN_STATUS(STATUS)
INTO :WS-CLEAN-STATUS
FROM CUSTOMER_STATUS
WHERE CUSTOMER_ID = :WS-CUSTOMER-ID
END-EXEC.
Keep the host variable data type close to the UDF return type. A CHAR(1) result belongs in a one-byte character field, not a loosely sized display field that later gets compared with padded values. The related DB2 Host Variables and Structures guide is the right companion when copybook definitions are the problem.
External UDFs Need More Operational Control
An external UDF registers code that lives outside the SQL function body. IBM's external scalar function reference notes that the statement registers an external scalar function and that a scalar function returns one value each time it is invoked. External definitions can specify options such as language, parameter style, WLM environment, security behavior, null handling, and SQL data access.
CREATE FUNCTION APP.RISK_SCORE
(P_ACCT_NO CHAR(12), P_BALANCE DECIMAL(13,2))
RETURNS INTEGER
EXTERNAL NAME 'RSKSCORE'
LANGUAGE COBOL
PARAMETER STYLE SQL
FENCED
DETERMINISTIC
NO SQL
RETURNS NULL ON NULL INPUT
NO EXTERNAL ACTION
WLM ENVIRONMENT RISKUDF;
That definition is not just syntax. It tells the operations team where the routine runs, whether it can read SQL data, whether null input should call the routine, and whether the function has side effects outside Db2.
UDF or Stored Procedure?
Do not use a UDF as a hidden transaction processor. If the logic updates several tables, writes audit rows, sends messages, or owns commit boundaries, a stored procedure is usually the cleaner object. A UDF should behave like a function: input values in, result value or rows out.
| Question | Choose UDF | Choose stored procedure |
|---|---|---|
Can it be used inside a SELECT expression? |
Yes | Usually no |
| Does it return one calculated value? | Yes | Not the main reason |
| Does it perform a business transaction? | No | Yes |
Does COBOL call it with CALL? |
No, it appears in SQL | Yes, SQL CALL is normal |
For the next topic in this cleanup list, the DB2 Stored Procedure page should cover transaction-style routines, result sets, parameters, and deployment. This UDF page should stay focused on function behavior inside SQL.
Performance Checks Before You Add a UDF
A UDF can make SQL easier to read, but it can also hide work from the person reading the query. Before using a UDF in a high-volume cursor, check how often it runs and whether it blocks index-friendly predicates.
- Use
EXPLAINon the SQL that calls the function. - Avoid wrapping indexed columns in a function inside a
WHEREclause unless you have tested the access path. - Mark
DETERMINISTIC,NO SQL, andNO EXTERNAL ACTIONonly when they are true. - For external UDFs, confirm WLM environment, RACF permissions, load module availability, and Language Environment setup.
- Test null handling with
RETURNS NULL ON NULL INPUTorCALLED ON NULL INPUT. - Do not give a UDF the same name as a built-in function unless the function signature and SQL path behavior are reviewed.
The companion Db2 SQL Optimization Tips for COBOL Programs article covers access-path checks in more detail.
Deployment Checklist
- Choose a schema that matches site naming rules and does not collide with system schemas.
- Use a specific name for functions that may be overloaded.
- Grant
EXECUTEonly to the packages, roles, or IDs that need the function. - For static COBOL SQL, confirm bind or rebind steps after new SQL references are introduced.
- Keep source, DDL, grants, WLM definition, and load module promotion in the same change record.
- Prepare rollback DDL before changing a function used by nightly batch jobs.
FAQ
Can a Db2 UDF return more than one row?
Yes. A table function can return a set of rows. A scalar function returns one value each time it is invoked.
Can COBOL code be used in a Db2 UDF?
Yes, an external UDF can reference a routine written in COBOL when the site supports the required Language Environment, WLM, security, and deployment setup.
Should a UDF replace a stored procedure?
No. Use a UDF for reusable logic inside SQL expressions. Use a stored procedure for transaction-style work, multiple statements, result sets, or business operations called with SQL CALL.
A UDF is worth creating when it gives SQL a clear name for a repeated rule and behaves predictably under production volume. If it hides expensive work inside every fetched row, fix the design before it reaches the batch window.
No comments:
Post a Comment