DB2的CREATE FUNCTION语句用于定义用户自定义函数(UDF),它既可以是完全用SQL编写的函数,也可以是对外部程序(如Java、C)的包装。而“函数映射”(Function Mapping)则是DB2联邦数据库特性中的一个重要概念,它允许将本地函数调用透明地转发到远程数据源上的同名或不同名函数。理解这两者的区别与联系,对于构建跨数据库的集成方案至关重要。

普通CREATE FUNCTION创建的函数在本地数据库中执行,由DB2引擎解析SQL PL语句或调用外部例程;而CREATE FUNCTION MAPPING创建的对象本身不包含函数体,它只是建立了一个从本地函数签名到远程数据源函数的映射关系。当SQL语句引用该映射函数时,DB2会将调用下推到远程数据源,由远程数据库执行实际计算。这种机制常用于联邦查询场景,例如本地DB2需要调用Oracle或SQL Server上的内置函数。
一、DB2 CREATE FUNCTION语法与类型
DB2支持多种用户定义函数类型,常见的有标量函数(Scalar Function)、表函数(Table Function)和行函数(Row Function)。其中标量函数返回单个值,表函数返回一个表,行函数返回一行。创建标量函数的基本语法如下:
CREATE FUNCTION function_name (parameter_name data_type, ...)
RETURNS return_data_type
LANGUAGE SQL
[SPECIFIC specific_name]
[DETERMINISTIC | NOT DETERMINISTIC]
[EXTERNAL ACTION | NO EXTERNAL ACTION]
[FENCED | NOT FENCED]
[READS SQL DATA | CONTAINS SQL | NO SQL]
[RETURN expression]
[BEGIN ATOMIC
SQL PL statements;
RETURN expression;
END]
对于简单的标量函数,可以直接在RETURN子句中给出表达式,无需使用BEGIN ATOMIC块。例如,创建一个将字符串转换为大写的函数:
CREATE FUNCTION to_upper_custom (input VARCHAR(100))
RETURNS VARCHAR(100)
LANGUAGE SQL
DETERMINISTIC
NO EXTERNAL ACTION
RETURN UPPER(input);
表函数则通过RETURNS TABLE子句定义输出列,并在RETURN中返回一个SELECT语句的结果集。例如,返回指定部门所有员工姓名的表函数:
CREATE FUNCTION dept_employees (dept_id INTEGER)
RETURNS TABLE (emp_name VARCHAR(50), hire_date DATE)
LANGUAGE SQL
READS SQL DATA
NO EXTERNAL ACTION
RETURN
SELECT name, hire_date
FROM employees
WHERE department_id = dept_id;
如果函数逻辑复杂,可以使用SQL PL编写过程体,包括变量声明、条件判断和循环。此时需要在BEGIN ATOMIC和END之间编写语句,并且函数必须声明为MODIFIES SQL DATA或READS SQL DATA等适当的数据访问选项。注意表函数不能使用BEGIN ATOMIC结构直接返回表,而是通过RETURN子句返回查询。
另一种常见形式是外部函数,即用C、Java、.NET等语言编写函数体,然后通过CREATE FUNCTION注册到DB2。这类函数需要指定LANGUAGE为对应语言,并提供EXTERNAL NAME指向实际例程。例如创建一个Java标量函数:
CREATE FUNCTION java_reverse (input VARCHAR(500))
RETURNS VARCHAR(500)
LANGUAGE JAVA
PARAMETER STYLE JAVA
EXTERNAL NAME 'com.example.UDF.reverse';
在上述语法中,PARAMETER STYLE JAVA表示参数以Java风格传递,EXTERNAL NAME指定类名和方法名。函数映射与这些普通函数有着本质区别,下面将详细介绍。
二、函数映射的概念与创建
函数映射在DB2联邦数据库环境中使用。联邦数据库允许一个DB2实例访问多个异构数据源(如Oracle、SQL Server、MySQL、甚至文件),这些数据源被定义为“昵称”(Nickname)。当用户对昵称执行查询时,DB2优化器会尝试将尽可能多的操作下推到数据源执行,以减少数据传输量。函数映射正是为了支持将本地函数调用转换为远程数据源上的等效函数而设计的。
CREATE FUNCTION MAPPING语句的语法相对简单,它不包含函数体,而是指定本地函数名、参数类型,以及远程数据源上的函数名和远程函数参数类型。基本形式如下:
CREATE FUNCTION MAPPING local_function_name (local_param_types)
FOR SERVER server_name
REMOTE FUNCTION remote_function_name (remote_param_types)
[OPTIONS (...)];
举例来说,假设本地DB2联邦了一个Oracle服务器,Oracle中有一个自定义函数ORA_CALC_BONUS,我们希望本地用户能够像调用本地函数一样调用它,可以创建如下映射:
CREATE FUNCTION MAPPING calc_bonus (INTEGER, DECIMAL(10,2))
FOR SERVER oracle_server
REMOTE FUNCTION ORA_CALC_BONUS (INTEGER, NUMBER);
之后,本地用户执行SELECT calc_bonus(emp_id, salary) FROM nick_employees时,DB2会将calc_bonus调用替换为远程的ORA_CALC_BONUS,并将参数发送到Oracle执行。需要注意的是,本地函数映射本身不进行任何计算,它只是一个“翻译器”。如果本地SQL中引用了映射函数,但查询无法下推到远程(例如涉及本地表与远程表连接且优化器决定在本地执行连接),则映射函数调用可能会失败,因为本地没有实际函数体。
函数映射的权限要求也与普通函数不同。创建映射的用户需要对远程数据源具有相应权限,并且需要拥有对服务器定义的USAGE权限。此外,映射函数的参数类型必须与远程函数兼容,否则会在调用时产生类型转换错误。DB2还支持使用OPTIONS子句指定额外的映射选项,例如UNABLE TO DETERMINE等。
三、实战:创建并测试函数映射
为了更好地理解函数映射的工作流程,我们搭建一个简化的联邦环境并演示完整步骤。假设本地DB2数据库为LOCALDB,远程数据源是一个MySQL数据库,其中包含一个名为fn_discount的函数,它接收一个价格参数并返回折扣后的价格。
第一步,在本地DB2中创建对MySQL服务器的包装器和服务器定义:
CREATE WRAPPER mysql_wrapper LIBRARY 'libdb2mysql.so';
CREATE SERVER mysql_server TYPE MYSQL VERSION '8.0' WRAPPER mysql_wrapper
OPTIONS (HOST '192.168.1.100', PORT '3306', DBNAME 'salesdb');
CREATE USER MAPPING FOR CURRENT USER SERVER mysql_server
OPTIONS (REMOTE_AUTHID 'db2user', REMOTE_PASSWORD 'secret');
第二步,为远程表创建昵称。假设MySQL中有一个orders表,包含列order_id和amount:
CREATE NICKNAME nick_orders FOR mysql_server.salesdb.orders;
第三步,创建函数映射,将本地函数名apply_discount映射到远程MySQL函数fn_discount:
CREATE FUNCTION MAPPING apply_discount (DECIMAL(10,2))
FOR SERVER mysql_server
REMOTE FUNCTION fn_discount (DECIMAL(10,2));
第四步,在本地执行查询验证。如果优化器能够将整个查询下推到MySQL,那么本地不会实际执行任何函数代码:
SELECT order_id, apply_discount(amount) AS discounted_amount FROM nick_orders WHERE order_id = 1001;
该查询会被完整下推,MySQL接收到类似SELECT order_id, fn_discount(amount) FROM orders WHERE order_id = 1001的语句。如果查询涉及本地表,导致无法完全下推,DB2会尝试在本地对昵称执行扫描,并将每一行的参数发送到远程函数求值,这时的性能会显著下降。因此,函数映射最适合用于能够整体下推的查询场景。
除了远程用户自定义函数,DB2还允许映射远程数据源的内置函数。例如,可以创建一个映射到MySQL的CONCAT函数:
CREATE FUNCTION MAPPING my_concat (VARCHAR(100), VARCHAR(100))
FOR SERVER mysql_server
REMOTE FUNCTION CONCAT (VARCHAR(100), VARCHAR(100));
这样,本地代码就可以使用my_concat来连接两个字符串,而不必关心底层数据源的语法差异。这种抽象在构建跨数据库应用层时非常有用。
四、常见问题与最佳实践
在使用CREATE FUNCTION MAPPING时,开发者经常会遇到以下几个陷阱。首先是参数类型不匹配。DB2对类型检查比较严格,本地映射函数的参数类型必须与远程函数声明完全一致或可以直接转换。例如本地使用INTEGER而远程使用BIGINT,虽然数值范围有重叠,但DB2可能不会自动转换,导致SQLCODE -440错误。建议在创建映射前查询远程数据源的系统目录,确认精确的类型名称。
其次是权限问题。创建函数映射需要CREATETAB或CREATEIN权限,并且用户必须拥有对指定服务器定义的USAGE权限。如果服务器定义使用了用户映射,还需要确保本地用户映射到远程的账号具有执行远程函数的权限。否则,即使映射创建成功,运行时也会收到远程数据源返回的权限不足错误。
另一个常见误区是将函数映射与普通函数混用。函数映射不能作为本地函数体的一部分调用,也不能在普通SQL PL函数中直接引用,因为映射函数只能在涉及昵称的查询中由优化器下推处理。如果确实需要在本地逻辑中调用远程函数,可以考虑创建本地包装函数,在内部使用联邦查询调用远程函数,但这会引入额外的网络开销。
最佳实践方面,建议为函数映射制定统一的命名规范,例如使用前缀rmt_表示远程函数,避免与本地函数混淆。同时,在文档中明确记录每个映射对应的远程数据源和函数签名,便于维护。此外,应定期审查映射函数的使用情况,删除不再使用的映射,以减少联邦目录的杂乱。
最后,性能监控也不容忽视。由于函数映射依赖于查询下推,可以通过EXPLAIN工具查看执行计划,确认函数调用是否被下推。如果发现频繁的本地逐行求值,可能需要调整查询写法或考虑在远程数据源上创建视图来简化下推条件。函数映射是DB2联邦数据库的强大特性,但只有正确理解其工作机制,才能发挥其跨库调用的真正价值。
DB2CREATE FUNCTION函数映射修改时间:2026-09-18 20:55:14