
# 执行SQL语句
应用程序通过执行SQL语句来操作数据库。本章节通过示例介绍CREATE TABLE、INSERT、UPDATE、DELETE和SELECT等操作。
- [执行普通SQL语句]
- [执行批量插入处理语句]
- [执行预处理更新语句]
- [执行删除语句]
- [执行查询语句]
- [调用存储过程]
 #### 执行普通SQL语句
前置条件：已连接数据库，连接对象为conn。
执行普通SQL语句，创建customer_t1表的步骤如下：
1. 调用Connection接口的createStatement方法创建语句对象stmt。 
   ```
   Statement stmt = conn.createStatement();
   ```
   
   
2. 调用Statement接口的executeUpdate方法执行SQL语句。 
   ```
   int rc = stmt.executeUpdate("CREATE TABLE customer_t1(c_customer_sk INTEGER, c_customer_name VARCHAR(32));");
   ```
   
   
3. 调用Statement接口的close方法关闭语句对象stmt。 
   ```
   stmt.close();
   ```
   
   
![](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/public_sys-resources/note_3.0-zh-cn.png)
- 数据库接收不在事务块中的执行请求，若包含多条语句时，将被打包成一个事务。事务块中不支持VACUUM操作。若任一语句执行失败，整个请求将被回滚。

- 使用Statement执行多条语句时，应以分号（;）作为各语句间的分隔符。存储过程、函数和匿名块不支持多条语句执行。当preferQueryMode=simple时，语句执行不进行逻辑解析，此场景下无法使用分号（;）作为多条语句间的分隔符。
- 斜杠（/）可作为创建存储过程、函数、匿名块、包体的结束符。当preferQueryMode=simple时，语句执行不进行逻辑解析，此场景下无法使用斜杠（/）作为结束符。
- JDBC会对prepareStatement中的SQL语句进行缓存，可能导致内存膨胀。若JVM内存较小，建议调整[preparedStatementCacheSizeMiB](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/gaussdb-12-0086.html#ZH-CN_TOPIC_0000002590200684__p732066154619)或[preparedStatementCacheQueries](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/gaussdb-12-0086.html#ZH-CN_TOPIC_0000002590200684__p12320266461)参数值。
- 在编写SQL语句时，若需添加注释，请避免注释内容紧贴SQL关键字。
 
 #### 执行批量插入处理语句
预处理语句仅编译和优化一次，可通过设置不同的参数值多次使用。由于已经预先编译完成，后续使用会减少执行时间。因此，对于需要重复执行的SQL操作，建议使用预处理语句。
前置条件：已通过[执行普通SQL语句]创建customer_t1表。
执行预处理语句，批量插入数据的步骤如下：
1. 调用Connection接口的prepareStatement方法创建预处理语句对象pst。 
   ```
   PreparedStatement pst = conn.prepareStatement("INSERT INTO customer_t1 VALUES (?,?)");
   ```
   
   
2. 针对每条数据要调用对应接口设置参数，并调用addBatch方法将SQL语句添加到批处理中。 
   ```
   for (int i = 0; i < 3; i++) {
      pst.setInt(1, i);
      pst.setString(2, "data " + i);
      pst.addBatch();
   }
   ```
   
   
3. 调用PreparedStatement接口的executeBatch方法执行批处理。 
   ```
   pst.executeBatch();
   ```
   
   
4. 调用PreparedStatement接口的close方法关闭预处理语句对象pst。 
   ```
   pst.close();
   ```
   
   
![](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/public_sys-resources/note_3.0-zh-cn.png)
在数据库批处理操作中，应遵循以下关键原则以确保性能：
1. 持续执行原则
   - 批处理程序应保持持续执行，避免中途终止。
   
   - 异常终止会显著影响数据库整体性能。
    
2. 事务管理优化
   - 必须禁用自动提交功能。
     ```
     conn.setAutoCommit(false);
     ```
     
   
   - 采用分批提交策略而非单行提交。
   
   - 根据数据量和系统资源确定合适的批量大小。
    
 
 #### 执行预处理更新语句
前置条件：已通过[执行批量插入处理语句]向customer_t1表插入数据。
执行预处理SQL语句对数据进行更新的步骤如下：
1. 调用Connection接口的prepareStatement方法创建预编译语句对象pstmt。 
   ```
   PreparedStatement pstmt = conn.prepareStatement("UPDATE customer_t1 SET c_customer_name = ? WHERE c_customer_sk = 1");
   ```
   
   
2. 调用PreparedStatement接口的setString方法设置参数。 
   ```
   pstmt.setString(1, "new Data");
   ```
   
   
3. 调用PreparedStatement接口的executeUpdate方法执行预编译SQL语句。 
   ```
   int rowcount = pstmt.executeUpdate();
   ```
   
   
4. 调用PreparedStatement接口的close方法关闭预编译语句对象pstmt。 
   ```
   pstmt.close();
   ```
   
   
![](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/public_sys-resources/notice_3.0-zh-cn.png)
prepareStatement设置绑定参数后，最终会构建成B报文或U报文，并在下一步执行SQL语句时发送给服务端。但B报文或U报文存在最大长度限制（不能超过1023MB）。若一次绑定的数据过大，可能因报文过长导致异常。因此，prepareStatement设置绑定参数时需评估并控制数据大小，以避免超出报文上限。
 #### 执行删除语句
前置条件：已[执行预处理更新语句]。
执行删除语句的步骤如下：
1. 调用Connection接口的createStatement方法创建语句对象stmtd。 
   ```
   Statement stmtd = conn.createStatement();
   ```
   
   
2. 调用Statement接口的executeUpdate方法执行SQL语句，删除customer_t1表中c_customer_sk为0的记录。 
   ```
   int rd = stmtd.executeUpdate("DELETE FROM customer_t1 WHERE c_customer_sk = 0;");
   ```
   
   
3. 调用Statement接口的close方法关闭语句对象stmtd。 
   ```
   stmtd.close();
   ```
   
   
 
 #### 执行查询语句
前置条件：已[执行删除语句]。
执行查询语句的步骤如下：
1. 调用Connection接口的createStatement()方法创建语句对象stmts。 
   ```
   Statement stmts = conn.createStatement();
   ```
   
   
2. 调用Statement接口的executeQuery()方法执行SQL查询语句，并返回ResultSet。 
   ```
   ResultSet rs = stmts.executeQuery("SELECT * FROM customer_t1");
   ```
   
   
 
 #### 调用存储过程
GaussDB支持通过JDBC调用存储过程，前置条件是已连接数据库并且连接对象为conn。
**创建存储过程的示例** **如下：**
```
//在数据库中创建如下存储过程，它带有out参数。
CREATE OR REPLACE PROCEDURE testproc 
(
    psv_in1 in integer,
    psv_in2 in integer,
    psv_inout inout integer
)
AS
BEGIN
    psv_inout := psv_in1 + psv_in2 + psv_inout;
END;
/
```
**调用存储过程的步骤如下：**
1. 调用Connection的prepareCall方法创建调用语句对象cstmt。 
   ```
   CallableStatement cstmt = conn.prepareCall("{? = CALL testproc(?,?,?)}");
   ```
   
   
2. 调用CallableStatement的setInt方法设置参数。 
   ```
   cstmt.setInt(2, 50); 
   cstmt.setInt(1, 20);
   cstmt.setInt(3, 90);
   ```
   
   
3. 调用CallableStatement的registerOutParameter方法注册输出参数。 
   ```
   cstmt.registerOutParameter(4, Types.INTEGER);  //注册out类型的参数，类型为整型。
   ```
   
   
4. 调用CallableStatement的execute方法执行SQL语句。 
   ```
   cstmt.execute();
   ```
   
   
5. 调用CallableStatement的getInt方法获取out输出参数。 
   ```
   int out = cstmt.getInt(4);
   ```
   
   
6. 调用CallableStatement的close方法关闭调用语句对象cstmt。 
   ```
   cstmt.close();
   ```
   
   
![](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/public_sys-resources/note_3.0-zh-cn.png)
- 一些JDBC驱动程序提供命名参数的方法来设置参数。命名参数的方法允许根据名称而不是顺序来设置参数。若参数有默认值，则可以不指定参数值，而使用此参数的默认值。即使存储过程中参数的顺序发生了变更，也不必修改应用程序。目前GaussDB数据库的JDBC驱动程序不支持此方法。
- conn.prepareCall("{? = CALL testproc(?,?,?)}")，执行存储过程绑定参数时，可以按照占位符的顺序绑定参数并注册第一个参数为出参；也可以按照存储过程中的参数顺序绑定参数并注册第四个参数为出参，上述用例为此场景，注册第四个参数为出参。
- 当游标作为存储过程的返回值时，如果使用JDBC调用该存储过程，返回的游标将不可用。
- 存储过程不能和普通SQL在同一条语句中执行。
- 存储过程中inout类型参数必须注册为出参。
 
#### 在语句中添加单分片执行语法
前置条件是已连接数据库，连接对象为conn，创建test表并插入数据。
1. 通过调用Connection对象的setClientInfo(String name,String value)方法来设置nodeName参数。 
   ```
   conn.setClientInfo("nodeName","datanode1");
   ```
   
   
2. 执行SQL语句，其中包括使用Statement对象的executeQuery(String sql)和execute(String sql)以及PreparedStatement对象的executeQuery()和execute()方法。 
   ```
   PreparedStatement pstm = conn.prepareStatement("SELECT * FROM test");
   pstm.execute();
   pstm.executeQuery();
   Statement stmt=conn.createStatement();
   stmt.execute("SELECT * FROM test");
   stmt.executeQuery("SELECT * FROM test");
   ```
   
   
3. 关闭参数，将参数值设置为空字符串。 
   ```
   conn.setClientInfo("nodeName","");
   ```
   
   
![](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/public_sys-resources/notice_3.0-zh-cn.png)
- 该功能基于单分片执行功能进行适配，所以使用前请确认使用的数据库是否支持单分片执行。
- nodeName参数开启后：
  - 执行SQL语句后需手动关闭参数，否则会对其他查询语句的执行产生影响。
  
  - 当前连接所有的语句都会到指定的DN上去执行。
  
  - PreparedStatement对象的缓存机制会受到影响，已经缓存的语句会被清空，之后执行的带单分片查询的语句都不再缓存，直到参数关闭后缓存功能恢复。
   
- 禁止多个线程对同一连接的nodeName参数进行修改。
 
