华为云用户手册

  • 示例 强制使用Custom Plan 1 2 3 create table t (a int, b int, c int); prepare p as select /*+ use_cplan */ * from t where a = $1; explain execute p(1); 计划如下。可以看到过滤条件为入参的实际值,即此计划为Custom Plan。 强制使用Generic Plan 1 2 3 deallocate p; prepare p as select /*+ use_gplan */ * from t where a = $1; explain execute p(1); 计划如下。可以看到过滤条件为待填充的入参,即此计划为Generic Plan。
  • 参数说明 @queryblock 见指定Hint所处于的查询块Queryblock,可省略,表示在当前查询块生效,该hint只在指定为最外层的queryblock时才会生效。 param表示参数名。 value表示参数的取值。 目前支持使用Hint设置生效的参数有 布尔类: enable_bitmapscan、enable_hashagg、enable_hashjoin、enable_indexscan、enable_indexonlyscan、enable_material、enable_mergejoin、enable_nestloop、enable_index_nestloop、enable_seqscan、enable_sort、enable_tidscan、partition_iterator_elimination、partition_page_estimation、enable_functional_dependency、var_eq_const_selectivity、enable_inner_unique_opt 整型类: query_dop 浮点类: cost_weight_index、default_limit_rows、seq_page_cost、random_page_cost、cpu_tuple_cost、cpu_index_tuple_cost、cpu_operator_cost、effective_cache_size 设置不在白名单中的参数,参数取值不合法,或hint语法错误时,不会影响查询执行的正确性。使用explain(verbose on)执行可以看到hint解析错误的报错提示。 GUC参数的hint只在最外层查询生效,子查询内的GUC参数hint不生效。 视图定义内的GUC参数hint不生效。 CREATE TABLE ... AS ... 查询最外层的GUC参数hint可以生效。
  • Hint的错误、冲突及告警 Plan Hint的结果会体现在计划的变化上,可以通过explain来查看变化。 Hint中的错误不会影响语句的执行,只是不能生效,该错误会根据语句类型以不同方式提示用户。对于explain语句,hint的错误会以warning形式显示在界面上,对于非explain语句,会以debug1级别日志显示在日志中,关键字为PLANHINT。 hint的错误分为以下类型: 语法错误 语法规则树归约失败,会报错,指出出错的位置。 例如:hint关键字错误,leading hint或join hint指定2个表以下,其它hint未指定表等。一旦发现语法错误,则立即终止hint的解析,所以此时只有错误前面的解析完的hint有效。 例如: 1 leading((t1 t2)) nestloop(t1) rows(t1 t2 #10) nestloop(t1)存在语法错误,则终止解析,可用hint只有之前解析的leading((t1 t2))。 语义错误 表不存在,存在多个,或在leading或join中出现多次,均会报语义错误。 scanhint中的index不存在,会报语义错误。 另外,如果子查询提升后,同一层出现多个名称相同的表,且其中某个表需要被hint,hint会存在歧义,无法使用,需要为相同表增加别名规避。 hint重复或冲突 如果存在hint重复或冲突,只有第一个hint生效,其它hint均会失效,会给出提示。 hint重复是指,hint的方法及表名均相同。例如:nestloop(t1 t2) nestloop(t1 t2)。 hint冲突是指,table list一样的hint,存在不一样的hint,hint的冲突仅对于每一类hint方法检测冲突。 例如:nestloop (t1 t2) hashjoin (t1 t2),则后面与前面冲突,此时hashjoin的hint失效。注意:nestloop(t1 t2)和no mergejoin(t1 t2)不冲突。 leading hint中的多个表会进行拆解。例如:leading ((t1 t2 t3))会拆解成:leading((t1 t2)) leading(((t1 t2) t3)),此时如果存在leading((t2 t1)),则两者冲突,后面的会被丢弃。(例外:指定内外表的hint若与不指定内外表的hint重复,则始终丢弃不指定内外表的hint。) 子链接提升后hint失效 子链接提升后的hint失效,会给出提示。通常出现在子链接中存在多个表连接的场景。提升后,子链接中的多个表不再作为一个整体出现在join中。 hint未被使用 非等值join使用hashjoin hint或mergejoin hint。 不包含索引的表使用indexscan hint或indexonlyscan hint。 通常只有在索引列上使用过滤条件才会生成相应的索引路径,全表扫描将不会使用索引,因此使用indexscan hint或indexonlyscan hint将不会使用。 indexonlyscan只有输出和谓词条件列仅包含索引列才会使用,否则指定时hint不会被使用。 多个表存在等值连接时,仅尝试有等值连接条件的表的连接,此时没有关联条件的表之间的路径将不会生成,所以指定相应的leading,join,rows hint将不使用,例如:t1 t2 t3表join,t1和t2, t2和t3有等值连接条件,则t1和t3不会优先连接,leading(t1 t3)不会被使用。 如果子链接未被提升,则blockname hint不会被使用。 父主题: 使用Plan Hint进行调优
  • 参数说明 @queryblock 见指定Hint所处于的查询块Queryblock,可省略,表示在当前查询块生效。 “#”、“+”、“-”、“*”,进行行数估算hint的四种操作符号。#表示直接使用后面的行数进行hint。“+”、“-”、“*”表示对原来估算的行数进行加、减、乘操作,运算后的行数最小值为1行。table_list为hint对应的单表或多表join结果集,与Join方式的Hint中table_list相同。
  • 建议 推荐使用两个表*的hint。对于两个表的采用*操作符的hint,只要两个表出现在join的两端,都会触发hint。例如:设置hint为rows(t1 t2 * 3),对于(t1 t3 t4)和(t2 t5 t6)join时,由于t1和t2出现在join的两端,所以其join的结果集也会应用该hint规则乘以3。 rows hint支持在单表、多表、function table及subquery scan table的结果集上指定hint。
  • 参数说明 join_table_list为表示表join顺序的hint字符串,可以包含当前层的任意个表(别名),或对于子查询提升的场景,也可以包含子查询的hint别名,同时任意表可以使用括号指定优先级,表之间使用空格分隔。 @queryblock 见指定Hint所处于的查询块Queryblock,可省略,表示在当前查询块生效。 表只能用单个字符串表示,不能带schema。 表如果存在别名,需要优先使用别名来表示该表。
  • 参数说明 relname为查询中表table的名字,表有别名时,需要优先使用别名alias,此时relname=alias。当表名中有特殊符号,比如“@”、“.”时,relname需要用""括起来,以避免和查询块和schema名的声明重合。比如表名relnametest@1,需要写做 "relnametest@1"。 schema为表所处的schema,可缺省,缺省时Hint不区分schema对relname进行查找。 queryblock为表所处的queryblock,可缺省,缺省时Hint不区分queryblock对relname进行查找。
  • 算子级调优介绍 一个查询语句要经过多个算子步骤才会输出最终的结果。由于个别算子耗时过长导致整体查询性能下降的情况比较常见。这些算子是整个查询的瓶颈算子。通用的优化手段是EXPLAIN ANALYZE/PERFORMANCE命令查看执行过程的瓶颈算子,然后进行针对性优化。 如下面的执行过程信息中,Hashagg算子的执行时间占总时间的:(51016-13535)/ 56476 ≈66%,此处Hashagg算子就是这个查询的瓶颈算子,在进行性能优化时应当优先考虑此算子的优化。
  • 统计信息调优介绍 GaussDB 是基于代价估算生成的最优执行计划。优化器需要根据analyze收集的统计信息进行行数估算和代价估算,因此统计信息对优化器行数估算和代价估算起着至关重要的作用。通过analyze收集全局统计信息,主要包括:pg_class表中的relpages和reltuples;pg_statistic表中的stadistinct、stanullfrac、stanumbersN、stavaluesN、histogram_bounds等。
  • 规格约束 告警字符串长度上限为2048。如果告警信息超过这个长度(例如存在大量未收集统计信息的超长表名,列名等信息)则不告警,只上报warning: WARNING, "Planner issue report is truncated, the rest of planner issues will be skipped" 如果query存在limit节点(即查询语句中包含limit),则不会上报limit节点以下的Operator级别的告警。
  • 告警场景 目前支持对多列/单列统计信息未收集导致性能问题的场景上报告警。 如果存在单列或者多列统计信息未收集,则上报相关告警。调优方法可以参考更新统计信息和统计信息调优。 告警信息示例: 整表的统计信息未收集: 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))
  • 选择数据类型 高效数据类型,主要包括以下三方面: 尽量使用执行效率比较高的数据类型 一般来说整型数据运算(包括“=”、“>”、“<”、“≧”、“≦”、“≠”等常规的比较运算,以及group by)的效率比字符串、浮点数要高。 尽量使用短字段的数据类型 长度较短的数据类型不仅可以减小数据文件的大小,提升I/O性能;同时也可以减小相关计算时的内存消耗,提升计算性能。比如对于整型数据,如果可以用smallint就尽量不用int,如果可以用int就尽量不用bigint。 使用一致的数据类型 表关联列尽量使用相同的数据类型。如果表关联列数据类型不同,数据库必须动态地转化为相同的数据类型进行比较,这种转换会带来一定的性能开销。 父主题: 审视和修改表定义
  • 使用分区表 分区表是把逻辑上的一张表根据某种方案分成几张物理块进行存储。这张逻辑上的表称之为分区表,物理块称之为分区。分区表是一张逻辑表,不存储数据,数据实际是存储在分区上的。分区表和普通表相比具有以下优点: 改善查询性能:对分区对象的查询可以仅搜索自己关心的分区,提高检索效率。 增强可用性:如果分区表的某个分区出现故障,表在其他分区的数据仍然可用。 方便维护:如果分区表的某个分区出现故障,需要修复数据,只修复该分区即可。 GaussDB支持的分区表为一级分区表和二级分区表,其中一级分区表包括范围分区表、间隔分区表、列表分区表、哈希分区表四种,二级分区表包括范围分区、列表分区、哈希分区两两组合的九种。 范围分区表:将数据基于范围映射到每一个分区。这个范围是由创建分区表时指定的分区键决定的。分区键经常采用日期,例如将销售数据按照月份进行分区。 间隔分区表:是一种特殊的范围分区表,相比范围分区表,新增间隔值定义,当插入记录找不到匹配的分区时,可以根据间隔值自动创建分区。 列表分区表:将数据中包含的键值分别存储在不同的分区中,依次将数据映射到每一个分区,分区中包含的键值由创建分区表时指定。 哈希分区表:将数据根据内部哈希算法依次映射到每一个分区中,包含的分区个数由创建分区表时指定。 二级分区表:由范围分区、列表分区、哈希分区任意组合得到的分区表,其一级分区和二级分区均可以使用前面三种定义方式。 父主题: 审视和修改表定义
  • 背景信息 ANALYZE语句可收集与数据库中表内容相关的统计信息,统计结果存储在系统表PG_STATISTIC中。查询优化器会使用这些统计数据,以生成最有效的执行计划。 建议在执行了大批量插入/删除操作后,例行对表或全库执行ANALYZE语句更新统计信息。目前默认收集统计信息的采样比例是30000行(即:guc参数default_statistics_target默认设置为100),如果表的总行数超过一定行数(大于1600000),建议设置guc参数default_statistics_target为-2,即按2%收集样本估算统计信息。 对于在批处理脚本或者存储过程中生成的中间表,也需要在完成数据生成之后显式的调用ANALYZE。 对于表中多个列有相关性且查询中有同时基于这些列的条件或分组操作的情况,可尝试收集多列统计信息,以便查询优化器可以更准确地估算行数,并生成更有效的执行计划。
  • 操作步骤 收集SQL中涉及到的所有表的统计信息。在数据库中,统计信息是优化器生成计划的源数据。没有收集统计信息或者统计信息陈旧往往会造成执行计划严重劣化,从而导致性能问题。从经验数据来看,10%左右性能问题是因为没有收集统计信息。具体请参见更新统计信息。 通过查看执行计划来查找原因。如果SQL长时间运行未结束,通过EXPLAIN命令查看执行计划,进行初步定位。如果SQL可以运行出结果来,则推荐使用EXPLAIN ANALYZE或EXPLAIN PERFORMANCE查看执行计划及实际运行情况,以便更精准地定位问题原因。有关执行计划的详细介绍请参见SQL执行计划介绍。 审视和修改表定义。 针对EXPLAIN或EXPLAIN PERFORMANCE信息,定位SQL慢的具体原因以及改进措施,具体参见典型SQL调优点。 通常情况下,有些SQL语句可以通过查询重写转换成等价的,或特定场景下等价的语句。重写后的语句比原语句更简单,且可以简化某些执行步骤达到提升性能的目的。查询重写方法在各个数据库中基本是通用的。经验总结:SQL语句改写规则介绍了几种常用的通过改写SQL进行调优的方法。 如果使用上述常规手段无法分析慢SQL根因的场景,还可以通过使用plan trace特性来分析慢SQL根因,具体请参见PLAN TRACE使用介绍。
  • 执行计划显示信息 除了设置不同的执行计划显示格式外,还可以通过不同的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查看执行计划及其实际的运行信息,以便更精准地定位问题原因。
  • 调优手段之统计信息 GaussDB优化器是典型的基于代价的优化 (Cost-Based Optimization,简称CBO)。在这种优化器模型下,数据库根据表的元组数、字段宽度、NULL记录比率、distinct值、MCV值、HB值等表的特征值,以及一定的代价计算模型,计算出每一个执行步骤的不同执行方式的输出元组数和执行代价(cost),进而选出整体执行代价最小/首元组返回代价最小的执行方式进行执行。这些特征值就是统计信息。从上面描述可以看出统计信息是查询优化的核心输入,准确的统计信息将帮助优化器选择最合适的查询规划,一般来说我们通过analyze语法收集整个表或者表的若干个字段的统计信息,周期性地运行ANALYZE,或者在对表的大部分内容做了更改之后马上运行它是个好习惯。 DDL可能会导致统计信息发生变化,进而导致计划跳变。当表上做了DDL操作后,应注意统计信息是否需要重新收集。
  • 调优手段之GUC参数 查询优化的主要目的是为查询语句选择高效的执行方式。 如下SQL语句: 1 2 select count(1) from customer inner join store_sales on (ss_customer_sk = c_customer_sk); 在执行customer inner join store_sales的时候,GaussDB支持Nested Loop、Merge Join和Hash Join三种不同的Join方式。优化器会根据表customer和表store_sales的统计信息估算结果集的大小以及每种Join方式的执行代价,然后对比选出执行代价最小的执行计划。 正如前面所说,执行代价计算都是基于一定的模型和统计信息进行估算,当因为某些原因代价估算不能反映真实的cost的时候,我们就需要通过GUC参数设置的方式让执行计划倾向更优规划。例如:random_page_cost参数表示优化器计算一次非顺序抓取磁盘页面的开销,该参数默认值为4。当机器磁盘随机读取的速度较快时,比如SSD设备,可以将该参数的值适当调小,更改后,索引扫描的代价降低,生成计划时更倾向于选择索引扫描的方式。
  • SQL调优指南 SQL调优的唯一目的是“资源利用最大化”,即CPU、内存、磁盘IO三种资源利用最大化。所有调优手段都是围绕资源使用开展的。所谓资源利用最大化是指SQL语句尽量高效,节省资源开销,以最小的代价实现最大的效益。比如做典型点查询的时候,可以用seqscan+filter(即读取每一条元组和点查询条件进行匹配)实现,也可以通过indexscan实现,显然indexscan可以以更小的代价实现相同的效果。 根据硬件资源和客户的业务特征确定合理的数据库部署方案和表定义是数据库在多数情况下满足性能要求的基础。下文的调优说明假设您已根据“软件安装”指引在安装过程中按照合理的数据库方案完成了安装,且已经根据“开发设计建议”的指引进行了数据库设计。 Query执行流程 SQL执行计划介绍 调优流程 更新统计信息 审视和修改表定义 典型SQL调优点 经验总结:SQL语句改写规则 SQL调优关键参数调整 使用Plan Hint进行调优 PLAN TRACE使用介绍 使用SQL PATCH进行调优 实际调优案例
  • 调试 用户可以根据自己的需要,通过修改实例数据目录下的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 报告导致所有会话退出的原因。 父主题: 应用程序开发教程
  • 命名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预编译器不能看到这个声明。 父主题: 预处理指令
  • 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; 父主题: 预处理指令
  • 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 宿主变量是类型boolean而数据库中的数据既不是'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。 父主题: 错误处理
  • 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语句中使用。 示例如下: /* 当出现一个告警时它打印一个消息,发生一个错误时中止程序。 */ 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语句忽略。 父主题: 错误处理
  • 执行具有输入参数的语句 准备一个普通语句,通过替换参数(在想要替换参数的地方输入问号)执行它的特定版本。使用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语句
  • 执行没有结果集的语句 执行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语句
  • 处理字符串 处理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类型的值,它们将被存储为字符串形式。 父主题: 宿主变量
  • 类型映射 当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 boolean 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。 父主题: 宿主变量
共100000条