华为云用户手册

  • 统计信息调优介绍 GaussDB Kernel是基于代价估算生成的最优执行计划。优化器需要根据analyze收集的统计信息行数估算和代价估算,因此统计信息对优化器行数估算和代价估算起着至关重要的作用。通过analyze收集全局统计信息,主要包括:pg_class表中的relpages和reltuples;pg_statistic表中的stadistinct、stanullfrac、stanumbersN、stavaluesN、histogram_bounds等。
  • 背景信息 ANALYZE语句可收集与数据库中表内容相关的统计信息,统计结果存储在系统表PG_STATISTIC中。查询优化器会使用这些统计数据,以生成最有效的执行计划。 建议在执行了大批量插入/删除操作后,例行对表或全库执行ANALYZE语句更新统计信息。目前默认收集统计信息的采样比例是30000行(即:guc参数default_statistics_target默认设置为100),如果表的总行数超过一定行数(大于1600000),建议设置guc参数default_statistics_target为-2,即按2%收集样本估算统计信息。 对于在批处理脚本或者存储过程中生成的中间表,也需要在完成数据生成之后显式的调用ANALYZE。 对于表中多个列有相关性且查询中有同时基于这些列的条件或分组操作的情况,可尝试收集多列统计信息,以便查询优化器可以更准确地估算行数,并生成更有效的执行计划。
  • 规格约束 告警字符串长度上限为2048。如果告警信息超过这个长度(例如存在大量未收集统计信息的超长表名,列名等信息)则不告警,只上报warning: WARNING, "Planner issue report is truncated, the rest of planner issues will be skipped" 如果query存在limit节点(即查询语句中包含limit),则不会上报limit节点以下的Operator级别的告警。
  • 选择数据类型 高效数据类型,主要包括以下三方面: 尽量使用执行效率比较高的数据类型 一般来说整型数据运算(包括=、>、<、≧、≦、≠等常规的比较运算,以及group by)的效率比字符串、浮点数要高。比如某客户场景中对列存表进行点查询,filter条件在一个numeric列上,执行时间为10+s;修改numeric为int类型之后,执行时间缩短为1.8s左右。 尽量使用短字段的数据类型 长度较短的数据类型不仅可以减小数据文件的大小,提升I/O性能;同时也可以减小相关计算时的内存消耗,提升计算性能。比如对于整型数据,如果可以用smallint就尽量不用int,如果可以用int就尽量不用bigint。 使用一致的数据类型 表关联列尽量使用相同的数据类型。如果表关联列数据类型不同,数据库必须动态地转化为相同的数据类型进行比较,这种转换会带来一定的性能开销。 父主题: 审视和修改表定义
  • 告警场景 目前支持对多列/单列统计信息未收集导致性能问题的场景上报告警。 如果存在单列或者多列统计信息未收集,则上报相关告警。调优方法可以参考更新统计信息和统计信息调优。 告警信息示例: 整表的统计信息未收集: Statistic Not Collect: schema_test.t1 单列统计信息未收集: Statistic Not Collect: schema_test.t2(c1,c2) 多列统计信息未收集: Statistic Not Collect: schema_test.t3((c1,c2)) 单列和多列统计信息未收集: Statistic Not Collect: schema_test.t4(c1,c2) schema_test.t4((c1,c2))
  • 调试 用户可以根据自己的需要,通过修改实例数据目录下的postgresql.conf文件中特定的配置参数来控制日志的输出,从而更好地了解数据库的运行状态。 可调整的配置参数请参见表1。 表1 配置参数 参数名称 描述 取值范围 备注 client_min_messages 配置发送到客户端信息的级别。 DEBUG5 DEBUG4 DEBUG3 DEBUG2 DEBUG1 LOG NOTICE WARNING ERROR FATAL PANIC 默认值:NOTICE 。 设置级别后,发送到客户端的信息包含所设级别及以下所有低级别会发送的信息。级别越低,发送的信息越少。 log_min_messages 配置写到服务器日志里信息的级别。 DEBUG5 DEBUG4 DEBUG3 DEBUG2 DEBUG1 INFO NOTICE WARNING ERROR LOG FATAL PANIC 默认值:WARNING。 指定某一级别后,写到日志的信息包含所有更高级别会输出的信息。级别越高,服务器日志的信息越少。 log_min_error_statement 配置写到服务器日志中错误SQL语句的级别。 DEBUG5 DEBUG4 DEBUG3 DEBUG2 DEBUG1 INFO NOTICE WARNING ERROR FATAL PANIC 缺省值:ERROR。 所有导致一个特定级别(或者更高级别)错误的SQL语句都将记录在服务器日志中。 只有系统管理员可以修改该参数。 log_min_duration_statement 配置语句执行持续的最短时间。如果某个语句的持续时间大于或者等于设置的毫秒数,则会在日志中记录该语句及其持续时间。打开这个选项可以方便地跟踪需要优化的查询。 INT类型。 默认值:30min。 单位:毫秒。 设置为-1表示关闭这个功能。 只有系统管理员可以修改该参数。 log_connections/log_disconnections 配置是否在每次会话连接或结束时向服务器日志里打印一条信息。 on:每次会话连接或结束时向日志里打印一条信息。 off:每次会话连接或结束时不向日志里打印信息。 默认值:off。 - log_duration 配置是否记录每个已完成语句的持续时间。 on:记录每个已完成语句的持续时间。 off:不记录已完成语句的持续时间。 默认值:off。 只有系统管理员可以修改该参数。 log_statement 配置日志中记录哪些SQL语句。 none:不记录任何SQL语句。 ddl:记录数据定义语句。 mod:记录数据定义语句和数据操作语句。 all :记录所有语句。 默认值: none。 只有系统管理员可以修改该参数。 log_hostname 配置是否记录主机名。 on:记录主机名。 off:不记录主机名。 默认值:off。 缺省时,连接日志只记录所连接主机的IP地址。打开这个选项会同时记录主机名。 该参数同时影响查看审计结果、GS_SESSION_MEMORY_DETAIL、PG_STAT_ACTIVITY和GUC参数log_line_prefix。 上表有关参数级别的说明请参见表2。 表2 日志级别参数说明 级别 说明 DEBUG[1-5] 提供开发人员使用的信息。5级为最高级别,依次类推,1级为最低级别。 INFO 提供用户隐含要求的信息。如在VACUUM VERBOSE过程中的信息。 NOTICE 提供可能对用户有用的信息。如长标识符的截断,作为主键一部分创建的索引。 WARNING 提供给用户的警告。如在事务块范围之外的COMMIT。 ERROR 报告导致当前命令退出的错误。 LOG 报告一些管理员感兴趣的信息。如检查点活跃性。 FATAL 报告导致当前会话终止的原因。 PANIC 报告导致所有会话退出的原因。 父主题: 应用程序开发教程
  • 使用分区表 分区表是把逻辑上的一张表根据某种方案分成几张物理块进行存储。这张逻辑上的表称之为分区表,物理块称之为分区。分区表是一张逻辑表,不存储数据,数据实际是存储在分区上的。分区表和普通表相比具有以下优点: 改善查询性能:对分区对象的查询可以仅搜索自己关心的分区,提高检索效率。 增强可用性:如果分区表的某个分区出现故障,表在其他分区的数据仍然可用。 方便维护:如果分区表的某个分区出现故障,需要修复数据,只修复该分区即可。 GaussDB Kernel支持的分区表为一级分区表和二级分区表,其中一级分区表包括范围分区表、间隔分区表、列表分区表、哈希分区表四种,二级分区表包括范围分区、列表分区、哈希分区两两组合的九种。 范围分区表:将数据基于范围映射到每一个分区。这个范围是由创建分区表时指定的分区键决定的。分区键经常采用日期,例如将销售数据按照月份进行分区。 间隔分区表:是一种特殊的范围分区表,相比范围分区表,新增间隔值定义,当插入记录找不到匹配的分区时,可以根据间隔值自动创建分区。 列表分区表:将数据中包含的键值分别存储再不同的分区中,依次将数据映射到每一个分区,分区中包含的键值由创建分区表时指定。 哈希分区表:将数据根据内部哈希算法依次映射到每一个分区中,包含的分区个数由创建分区表时指定。 二级分区表:由范围分区、列表分区、哈希分区任意组合得到的分区表,其一级分区和二级分区均可以使用前面三种定义方式。 父主题: 审视和修改表定义
  • 选择存储模型 进行数据库设计时,表设计上的一些关键项将严重影响后续整库的查询性能。表设计对数据存储也有影响:好的表设计能够减少I/O操作及最小化内存使用,进而提升查询性能。 表的存储模型选择是表定义的第一步。客户业务属性是表的存储模型的决定性因素,依据下面表格选择适合当前业务的存储模型。 存储模型 适用场景 行存 点查询(返回记录少,基于索引的简单查询)。 增删改比较多的场景。 列存 统计分析类查询 (group , join多的场景)。 父主题: 审视和修改表定义
  • 操作步骤 收集SQL中涉及到的所有表的统计信息。在数据库中,统计信息是规划器生成计划的源数据。没有收集统计信息或者统计信息陈旧往往会造成执行计划严重劣化,从而导致性能问题。从经验数据来看,10%左右性能问题是因为没有收集统计信息。具体请参见更新统计信息。 通过查看执行计划来查找原因。如果SQL长时间运行未结束,通过EXPLAIN命令查看执行计划,进行初步定位。如果SQL可以运行出来,则推荐使用EXPLAIN ANALYZE或EXPLAIN PERFORMANCE查看执行计划及实际运行情况,以便更精准地定位问题原因。有关执行计划的详细介绍请参见SQL执行计划介绍。 审视和修改表定义。 针对EXPLAIN或EXPLAIN PERFORMANCE信息,定位SQL慢的具体原因以及改进措施,具体参见典型SQL调优点。 通常情况下,有些SQL语句可以通过查询重写转换成等价的,或特定场景下等价的语句。重写后的语句比原语句更简单,且可以简化某些执行步骤达到提升性能的目的。查询重写方法在各个数据库中基本是通用的。经验总结:SQL语句改写规则介绍了几种常用的通过改写SQL进行调优的方法。 如果使用上述常规手段无法分析慢SQL根因的场景,还可以通过使用plan trace特性来分析慢SQL根因,具体请参见PLAN TRACE使用介绍。
  • SQL调优指南 SQL调优的唯一目的是“资源利用最大化”,即CPU、内存、磁盘IO三种资源利用最大化。所有调优手段都是围绕资源使用开展的。所谓资源利用最大化是指SQL语句尽量高效,节省资源开销,以最小的代价实现最大的效益。比如做典型点查询的时候,可以用seqscan+filter(即读取每一条元组和点查询条件进行匹配)实现,也可以通过indexscan实现,显然indexscan可以以更小的代价实现相同的效果。 根据硬件资源和客户的业务特征确定合理的数据库部署方案和表定义是数据库在多数情况下满足性能要求的基础。下文的调优说明假设您已根据“软件安装”指引在安装过程中按照合理的数据库方案完成了安装,且已经根据“开发设计建议”的指引进行了数据库设计。 Query执行流程 SQL执行计划介绍 调优流程 更新统计信息 审视和修改表定义 典型SQL调优点 经验总结:SQL语句改写规则 SQL调优关键参数调整 使用Plan Hint进行调优 使用向量化执行引擎进行调优 PLAN TRACE使用介绍 使用SQL PATCH进行调优 实际调优案例
  • 调优手段之统计信息 GaussDB Kernel优化器是典型的基于代价的优化 (Cost-Based Optimization,简称CBO)。在这种优化器模型下,数据库根据表的元组数、字段宽度、NULL记录比率、distinct值、MCV值、HB值等表的特征值,以及一定的代价计算模型,计算出每一个执行步骤的不同执行方式的输出元组数和执行代价(cost),进而选出整体执行代价最小/首元组返回代价最小的执行方式进行执行。这些特征值就是统计信息。从上面描述可以看出统计信息是查询优化的核心输入,准确的统计信息将帮助规划器选择最合适的查询规划,一般来说我们通过analyze语法收集整个表或者表的若干个字段的统计信息,周期性地运行ANALYZE,或者在对表的大部分内容做了更改之后马上运行它是个好习惯。
  • 调优手段之GUC参数 查询优化的主要目的是为查询语句选择高效的执行方式。 如下SQL语句: 12 select count(1) from customer inner join store_sales on (ss_customer_sk = c_customer_sk); 在执行customer inner join store_sales的时候,GaussDB Kernel支持Nested Loop、Merge Join和Hash Join三种不同的Join方式。优化器会根据表customer和表store_sales的统计信息估算结果集的大小以及每种join方式的执行代价,然后对比选出执行代价最小的执行计划。 正如前面所说,执行代价计算都是基于一定的模型和统计信息进行估算,当因为某些原因代价估算不能反映真实的cost的时候,我们就需要通过guc参数设置的方式让执行计划倾向更优规划。
  • 执行计划显示信息 除了设置不同的执行计划显示格式外,还可以通过不同的EXPLAIN用法,显示不同详细程度的执行计划信息。常见有如下几种,关于更多用法请参见EXPLAIN语法说明。 EXPLAIN statement: 只生成执行计划,不实际执行。其中statement代表SQL语句。 EXPLAIN ANALYZE statement:生成执行计划,进行执行,并显示执行的概要信息。显示中加入了实际的运行时间统计,包括在每个规划节点内部花掉的总时间(以毫秒计)和它实际返回的行数。 EXPLAIN PERFORMANCE statement:生成执行计划,进行执行,并显示执行期间的全部信息。 为了测量运行时在执行计划中每个节点的开销,EXPLAIN ANALYZE或EXPLAIN PERFORMANCE会在当前查询执行上增加性能分析的开销。在一个查询上运行EXPLAIN ANALYZE或EXPLAIN PERFORMANCE有时会比普通查询明显的花费更多的时间。超支的数量依赖于查询的本质和使用的平台。 因此,当定位SQL运行慢问题时,如果SQL长时间运行未结束,建议通过EXPLAIN命令查看执行计划,进行初步定位。如果SQL可以运行出来,则推荐使用EXPLAIN ANALYZE或EXPLAIN PERFORMANCE查看执行计划及其实际的运行信息,以便更精准地定位问题原因。
  • SQLSTATE与SQLCODE SQLSTATE是一个由五个字符组成的数组。这五个字符包含数字或大写字母,它表示多种错误或告警情况的代码。SQLSTATE具有一种层次模式:前两个字符表示情况的总体分类,后三个字符表示总体情况的子类。例如:代码00000表示成功状态。 SQLCODE是一个简单的整数形式。值为0表示成功,一个正值表示带附加信息的成功,一个负值表示错误。SQL标准只定义了正值+100,它表示上一个命令返回或者影响了零行,且没有特定的负值。 表1 SQLSTATE与SQLCODE对应关系表 SQLCODE值 SQLSTATE值 含义 0 (ECPG_NO_ERROR) SQLSTATE 00000 表示没有错误。 100 (ECPG_NOT_FOUND) SQLSTATE 02000 一种无害情况,它表示上一个命令检索或者处理了零行,或者已到达游标的末尾。 在循环中处理游标时,可以使用这个代码来检测何时中止该循环,示例如下: while (1) { EXEC SQL FETCH ... ; if (sqlca.sqlcode == ECPG_NOT_FOUND) break; } 实际上WHENEVER NOT FOUND DO BREAK也会在内部这样做,所以一般不会直接使用这种方法。 -12 (ECPG_OUT_OF_MEMORY) SQLSTATE YE001 虚拟内存已被耗尽,数字值被定义为-ENOMEM。 -200 (ECPG_UNSUPPORTED) SQLSTATE YE000 预处理器产生了一些该库无法识别的内容。 -201 (ECPG_TOO_MANY_ARGUMENTS) SQLSTATE 07001 或 07002 表示命令指定的宿主变量数量超过该命令预期。 -202 (ECPG_TOO_FEW_ARGUMENTS) SQLSTATE 07001 或 07002 表示命令指定的宿主变量数量低于该命令的预期。 -203 (ECPG_TOO_MANY_MATCHES) SQLSTATE 21000 表示一个查询已经返回了多个行,但是该语句只准备存储一个结果行。 -204 (ECPG_INT_FORMAT) SQLSTATE 42804 宿主变量是类型int而数据库中的数据是一种不同的类型并且含有不能被解释为int的值。该库使用strtol()进行这种转换。 -205 (ECPG_UINT_FORMAT) SQLSTATE 42804 宿主变量是类型unsigned int而数据库中的数据是一种不同的类型并且含有不能被解释为unsigned int的值。该库使用strtoul()进行这种转换。 -206 (ECPG_FLOAT_FORMAT) SQLSTATE 42804 宿主变量是类型float而数据库中的数据是另一种类型并且含有不能被解释为float的值。该库使用strtod()进行这种转换。 -207 (ECPG_NUMERIC_FORMAT) SQLSTATE 42804 宿主变量是类型numeric而数据库中的数据是另一种类型并且含有不能被解释为numeric的值。 -208 (ECPG_INTERVAL_FORMAT) SQLSTATE 42804 宿主变量是类型interval而数据库中的数据是另一种类型并且含有一个不能被解释为interval的值。 -209 (ECPG_DATE_FORMAT) SQLSTATE 42804 宿主变量是类型date而数据库中的数据是另一种类型并且含有不能被解释为date的值。 -210 (ECPG_TIMESTAMP_FORMAT) SQLSTATE 42804 宿主变量是类型timestamp而数据库中的数据是另一种类型并且含有不能被解释为timestamp的值。 -211 (ECPG_CONVERT_BOOL) SQLSTATE 42804 宿主变量是类型bool而数据库中的数据既不是't'也不是'f'。 -212 (ECPG_EMPTY) SQLSTATE YE000 发送给SQL服务器的语句是空的(通常在一个嵌入式SQL程序中不会发生,因此它可能指向一个内部错误)。 -213 (ECPG_MISSING_INDICATOR) SQLSTATE 22002 返回了一个空值并且没有提供空值指示符。 -214 (ECPG_NO_ARRAY) SQLSTATE 42804 在要求一个数组的地方使用了一个普通变量。 -215 (ECPG_DATA_NOT_ARRAY) SQLSTATE 42804 在一个要求数组值的地方数据库返回了一个普通变量。 -216 (ECPG_ARRAY_INSERT) SQLSTATE 42804 该值不能被插入到数组中。 -220 (ECPG_NO_CONN) SQLSTATE 08003 程序尝试访问一个不存在的连接。 -221 (ECPG_NOT_CONN) SQLSTATE YE000 程序尝试访问一个存在的连接但是它没有打开(这是一个内部错误)。 -230 (ECPG_INVALID_STMT) SQLSTATE 26000 尝试使用的语句还没有被准备好。 -239 (ECPG_INFORMIX_DUPLICATE_KEY) SQLSTATE 23505 重复键错误,违背唯一约束。 -240 (ECPG_UNKNOWN_DESCRIPTOR) SQLSTATE 33000 没有找到指定的描述符,尝试使用的语句还没有被准备好。 -241 (ECPG_INVALID_DESCRIPTOR_INDEX) SQLSTATE 07009 指定的描述符超出范围。 -242 (ECPG_UNKNOWN_DESCRIPTOR_ITEM) SQLSTATE YE000 请求了一个非法的描述符(这是一个内部错误)。 -243 (ECPG_VAR_NOT_NUMERIC) SQLSTATE 07006 在执行一个动态语句期间,数据库返回了一个numeric值而宿主变量不是numeric类型的。 -244 (ECPG_VAR_NOT_CHAR) SQLSTATE 07006 在执行一个动态语句期间,数据库返回了一个非numeric值而宿主变量是numeric类型的。 -284 (ECPG_INFORMIX_SUBSELECT_NOT_ONE) SQLSTATE 21000 子查询的结果不是单一行。 -400 (ECPG_PGSQL) - SQL服务器导致了某个错误。该消息包含来自SQL服务器的错误消息。 -401 (ECPG_TRANS) SQLSTATE 08007 SQL服务器通知我们不能启动、提交或回滚事务。 -402 (ECPG_CONNECT) SQLSTATE 08001 无法建立数据库连接。 -403 (ECPG_DUPLICATE_KEY) SQLSTATE 23505 重复键错误,违背唯一约束。 -404 (ECPG_SUBSELECT_NOT_ONE) SQLSTATE 21000 子查询的结果不是单一行。 -602 (ECPG_WARNING_UNKNOWN_PORTAL) SQLSTATE 34000 指定了一个非法的游标名。 -603 (ECPG_WARNING_IN_TRANSACTION) SQLSTATE 25001 事务正在进行。 -604 (ECPG_WARNING_NO_TRANSACTION) SQLSTATE 25P01 没有活动(正在进行)的事务。 -605 (ECPG_WARNING_PORTAL_EXISTS) SQLSTATE 42P03 指定了一个现有的游标名。 ecpg为嵌入式SQL新增加的SQLSTATE码有:22002,07001,07002,07006,07009,33000,42601,42804,42P03,YE000,YE001。其余SQLSTATE码沿用内核SQLSTATE码。 SQLSCODE为-400表示ecpg检测到内核服务器返回错误,其SQLSTATE为内核相应错误的SQLSTATE。 父主题: 错误处理
  • 命名SQL描述符区域 一个命名SQL描述符区域由一个头部以及一个或多个条目描述符区域构成。头部包含与整个描述区域相关的信息,而条目描述符区域则描述结果行中的某一列。 在使用SQL描述符区域之前,需要分配一个SQL描述符区域: EXEC SQL ALLOCATE DESCRIPTOR identifier; 当不再需要这个描述符区域时,应及时释放: EXEC SQL DEALLOCATE DESCRIPTOR identifier; 要使用一个描述符区域,需要使用INTO子句声明: EXEC SQL FETCH NEXT FROM mycursor INTO SQL DESCRIPTOR mydesc; 如果结果集为空,该描述符区域仍会包含查询的元数据。 对于还没有执行的预备查询,可以使用DESCRIBE得到其结果集的元数据。 EXEC SQL BEGIN DECLARE SECTION; char *sql_stmt = "SELECT * FROM table1"; EXEC SQL END DECLARE SECTION; EXEC SQL PREPARE stmt1 FROM :sql_stmt; EXEC SQL DESCRIBE stmt1 INTO SQL DESCRIPTOR mydesc; 在DESCRIBE和FETCH语句中,INTO和USING关键词的使用相似:它们产生结果集以及一个描述符区域的元数据。 从头部检索一个描述符区域的值并且将其存储到一个宿主变量中: EXEC SQL GET DESCRIPTOR name :hostvar = field; 当前只定义了一个头部描述符区域COUNT,它存放描述符区域的条目(即结果集中包含多少列),宿主变量为一个整数类型,需从条目描述符区域中得到一个具体值: EXEC SQL GET DESCRIPTOR name VALUE num :hostvar = field; num可以是一个字符整数或者一个包含整数的宿主变量。可能的类型如下: CARDINALITY(整数):结果集中的行数 DATA:实际的数据项(这个范围的实际数据类型取决于查询) DATETIME_INTERVAL_CODE(整数):当TYPE是9时,DATETIME_INTERVAL_CODE将具有以下值之一:1表示DATE,2表示TIME,3表示TIMESTAMP,4表示TIME WITH TIME ZONE,5表示TIMESTAMP WITH TIME ZONE。 INDICATOR(整数):指示符(表示一个空值或者一个值截断) LENGTH(整数):以字符计的数据长度 NAME(string):列名 OCTET_LENGTH(整数):以字节计的数据字符表达的长度 PRECISION(整数):精度(用于类型numeric) RETURNED_LENGTH(整数):以字符计的数据长度 RETURNED_OCTET_LENGTH(整数):以字节计的数据字符表达的长度 SCALE(整数):比例(用于类型numeric) TYPE(整数):列的数据类型的数字编码 要检索字段数值并且把它存储到一个宿主变量里,使用下面的命令: EXEC SQL GET DESCRIPTOR mydesc VALUE num :hostvar = field num可以是一个字符整数或者一个包含整数的宿主变量。可能的字段有: DATA 实际数据项(因此,这个字段的数据类型依赖于这个查询) NAME(string) 字段名称 手动建立一个描述符区域为一个查询或游标提供输入参数,使用如下命令: EXEC SQL SET DESCRIPTOR name VALUE numfield = :hostvar; 在一个FETCH语句中检索多行记录且用数组类型的宿主变量来存储数据,示例如下: EXEC SQL BEGIN DECLARE SECTION; int id[5]; EXEC SQL END DECLARE SECTION; EXEC SQL FETCH 5 FROM mycursor INTO SQL DESCRIPTOR mydesc; EXEC SQL GET DESCRIPTOR mydesc VALUE 1 :id = DATA; 父主题: SQL描述符区域
  • define和undef指令 嵌入式 SQL具有类似于C语言中#define指令: EXEC SQL DEFINE name;EXEC SQL DEFINE name value;EXEC SQL UNDEF name; 示例如下: /* 定义名称 */EXEC SQL DEFINE HAVE_FEATURE;/* 定义常量 */EXEC SQL DEFINE MYNUMBER 12;EXEC SQL DEFINE MYSTRING 'abc';/* 使用 UNDEF 移除定义 */EXEC SQL UNDEF MYNUMBER; 在嵌入式SQL程序中也可以使用C语言版本的#define和#undef。区别在于定义的值会在哪里被计算,如果使用EXEC SQL DEFINE,那么ecpg预处理阶段会计算这些定义并替换值。如下示例,ecpg对其进行替换并且编译器不会解析名为MYNUMBER的任何名称或标识符: EXEC SQL DEFINE MYNUMBER 12; ... EXEC SQL UPDATE Tbl SET col = MYNUMBER; 不能把#define用于一个将要在一个嵌入式SQL查询中使用的变量,因为在这种情况下嵌入式SQL预编译器不能看到这个声明。 父主题: 预处理指令
  • sqlca 嵌入式SQL接口提供了sqlca(SQL通信区)的全局变量。sqlca包含告警和错误信息。如果在语句执行期间发生多个告警和错误,那么sqlca将只保存最后一个信息。在一个多线程的程序中,每一个线程会自动得到它的sqlca副本。 数据结构如下: struct { char sqlcaid[8]; long sqlabc; long sqlcode; struct { int sqlerrml; char sqlerrmc[SQLERRMC_LEN]; } sqlerrm; char sqlerrp[8]; long sqlerrd[6]; char sqlwarn[8]; char sqlstate[5]; } sqlca; 如果SQL语句没有发生错误,则sqlca.sqlcode为0,sqlca.sqlstate为"00000"。如果发生了告警或者错误,那么sqlca.sqlcode是负数并且sqlca.sqlstate不同于"00000"。SQLSTATE与SQLCODE的具体值可参考SQLSTATE与SQLCODE。 如果SQL语句正确执行,那么sqlca.sqlerrd[1]包含被处理行的OID,并且sqlca.sqlerrd[2]包含被处理或返回的行数。 在发生错误或告警时,sqlca.sqlerrm.sqlerrmc将包含描述该错误的字符串。sqlca.sqlerrm.sqlerrml包含存储在sqlca.sqlerrm.sqlerrmc中错误消息的长度(strlen()的结果)。注意:一些消息可能无法适应定长的sqlerrmc数组,它们将被截断。 在发生告警时,sqlca.sqlwarn[2]被设置为W。 sqlcaid、sqlabc、sqlerrp、sqlwarn以及sqlerrd的剩余元素目前未包含有用的信息。 示例如下: /* 整合WHENEVER和sqlca实现错误处理 */EXEC SQL WHENEVER SQLERROR SQLCALL print_sqlca(); void print_sqlca() { fprintf(stderr, "==== sqlca ====\n"); fprintf(stderr, "sqlcode: %ld\n", sqlca.sqlcode); fprintf(stderr, "sqlerrm.sqlerrml: %d\n", sqlca.sqlerrm.sqlerrml); fprintf(stderr, "sqlerrm.sqlerrmc: %s\n", sqlca.sqlerrm.sqlerrmc); fprintf(stderr, "sqlerrd: %ld %ld %ld %ld %ld %ld\n", sqlca.sqlerrd[0],sqlca.sqlerrd[1],sqlca.sqlerrd[2], sqlca.sqlerrd[3],sqlca.sqlerrd[4],sqlca.sqlerrd[5]); fprintf(stderr, "sqlwarn: %d %d %d %d %d %d %d %d\n", sqlca.sqlwarn[0], sqlca.sqlwarn[1], sqlca.sqlwarn[2], sqlca.sqlwarn[3], sqlca.sqlwarn[4], sqlca.sqlwarn[5], sqlca.sqlwarn[6], sqlca.sqlwarn[7]); fprintf(stderr, "sqlstate: %5s\n", sqlca.sqlstate); fprintf(stderr, "===============\n"); } 输出结果形如(此处的错误是一个拼写表名错误): ==== sqlca ==== sqlcode: -400 sqlerrm.sqlerrml: 49 sqlerrm.sqlerrmc: relation "pg_databasep" does not exist on line 38 sqlerrd: 0 0 0 0 0 0 sqlwarn: 0 0 0 0 0 0 0 0 sqlstate: 42P01 =============== 父主题: 错误处理
  • 设置回调 设置回调操作,当告警或者错误发生时,直接执行具体操作进行处理,设置回调命令如下: EXEC SQL WHENEVER condition action; condition取值范围: SQLERROR:当在SQL语句执行期间发生错误时,调用指定操作。 SQLWARNING:当在SQL语句执行期间发生告警时,调用指定操作。 NOT FOUND:当SQL语句检索或者影响零行,则调用指定操作。 action取值范围: CONTINUE:忽略回调错误条件,继续执行,通常可以用来停止break包含条件,为缺省值。 GOTO label/GO TO label:跳转到指定标签(使用C语言goto语句)。 SQLPRINT:输出消息到标准错误。 STOP:调用exit(1),终止程序。 DO BREAK:执行C语句break,只有在循环中或者switch语句中使用。 SQLCALL name (args) / DO name (args):调用具有指定参数的指定C语言函数(当前特性是实验室特性,使用时请联系华为工程师提供技术支持)。 示例如下: /* 当出现一个告警时它打印一个消息,发生一个错误时中止程序。 */EXEC SQL WHENEVER SQLWARNING SQLPRINT; EXEC SQL WHENEVER SQLERROR STOP; 语句EXEC SQL WHENEVER是SQL预处理器的一个指令,而非一个C语言语句。不管C语言程序的流程如何,该语句设置的错误或告警动作都适用于位于处理程序设置点之后的嵌入式SQL语句,除非第一个EXEC SQL WHENEVER语句和导致错误或告警情况发生的SQL语句之间为同一个情况设置了不同的动作。因此下面的两个C语言程序都不会得到预期的效果: /* * 错误 */ int main(int argc, char *argv[]) { ... if (verbose) { EXEC SQL WHENEVER SQLWARNING SQLPRINT; } ... EXEC SQL SELECT ...; ... } /* * 错误 */ int main(int argc, char *argv[]) { ... set_error_handler(); ... EXEC SQL SELECT ...; ... } static void set_error_handler(void) { EXEC SQL WHENEVER SQLERROR STOP; } 当使用DO BREAK时只能用于while/for/switch场景,且用完需要使用CONTINUE语句忽略。 父主题: 错误处理
  • ifdef,ifndef,else,elif和endif指令 ecpg提供了ifdef、ifndef、else、elif和endif条件编译指令。在预处理时,按照不同的条件去编译程序的不同部分,使用时,需要添加EXEC SQL前缀关键字。 示例如下: EXEC SQL ifndef TZVAR; EXEC SQL SET TIMEZONE TO 'GMT'; EXEC SQL elif TZNAME; EXEC SQL SET TIMEZONE TO TZNAME; EXEC SQL else; EXEC SQL SET TIMEZONE TO TZVAR; EXEC SQL endif; 父主题: 预处理指令
  • 类型映射 当ecpg应用程序在GaussDB Kernel服务器和C语言程序之间交换值时(例如:从服务器检索查询结果或者执行带有输入参数的SQL语句),在GaussDB Kernel数据类型和宿主语言变量类型(具体的C语言数据类型)之间需要进行值的转换。有两种数据类型可以使用:一些简单的GaussDB Kernel数据类型,如integer和text,可以直接被应用程序读取和写入;其他GaussDB Kernel数据类型,如timestamp和numeric,只能通过特殊库函数进行访问,参见ecpg接口参考章节。 表1 GaussDB Kernel数据类型和C变量类型之间的映射 GaussDB Kernel数据类型 宿主变量数据类型 smallint short integer int bigint long long int boolean bool character(n), varchar(n), text char[n+1], VARCHAR[n+1] double precision double real float smallserial short serial int bigserial long long int oid unsigned int name char[NAMEDATALEN] date date [a] timestamp timestamp [a] interval interval [a] decimal decimal [a] numeric numeric [a] [a]这种类型可以通过访问特殊数据类型访问。 当前仅支持对于C语言的基本数据类型的使用或者组合,不支持C++语言中string数据类型用作宿主变量数据类型。 当前ecpg仅对GaussDB Kernel SQL的常用数据类型做映射,具体支持项可参考表1。 父主题: 宿主变量
  • 处理字符串 处理SQL字符串数据类型(例如:varchar、text),有两种方式来声明宿主变量: 方式一:使用char[](一个char字符串),C语言程序中处理字符数据最常见的方式。 EXEC SQL BEGIN DECLARE SECTION; char str[50];EXEC SQL END DECLARE SECTION; 注意字符串必须控制长度,如果上述示例的宿主变量用作存放查询结果且查询命令返回的字符串长度超过49字节,那么将会发生缓冲区溢出。 方式二:使用VARCHAR类型,ecpg提供的一种特殊类型。在一个VARCHAR类型数组上的定义会被转变成一个struct类型。如下声明: VARCHAR var[180]; 会被转变成: struct varchar_var { int len; char arr[180]; } var; 要在一个VARCHAR宿主变量中存储一个字符串,该宿主变量必须被声明为包含零字节为终止符长度的字符串。字段arr存放以零字节为终止符的字符串,字段len保存存储在arr中的字符串的长度,计算长度时不包括终止符。当宿主变量被用于一个查询的输入时,如果strlen(arr)和len结果不同,将使用较短的那一个。 VARCHAR可以被写成大写或小写形式,但是不能大小写混合。 char和VARCHAR类型宿主变量也可以保存其他SQL类型的值,它们将被存储为字符串形式。 父主题: 宿主变量
  • 执行没有结果集的语句 执行EXECUTE IMMEDIATE命令示例如下: EXEC SQL BEGIN DECLARE SECTION; const char *stmt = "CREATE TABLE test1 (...);";EXEC SQL END DECLARE SECTION; EXEC SQL EXECUTE IMMEDIATE :stmt; EXECUTE IMMEDIATE可以用于不返回结果集的SQL语句,比如:DDL,INSERT,UPDATE,DELETE语句。但不能用这种方式执行检索数据的语句,比如:SELECT语句。 父主题: 执行动态SQL语句
  • 执行具有输入参数的语句 准备一个普通语句,通过替换参数(在想要替换参数的地方写上问号)执行它的特定版本。使用EXECUTE语句通过USING子句给定参数执行准备语句。示例如下: EXEC SQL BEGIN DECLARE SECTION; const char *stmt = "INSERT INTO test1 VALUES(?, ?);";EXEC SQL END DECLARE SECTION; /* PREPARE 准备一个语句用于执行 */ EXEC SQL PREPARE mystmt FROM :stmt; ... /* 单引号为有效字符,若用字符串需用双引号 */ EXEC SQL EXECUTE mystmt USING 42, 'foobar'; /* 当你不再需要预备语句时,你应该释放它 */ EXEC SQL DEALLOCATE PREPARE name; 父主题: 执行动态SQL语句
  • 声明段 要实现嵌入式SQL-C程序和数据库间的数据交互(例如:从SQL-C程序把查询语句中的参数传递给数据库,或者从数据库向嵌入式SQL-C程序传回数据),需要在特殊的标记段里面声明包含此数据的C语言变量,以便预处理器能够识别。 标记段以下面的代码开始: EXEC SQL BEGIN DECLARE SECTION; 以下面的代码结束: EXEC SQL END DECLARE SECTION; 在此之间,必须有常规的C语言变量声明,比如: int x = 4;char foo[16], bar[16]; 标记段代码开始和结束之间声明的宿主变量类型必须为当前支持的数据类型,详见表1。 可以隐式地创建一个声明段声明变量:EXEC SQL int i = 4。 那些不在SQL命令里使用的变量可以在这些特殊的声明段外面声明。 结构体或者联合体的定义也必须在DECLARE段中列出。否则预处理器就无法处理这些类型,因为它不知道定义。 父主题: 宿主变量
  • 检索查询 对于常用的检索查询,嵌入式SQL提供了常规命令SELECT和FETCH的特殊变体。这些命令使用特殊的INTO子句,用以指定检索出来的数值存储在哪些宿主变量里。SELECT用于返回单行的查询,FETCH用于使用游标返回多行的查询。 使用SELECT /* * 假定有这个表: * CREATE TABLE test1 (a int, b varchar(50)); */EXEC SQL BEGIN DECLARE SECTION; int v1; VARCHAR v2;EXEC SQL END DECLARE SECTION; ... EXEC SQL SELECT a, b INTO :v1, :v2 FROM test; INTO子句出现在选择列表和FROM子句之间。选择列表和INTO后面的列表的元素(也叫目标列表)个数必须相同。 使用FETCH EXEC SQL BEGIN DECLARE SECTION; int v1; VARCHAR v2;EXEC SQL END DECLARE SECTION; ... EXEC SQL DECLARE foo CURSOR FOR SELECT a, b FROM test; ... do { ... EXEC SQL FETCH NEXT FROM foo INTO :v1, :v2; ... } while (...); 这里的INTO子句出现在所有SQL子句后面。 父主题: 宿主变量
  • 宿主变量 本节详细介绍如何在C语言程序和嵌入式SQL程序之间使用宿主变量传递数据。在嵌入式SQL-C程序中,我们将C语言作为宿主语言,将EXEC SQL [Command]语句认为是宿主语言的嵌入式SQL,因此将C语言程序中用于嵌入式SQL语句的变量称为宿主变量。 概述 声明段 检索查询 类型映射 处理字符串 使用非初级类型的宿主变量 访问特殊数据类型 处理非初级SQL数据类型 父主题: 基于ecpg开发
  • 概述 在嵌入式SQL中进行C语言程序和SQL语句之间的数据传递不需要把数据粘贴到语句中,只需要在SQL语句里写上C语言变量的名称,前缀一个冒号即可。示例如下: EXEC SQL INSERT INTO sometable VALUES (:v1, 'foo', :v2); 这个语句引用了两个C语言变量:v1和v2,并且使用一个普通的SQL字串文本,这表明一条SQL语句内并不限制只使用某一种数据。 父主题: 宿主变量
  • 示例 EXEC SQL WHENEVER NOT FOUND CONTINUE; EXEC SQL WHENEVER NOT FOUND DO BREAK; EXEC SQL WHENEVER SQLWARNING SQLPRINT; EXEC SQL WHENEVER SQLWARNING DO warn(); EXEC SQL WHENEVER SQLERROR sqlprint; EXEC SQL WHENEVER SQLERROR SQLCALL print2(); EXEC SQL WHENEVER SQLERROR DO handle_error("select"); EXEC SQL WHENEVER SQLERROR DO sqlnotice(NULL, NONO);EXEC SQL WHENEVER SQLERROR DO sqlprint(); EXEC SQL WHENEVER SQLERROR GOTO error_label; EXEC SQL WHENEVER SQLERROR STOP; 使用WHENEVER NOT FOUND BREAK来处理结果集的循环: int main(void) { EXEC SQL CONNECT TO testdb AS con1; EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; EXEC SQL ALLOCATE DESCRIPTOR d; EXEC SQL DECLARE cur CURSOR FOR SELECT current_database(), 'hoge', 256; EXEC SQL OPEN cur; /* 当到达结果集末尾时,跳出循环 */ EXEC SQL WHENEVER NOT FOUND DO BREAK; while (1) { EXEC SQL FETCH NEXT FROM cur INTO SQL DESCRIPTOR d; ... } EXEC SQL CLOSE cur; EXEC SQL COMMIT; EXEC SQL DEALLOCATE DESCRIPTOR d; EXEC SQL DISCONNECT ALL; return 0; }
  • 参数说明 connection_target 以下列形式之一指定连接的目标服务器: [ database_name ] [ @host ] [ :port ]:通过TCP/IP连接。 unix:postgresql://host [ :port ] / [ database_name ] [ ?connection_option ]:通过Unix域套接字连接。 tcp:postgresql://host [ :port ] / [ database_name ] [ ?connection_option ]:通过TCP/IP连接。 SQL string constant:包含上述形式之一的值。 connection_name 用于该连接的一个可选标识符,可以在其他命令中引用它。可以是一个SQL标识符或者一个宿主变量。 connection_user 用于数据库连接的用户名。 使用user_name/password、user_name SQLIDENTIFIED BY password或者user_name USING password之一,这个参数也能指定用户名和口令。 用户名和口令可以是SQL标识符、字符串常量或者宿主变量。 上述参数中斜体部分为变量,请根据实际情况进行修改。
  • 示例 指定连接参数的变体的示例: EXEC SQL CONNECT TO "connectdb" AS main; EXEC SQL CONNECT TO "connectdb" AS second; EXEC SQL CONNECT TO 'connectdb' AS main; EXEC SQL CONNECT TO REGRESSDB1 as main; EXEC SQL CONNECT TO connectdb AS :id; EXEC SQL CONNECT TO connectdb AS main USER connectuser/connectdb; EXEC SQL CONNECT TO connectdb AS main USER connectuser USING "connectdb"; EXEC SQL CONNECT TO connectdb AS main; EXEC SQL CONNECT TO tcp:postgresql://localhost/connectdb USER connectuser IDENTIFIED BY connectpw; EXEC SQL CONNECT TO tcp:postgresql://localhost:20/connectdb USER connectuser SQLIDENTIFIED BY connectpw; EXEC SQL CONNECT TO unix:postgresql://localhost/connectdb USER connectuser SQLIDENTIFIED BY "connectpw"; EXEC SQL CONNECT TO unix:postgresql://localhost/connectdb USER connectuser USING "connectpw"; 使用宿主变量指定连接参数的示例: int main(void) { EXEC SQL BEGIN DECLARE SECTION; char *dbname = "testdb"; /* 数据库名 */ char *user = "testuser"; /* 连接用户名 */ char *pwd = "pwd_123_pwd"; /* 密码 */ char *connection = "tcp:postgresql://localhost:5432/testdb"; /* 连接字符串 */ char ver[256]; /* 存储版本字符串的缓冲区 */ EXEC SQL END DECLARE SECTION; ECPGdebug(1, stderr); EXEC SQL CONNECT TO :dbname; EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; EXEC SQL SELECT version() INTO :ver; EXEC SQL DISCONNECT; printf("version: %s\n", ver); EXEC SQL CONNECT TO :connection USER :user USING :pwd; EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT; EXEC SQL SELECT version() INTO :ver; EXEC SQL DISCONNECT; printf("version: %s\n", ver); return 0; }
共100000条