
# Spark作业中SQL语句占位符使用指南
#### 什么是占位符
占位符是在SQL语句中使用的一种参数标记方法，用":参数名"的形式代替具体的数值或文本。它相当于先写好完整 SQL 逻辑框架，用占位符标记待填充数据位，执行时系统自动将用户输入数据安全地填充到这些位置。这种方法将SQL逻辑框架与数据完全分离，避免了直接拼接用户输入带来的SQL注入风险和语法错误问题，既保证了安全性又提高了代码的可维护性。
![](https://support.huaweicloud.com/api-aidatalake/public_sys-resources/note_3.0-zh-cn.png)
占位符只能替换值，不能用于替换表名、列名、关键字等SQL结构。
#### 为什么要用占位符
- 类型安全。 声明value_type后，系统会自动将传入的值转换为正确的类型，避免隐式转换错误。
  例如：传入"123"但声明为DATE类型，系统会按日期规则处理，而不是当作字符串"123"直接拼接。
  
- 防止SQL注入。 占位符只能替换值，不能替换列名、表名等SQL结构。参数和SQL逻辑完全分离，从根本上防御SQL注入攻击。
  
- 执行计划复用。 Spark SQL引擎可以缓存不含具体参数的逻辑计划。用不同参数值执行同一SQL时，可直接复用缓存的计划，提升编译速度。
  
 
#### 使用场景
- Spark SQL作业中使用占位符，通过API执行单条SQL语句时，可以在SQL中使用占位符，然后在请求参数中指定占位符的值和类型。
- Spark Job作业中使用占位符，通过API提交spark_sql_scripting_job类型的作业时，可以在SQL脚本文件中使用占位符。
 
#### SQL语句占位符支持的参数类型
表1SQL语句占位符支持的参数类型 
| 类型        | 说明          | 传入值格式            | 传入值示例          |
|:---|:---|:---|:---|
| STRING    | 字符串类型（缺省类型） | 直接传字符串。          | hello          |
| INTEGER   | 整数类型        | 直接传数字字符串。        | 100            |
| DECIMAL   | 定点数类型       | 直接传小数字符串。        | 12.1           |
| DATE      | 日期类型        | 仅支持long值（毫秒时间戳）。 | -1500000000000 |
| TIMESTAMP | 时间戳类型       | 仅支持long值（毫秒时间戳）。 | -1779866327983 |
   
#### Spark SQL作业中使用SQL语句占位符
1. 在SQL语句中用":参数名"标记占位符。 
   ```
   INSERT INTO test_users_0527 VALUES (3, 'liu', 20, :param2, :param3)
   ```
   其中":param2"和":param3"就是占位符，可以自定义。
   
   
2. 在请求体的"parameters"数组中指定每个占位符的值和类型。 
   ```
   {
       "endpoint_name": "spark-sql-cwk",
       "catalog_context": {
           "catalog_name": "lzh",
           "database_name": "test"
       },
       "statement": "INSERT INTO test_users_0527 VALUES (3, 'liu', 20, :param2, :param3)",
       "parameters": [
           {
               "key": "param2",
               "value": "string1",
               "value_type": "STRING"
           },
           {
               "key": "param3",
               "value": "123",
               "value_type": "DATE"
           }
       ],
       "spark_config": {
           "spark.driver.cores": "2"
       },
       "timeout": {
           "launching_timeout": 10800
       },
       "labels": [
           {
               "key": "sql",
               "value": "test"
           }
       ]
   }
   ```
   表2请求体字段说明 
   | 字段         | 是否必填 | 说明                      |
   |:---|:---|:---|
   | key        | 是    | 占位符名称，必须与SQL中的":参数名"一致。 |
   | value      | 是    | 要替换的真实值。                |
   | value_type | 否    | 值的类型，不填时默认为string。      |
      
   ![](https://support.huaweicloud.com/api-aidatalake/public_sys-resources/note_3.0-zh-cn.png)
   请求体中"parameters"中的参数顺序不重要，系统是通过"key"名称进行匹配。
   
   
 
#### Spark Job作业中使用SQL语句占位符
1. 编写SQL脚本文件（必须用 BEGIN 和 END 包裹）。 
   例如，编写名为"sql_scripting_insert_ljh.sql"的文件。
   ```
   BEGIN
   SELECT * FROM (VALUES (3, :param1, 20, :param2, :param3, :param4)) AS
     t(id, name, age, date, timestamp, decimal_test);
   END
   ```
   
   
2. 在作业配置的"sql_scripting_parameters"中指定占位符的值。 
   ```
   {
       "endpoint_name": "spark-job-vu9e",
       "name": "test-job-dsds",
       "spark_version": "4.0.0",
       "job_config": {
           "type": "spark_sql_scripting_job",
           "sql_scripting_file": "obs://obs-jiaxg/zhaoyu/spark_sql_scripting/sql/sql_scripting_insert_ljh.sql",
           "sql_scripting_result_to_obs": true,
           "sql_scripting_parameters": [
               {
                   "key": "param1",
                   "value": "liu"
               },
               {
                   "key": "param2",
                   "value": "-1500000000000",
                   "value_type": "DATE"
               },
               {
                   "key": "param3",
                   "value": "-1779866327983",
                   "value_type": "TIMESTAMP"
               },
               {
                   "key": "param4",
                   "value": "12.1",
                   "value_type": "DECIMAL"
               }
           ]    
       }
   }
   ```
   表3请求体字段说明 
   | 字段                          | 是否必填 | 说明               |
   |:---|:---|:---|
   | sql_scripting_file          | 是    | SQL脚本文件在OBS上的路径。 |
   | sql_scripting_result_to_obs | 否    | 结果是否写入OBS。       |
   | sql_scripting_parameters    | 否    | 占位符参数列表。         |
      
   ![](https://support.huaweicloud.com/api-aidatalake/public_sys-resources/note_3.0-zh-cn.png)
   - 请求体中"sql_scripting_parameters"内"key"的值必须与SQL中的":参数名"一致。
   
   - 请求体中"sql_scripting_parameters"中的参数顺序不重要，系统是通过key名称进行匹配。
    
   
   
 
#### 参考模板
- Spark SQL作业请求体模板。
  ```
  {
      "endpoint_name": "你的端点名",
      "catalog_context": {
          "catalog_name": "catalog名",
          "database_name": "数据库名"
      },
      "statement": "含占位符的SQL语句",
      "parameters": [
          {
              "key": "占位符名",
              "value": "替换值",
              "value_type": "STRING|INTEGER|DECIMAL|DATE|TIMESTAMP"
          }
      ]
  }
  ```
  
- Spark Job作业请求体模板。
  ```
  {
      "endpoint_name": "你的端点名",
      "name": "你的作业名",
      "spark_version": "4.0.0",
      "job_config": {
          "type": "spark_sql_scripting_job",
          "sql_scripting_file": "OBS上的SQL脚本路径",
          "sql_scripting_result_to_obs": true,
          "sql_scripting_parameters": [
              {
                  "key": "占位符名",
                  "value": "替换值",
                  "value_type": "STRING|INTEGER|DECIMAL|DATE|TIMESTAMP"
              }
          ]
      }
  }
  ```
  
 
