• 沒有找到結果。

● 【建议】除ETL程序外,应该尽量避免向客户端返回大量结果集的操作。如果结果 集过大,应考虑业务设计是否合理。

● 【建议】使用事务方式执行DDL和DML操作。例如,truncate table、update table、delete table、drop table等操作,一旦执行提交就无法恢复。对于这类操 作,建议使用事务进行封装,必要时可以进行回滚。

● 【建议】在查询编写时,建议明确列出查询涉及的所有字段,不建议使用

“SELECT *”这种写法。一方面基于性能考虑,尽量减少查询输出列;另一方面 避免增删字段对前端业务兼容性的影响。

● 【建议】在访问表对象时带上schema前缀,可以避免因schema切换导致访问到 非预期的表。

● 【建议】超过3张表或视图进行关联(特别是full join)时,执行代价难以估算。

建议使用WITH TABLE AS语句创建中间临时表的方式增加SQL语句的可读性。

● 【建议】尽量避免使用笛卡尔积和Full join。这些操作会造成结果集的急剧膨胀,

同时其执行性能也很低。

● 【关注】NULL值的比较只能使用IS NULL或者IS NOT NULL的方式判断,其他任 何形式的逻辑判断都返回NULL。例如:NULL<>NULL、NULL=NULL和NULL<>1 返回结果都是NULL,而不是期望的布尔值。

● 【关注】需要统计表中所有记录数时,不要使用count(col)来替代count(*)。

count(*)会统计NULL值(真实行数),而count(col)不会统计。

● 【关注】在执行count(col)时,将“值为NULL”的记录行计数为0。在执行 sum(col)时,当所有记录都为NULL时,最终将返回NULL;当不全为NULL时,

“值为NULL”的记录行将被计数为0。

● 【关注】count(多个字段)时,多个字段名必须用圆括号括起来。例如,

count( (col1,col2,col3) )。注意:通过多字段统计行数时,即使所选字段都为 NULL,该行也被计数,效果与count(*)一致。

● 【关注】count(distinct col)用来计算该列不重复的非NULL的数量,NULL将不被 计数。

● 【关注】count(distinct (col1,col2,...))用来统计多列的唯一值数量,当所有统计字 段都为NULL时,也会被计数,同时这些记录被认为是相同的。

● 【关注】通过常量来过滤数据时,会根据常量的数据类型和匹配列的数据类型来 查找用于这两种数据类型计算的函数,如果找不到对应的函数,则会相应的进行 隐式数据类型转化,然后再根据转化后的数据类型查找用于转化后的数据类型计 算的函数。

SELECT * FROM test WHERE timestamp_col = 20000101;

上述例子中,假设timestamp_col是timestamp类型,则会先查找支持timestamp 类型和int类型(常量数字认为是int类型)“等于”运算的函数,如果找不到,则 把timestamp_col和常量数字隐式类型转化成text类型来计算。

● 【建议】尽量避免标量子查询语句的出现。标量子查询是出现在select语句输出列 表中的子查询,在下面例子中,括号内部分即为一个标量子查询语句:

SELECT id, (SELECT COUNT(*) FROM films f WHERE f.did = s.id) FROM staffs_p1 s;

标量子查询往往会导致查询性能急剧劣化,在应用开发过程中,应当根据业务逻 辑,对标量子查询进行等价转换,将其写为表关联。

● 【建议】在where子句中,应当对过滤条件进行排序,把选择读较小(筛选出的 记录数较少)的条件排在前面。

● 【建议】where子句中的过滤条件,尽量符合单边规则。即把字段名放在比较条 件的一边,优化器在某些场景下会自动进行剪枝优化。形如col op expression,

其中col为表的一个列,op为‘=’、‘>’的等比较操作符,expression为不含列 名的表达式。例如,

SELECT id, from_image_id, from_person_id, from_video_id FROM face_data WHERE current_timestamp(6) - time < '1 days'::interval;

改写为:

SELECT id, from_image_id, from_person_id, from_video_id FROM face_data where time >

current_timestamp(6) - '1 days'::interval;

● 【建议】尽量避免不必要的排序操作。排序需要耗费大量的内存及CPU,如果业 务逻辑许可,可以组合使用order by和limit,减小资源开销。GaussDB(DWS)默 认按照ASC & NULL LAST进行排序。

● 【建议】使用ORDER BY子句进行排序时,显式指定排序方式(ASC/DESC),

NULL的排序方式(NULL FIRST/NULL LAST)。

● 【建议】不要单独依赖limit子句返回特定顺序的结果集。如果部分特定结果集,

可以将ORDER BY子句与Limit子句组合使用,必要时也可以使用offset跳过特定结 果。

● 【建议】在保障业务逻辑准确的情况下,建议尽量使用UNION ALL来代替 UNION。

● 【建议】如果过滤条件只有OR表达式,可以将OR表达式转化为UNION ALL以提 升性能。使用OR的SQL语句经常无法优化,导致执行速度慢。例如,将下面语句

SELECT * FROM scdc.pub_menu

WHERE (cdp= 300 AND inline=301) OR (cdp= 301 AND inline=302) OR (cdp= 302 AND inline=301);

转换为:

SELECT * FROM scdc.pub_menu WHERE (cdp= 300 AND inline=301) union all

SELECT * FROM scdc.pub_menu WHERE (cdp= 301 AND inline=302) union all

SELECT * FROM tablename WHERE (cdp= 302 AND inline=301)

● 【建议】当in(val1, val2, val3…)表达式中字段较多时,建议使用in (values (va11), (val2),(val3)…)语句进行替换。优化器会自动把in约束转换为非关联子查 询,从而提升查询性能。

● 【建议】在关联字段不存在NULL值的情况下,使用(not) exist代替(not) in。例 如,在下面查询语句中,当T1.C1列不存在NULL值时,可以先为T1.C1字段添加 NOT NULL约束,再进行如下改写。

SELECT * FROM T1 WHERE T1.C1 NOT IN (SELECT T2.C2 FROM T2);

可以改写为:

SELECT * FROM T1 WHERE NOT EXISTS (SELECT * FROM T1,T2 WHERE T1.C1=T2.C2);

说明

● 如果不能保证T1.C1列的值为NOT NULL的情况下,就不能进行上述改写。

● 如果T1.C1为子查询的输出,要根据业务逻辑确认其输出是否为NOT NULL。

● 【建议】通过游标进行翻页查询,而不是使用LIMIT OFFSET语法,避免多次执行 带来的资源开销。游标必须在事务中使用,执行完后务必关闭游标并提交事务。

8.6 PL/Java 使用

● 【关注】Java UDF可以实现一些java逻辑计算,禁止在Java UDF中封装业务。

● 【关注】禁止在Java函数中使用任何方式连接数据库,包括但不限于JDBC。

● 【关注】只能选择下表中的数据类型,不支持自定义类型、复杂数据类型(Java Array类及派生类)等:

● 【关注】不支持UDAF(用户定义聚合函数),UDTF(用户自定义表生成函 数)。

8-4 PL/Java 默认数据类型映射关系

GaussDB(DWS) Java

BOOLEAN boolean

"char" byte

bytea byte[]

SMALLINT short

INTEGER int

BIGINT long

FLOAT4 float

FLOAT8 double

CHAR java.lang.String VARCHAR java.lang.String TEXT java.lang.String name java.lang.String DATE java.sql.Timestamp

TIME java.sql.Time (stored value treated as local time)

TIMETZ java.sql.Time TIMESTAMP java.sql.Timestamp TIMESTAMPTZ java.sql.Timestamp

8.7 PL/pgSQL 使用