
# 创建函数
#### 功能描述
DLI支持创建使用UDF和UDTF等自定义函数应用于Spark作业开发当中。
具体使用自定义函数端到端的开发指导可以参考：[Spark SQL作业使用UDF](https://support.huaweicloud.com/devg-dli/dli_09_0171.html)和[Spark SQL作业使用UDTF](https://support.huaweicloud.com/devg-dli/dli_09_0204.html)。
#### 语法格式
```
CREATE FUNCTION [db_name.]function_name AS class_name
  [USING resource,...]
resource: 
  : JAR file_uri
```
或
```
CREATE OR REPLACE FUNCTION [db_name.]function_name AS class_name
  [USING resource,...]
resource: 
  : JAR file_uri
```
#### 注意事项
- 如果在数据库中存在同名的函数，系统将会报错。
- 只支持Hive语法创建函数。
- 请注意避免该场景：如果创建的自定义函数F1指定类C1，程序包名JAR1，创建自定义函数F2也指定类C1，程序包JAR2，因为F2和F1使用相同的类名，导致功能相互冲突，影响作业执行。
- UDF热加载功能处于受限使用阶段，如需使用请提交工单申请开通。
 
#### 关键字
- USING \<resources\>：需要加载的资源。可以是JAR、文件或者URI的列表。
- OR REPLACE：支持自定义函数热加载功能。
  - 如果创建自定义函数时**不携带OR REPLACE** ，则需要注意以下场景：
    表1不携带OR REPLACE场景说明 
    | 序号  | 场景说明                                          | 场景举例                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | 生效机制                          | 操作影响                                                                                                                                                                                                                                                                                                                                   |
    |:---|:---|:---|:---|:---|
    | 场景一 | 修改了原有程序包类的实现逻辑，重新创建的函数指定的JAR包名和类名保持和原有一致。     | 1. 在Spark SQL队列下已创建自定义函数F1，指定类名C1，Jar包名J1。  2. 后续对J1包中函数实现做了逻辑修改，重新执行创建函数F2，指定类名C1，Jar包名J1。 说明： 注意，如果步骤2继续使用函数名F1，则会因为函数名重复导致创建失败。这时可以考虑使用**OR REPLACE，**或者替换所有作业中的函数F1为F2。    | 需要重启Spark SQL队列后新创建的自定义函数F2生效 | 1. 需要重启Spark SQL队列，影响当前运行的作业。  2. 重启队列后，影响F1原有功能，F1的功能变为和F2一样。   |
    | 场景二 | 在原有程序包类的基础上新增了类，新创建的函数指定为新增的类，包名不变。           | 1. 在Spark SQL队列下已创建自定义函数F1，指定类名C1，Jar包名J1。  2. J1的Jar包中新增了类C2，C1保持不变，新创建自定义函数F2，指定类名C2，Jar包名J1。                                                                                                                                   | 需要重启Spark SQL队列后新创建的自定义函数F2生效 | 1. 需要重启Spark SQL队列，影响当前运行的作业。  2. 重启队列后，F1的功能不变。          |
    | 场景三 | 原有程序包类的实现逻辑不变，重新打包程序包名。新创建的函数指定新JAR包名，类名保持不变。 | 1. 在Spark SQL队列下已创建自定义函数F1，指定类名C1，Jar包名J1。  2. 重新打包Jar包为J2，功能逻辑不变。新创建的自定义函数F2，指定类名C1，Jar包名J2。                                                                                                                                      | 新创建的自定义函数F2立即生效               | 无影响。                                                                                                                                                                                                                                                                                                                                   |
       
    
  
  - 如果创建自定义函数**携带OR REPLACE** ，表示需要对已有的函数内容进行功能替换并实时生效。
    ![](https://support.huaweicloud.com/sqlref-spark-dli/public_sys-resources/caution_3.0-zh-cn.png)
    - **该功能处于受限使用阶段，如需使用请提交工单申请开通。**
    
    - 如果要在所有SQL队列上立即生效，需要分别选择SQL队列执行一遍：CREATE OR REPLACE xxx FUNCTION ...，否则没有执行的队列可能延迟0-12小时生效。
    
    - 如果当前运行的作业中使用自定义函数F1，该F1函数指定类名C1，程序包名J1，作业运行了一半后，重新修改J1程序包逻辑，CREATE OR REPLACE FUNCTION F1后，后续作业使用新的F1函数逻辑，但是已执行完一半的作业使用旧的函数逻辑。
     
    表2携带OR REPLACE场景说明 
    | 序号  | 场景说明                                          | 场景举例                                                                                                                                                                                                                                                                                                                                                                                                             | 生效机制                                                                                                                                                                                                                                                                                                                                                                                                   | 操作影响                               |
    |:---|:---|:---|:---|:---|
    | 场景一 | 修改了原有程序包类的实现逻辑，重新创建的函数指定的JAR包名和类名保持和原有一致。     | 1. 在Spark SQL队列下已创建自定义函数F1，指定类名C1，Jar包名J1。  2. 后续对J1包中函数实现做了逻辑修改，重新执行创建函数F1，指定类名C1，Jar包名J1。                                          | 立即生效                                                                                                                                                                                                                                                                                                                                                                                                   | F1的功能变成新修改的逻辑。                     |
    | 场景二 | 在原有程序包类的基础上新增了类，新创建的函数指定为新增的类，包名不变。           | 1. 在Spark SQL队列下已创建自定义函数F1，指定类名C1，Jar包名J1。  2. J1的Jar包中新增了类C2，C1保持不变，新创建自定义函数F2，指定类名C2，Jar包名J1。因为是第一次创建指定C2的函数，这时该F2函数不能立即生效。   | 两种生效方式 - CREATE OR REPLACE *F2* FUNCTION创建语句再执行一次生效  - 重启Spark SQL队列后生效   | 如果是选择重启Spark SQL队列，则会导致当前运行的作业受影响。 |
    | 场景三 | 修改了原有程序包类的类名，新创建的函数指定为新类名，包名不变。               | 1. 在Spark SQL队列下已创建自定义函数F1，指定类名C1，Jar包名J1。  2. J1的Jar包中修改原C1为C2，C1已经不存在，新创建自定义函数F2，指定类名C2，Jar包名J1。                                    | 立即生效                                                                                                                                                                                                                                                                                                                                                                                                   | F1功能失效，F2功能立即生效。                   |
    | 场景四 | 原有程序包类的实现逻辑不变，重新打包程序包名。新创建的函数指定新JAR包名，类名保持不变。 | 1. 在Spark SQL队列下已创建自定义函数F1，指定类名C1，Jar包名J1。  2. 重新打包Jar包为J2，功能逻辑不变。新创建的自定义函数F2，指定类名C1，Jar包名J2。                                    | 立即生效                                                                                                                                                                                                                                                                                                                                                                                                   | 无影响。                               |
       
    
    
   
 
#### 示例
创建函数mergeBill。
```
CREATE FUNCTION mergeBill AS 'com.xxx.hiveudf.MergeBill'
  using jar 'obs://onlyci-7/udf/MergeBill.jar';
```
