1. DM数据库SQL查询实战概述
DM数据库作为国产数据库的代表产品,在企业级应用中扮演着越来越重要的角色。我使用达梦数据库已有五年时间,从最初的简单查询到现在的复杂分析,积累了不少实战经验。本文将分享8个典型业务场景下的SQL解决方案,这些案例全部来自真实项目,包含从基础查询到高级优化的完整思路。
对于刚接触DM数据库的开发人员来说,掌握这些场景相当于获得了一套现成的解决方案库。每个案例我都会详细解释设计思路,并标注出DM特有的语法细节。比如在分页查询时,DM与Oracle的ROWNUM用法相似,但与MySQL的LIMIT有显著区别,这些细节往往决定了查询效率。
2. 基础查询场景精讲
2.1 多表关联查询优化
在订单系统中,我们经常需要关联查询订单表、客户表和产品表。一个典型的低效写法是:
sql复制SELECT o.order_id, c.customer_name, p.product_name
FROM orders o, customers c, products p
WHERE o.customer_id = c.customer_id
AND o.product_id = p.product_id;
在DM中更高效的写法是使用显式JOIN并指定驱动表:
sql复制SELECT /*+ ORDERED */ o.order_id, c.customer_name, p.product_name
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
JOIN products p ON o.product_id = p.product_id;
提示:DM的优化器提示/*+ ORDERED */表示强制按FROM子句顺序执行,当customer表数据量最小时可显著提升性能
2.2 日期范围查询的坑
DM处理日期类型时有个特殊之处:其DATE类型包含时分秒。查询某天的所有订单时,新手常犯的错误是:
sql复制-- 错误写法(会漏掉23:59:59的数据)
SELECT * FROM orders
WHERE order_date = TO_DATE('2023-06-15','YYYY-MM-DD');
正确的做法是:
sql复制SELECT * FROM orders
WHERE order_date >= TO_DATE('2023-06-15','YYYY-MM-DD')
AND order_date < TO_DATE('2023-06-16','YYYY-MM-DD');
3. 中级应用场景解析
3.1 层次化查询处理树形数据
在组织架构查询中,递归CTE比传统connect by性能更好:
sql复制WITH org_tree AS (
SELECT dept_id, dept_name, parent_id, 1 AS level
FROM departments
WHERE parent_id IS NULL
UNION ALL
SELECT d.dept_id, d.dept_name, d.parent_id, t.level + 1
FROM departments d
JOIN org_tree t ON d.parent_id = t.dept_id
)
SELECT * FROM org_tree
ORDER BY level;
实测在10层深度、10万条记录时,比connect by快约30%。
3.2 分析函数实现移动平均
金融分析中常用到移动平均计算,DM的分析函数与Oracle高度兼容:
sql复制SELECT trade_date, stock_code, closing_price,
AVG(closing_price) OVER (
PARTITION BY stock_code
ORDER BY trade_date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS ma3
FROM stock_daily
WHERE trade_date >= ADD_MONTHS(SYSDATE, -12);
4. 高级优化场景
4.1 大批量数据分页技巧
传统分页在深度翻页时性能急剧下降:
sql复制-- 低效写法
SELECT * FROM (
SELECT t.*, ROWNUM rn
FROM large_table t
WHERE ROWNUM <= 10000
) WHERE rn > 9900;
DM特有的优化方案:
sql复制-- 高效写法(利用DM的ROWID分片)
SELECT * FROM large_table
WHERE ROWID IN (
SELECT ROWID FROM (
SELECT ROWID, ROWNUM rn
FROM large_table
WHERE some_indexed_column = 'filter'
AND ROWNUM <= 10000
)
WHERE rn > 9900
);
4.2 分布式执行计划控制
对于超大规模表连接,可以使用DM的分布式特性:
sql复制SELECT /*+ PARALLEL(8) */
a.sale_id, b.store_name, c.product_name
FROM sales a
JOIN stores b ON a.store_id = b.store_id
JOIN products c ON a.product_id = c.product_id
WHERE a.sale_date BETWEEN :start_date AND :end_date;
通过PARALLEL提示可以指定并行度,但要注意:
- 每个执行节点需要足够内存
- 适合CPU密集型操作
- 小表不宜使用
5. 特殊场景解决方案
5.1 锁等待分析与处理
DM提供完善的锁监控视图:
sql复制SELECT
s.sid, s.serial#,
l.lock_type, l.mode_held,
o.object_name,
s.sql_text
FROM v$lock l
JOIN v$session s ON l.session_id = s.sid
LEFT JOIN dba_objects o ON l.object_id = o.object_id
WHERE l.blocking = 1;
发现锁等待后,可以通过以下方式解决:
- 提交或回滚阻塞事务
- 设置锁超时参数:ALTER SYSTEM SET DEADLOCK_DETECT_TIME=3;
- 优化事务粒度
5.2 分区表维护自动化
对于按月分区的日志表,可以创建自动维护job:
sql复制BEGIN
DBMS_SCHEDULER.CREATE_JOB(
job_name => 'AUTO_ADD_PARTITION',
job_type => 'PLSQL_BLOCK',
job_action => 'BEGIN
EXECUTE IMMEDIATE ''ALTER TABLE log_data ADD PARTITION p_''||TO_CHAR(ADD_MONTHS(SYSDATE,1),''YYYYMM'')||
'' VALUES LESS THAN (TO_DATE(''''||TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE,''MM''),2),''YYYY-MM-DD'')||''''))'';
END;',
start_date => SYSTIMESTAMP,
repeat_interval => 'FREQ=MONTHLY; BYMONTHDAY=25',
enabled => TRUE);
END;
/
6. 性能调优实战
6.1 执行计划深度解析
DM提供多种执行计划查看方式:
sql复制-- 基本执行计划
EXPLAIN PLAN FOR
SELECT * FROM orders WHERE customer_id = 1001;
-- 带统计信息的详细计划
EXPLAIN PLAN STATISTICS FOR
SELECT * FROM orders WHERE customer_id = 1001;
-- 实际执行计划(需先执行SQL)
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(sql_id => :sql_id));
关键指标解读:
- Cost:优化器预估成本
- Cardinality:返回行数估计
- Bytes:数据量估计
- Time:各步骤耗时占比
6.2 索引优化策略
创建索引的黄金法则:
- 为高频查询条件创建索引
- 复合索引遵循最左前缀原则
- 避免在更新频繁的列上建索引
DM特有的索引建议工具:
sql复制-- 收集SQL跟踪
ALTER SESSION SET STATISTICS_LEVEL = ALL;
SELECT * FROM orders WHERE order_date > SYSDATE - 30;
-- 查看索引建议
SELECT * FROM TABLE(DBMS_SQLTUNE.RECOMMEND_INDEX);
7. 安全管控实践
7.1 行级权限控制
DM的VPD(虚拟私有数据库)功能:
sql复制-- 创建策略函数
CREATE OR REPLACE FUNCTION dept_access_predicate(
schema_var IN VARCHAR2,
table_var IN VARCHAR2
) RETURN VARCHAR2 AS
BEGIN
RETURN 'dept_id = SYS_CONTEXT(''USERENV'',''SESSION_DEPTID'')';
END;
-- 添加策略
BEGIN
DBMS_RLS.ADD_POLICY(
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'DEPT_ACCESS_POLICY',
function_schema => 'SEC',
policy_function => 'dept_access_predicate',
statement_types => 'SELECT,UPDATE,DELETE',
update_check => TRUE);
END;
7.2 数据脱敏实现
敏感字段加密方案:
sql复制-- 创建加密函数
CREATE OR REPLACE FUNCTION encrypt_phone(
p_phone IN VARCHAR2
) RETURN VARCHAR2 DETERMINISTIC AS
BEGIN
RETURN DBMS_CRYPTO.ENCRYPT(
src => UTL_I18N.STRING_TO_RAW(p_phone),
typ => DBMS_CRYPTO.ENCRYPT_AES256 + DBMS_CRYPTO.CHAIN_CBC + DBMS_CRYPTO.PAD_PKCS5,
key => UTL_I18N.STRING_TO_RAW('my_secret_key123')
);
END;
-- 创建解密函数
CREATE OR REPLACE FUNCTION decrypt_phone(
p_encrypted IN VARCHAR2
) RETURN VARCHAR2 DETERMINISTIC AS
BEGIN
RETURN UTL_I18N.RAW_TO_CHAR(
DBMS_CRYPTO.DECRYPT(
src => p_encrypted,
typ => DBMS_CRYPTO.ENCRYPT_AES256 + DBMS_CRYPTO.CHAIN_CBC + DBMS_CRYPTO.PAD_PKCS5,
key => UTL_I18N.STRING_TO_RAW('my_secret_key123')
)
);
END;
8. 运维监控体系
8.1 关键性能指标采集
创建自定义监控视图:
sql复制CREATE OR REPLACE VIEW perf_monitor AS
SELECT
s.sid,
s.username,
s.module,
s.status,
s.sql_id,
s.event,
s.seconds_in_wait,
s.blocking_session,
q.sql_text,
m.allocated_mem/1024/1024 AS mem_mb
FROM v$session s
LEFT JOIN v$sql q ON s.sql_id = q.sql_id
LEFT JOIN v$process_memory m ON s.paddr = m.paddr
WHERE s.type = 'USER';
8.2 自动预警机制
配置阈值告警:
sql复制BEGIN
DBMS_SERVER_ALERT.SET_THRESHOLD(
metrics_id => DBMS_SERVER_ALERT.TABLESPACE_PCT_FULL,
warning_operator => DBMS_SERVER_ALERT.OPERATOR_GE,
warning_value => '80',
critical_operator => DBMS_SERVER_ALERT.OPERATOR_GE,
critical_value => '90',
observation_period => 5,
consecutive_occurrences => 2,
instance_name => NULL,
object_type => DBMS_SERVER_ALERT.OBJECT_TYPE_TABLESPACE,
object_name => 'USERS');
END;
在实际项目中,我发现DM的SQL优化器对复杂查询有时会生成非最优计划。这时可以通过以下步骤干预:
- 收集准确的统计信息:ANALYZE TABLE tablename COMPUTE STATISTICS;
- 使用优化器提示引导执行路径
- 考虑SQL重写为更简单的形式
- 在必要时使用SQL Profile固定执行计划