简介:面向Oracle数据库开发者与DBA的19c版SQL语言参考手册,聚焦标准SQL语法及Oracle特有函数查询,适合需要系统查阅函数用法、核对语法细节或解决日常开发问题的中高级用户。压缩包内为1个PDF文件,约14MB,内容对应官方SQL Language Reference,可集中检索数学、字符串、日期时间、类型转换、加密及集合聚合等函数分类,也覆盖大数据、云计算等场景下的新增函数说明。已有185人学习,便于在离线环境中快速定位函数定义、参数与使用限制,减少反复搜索零散资料的时间。手册同时载明版权与许可约束,适合作为案头工具书配合实际项目查阅,帮助读者更规范地理解Oracle 19c的SQL能力边界并提升开发与排错效率。
1. Oracle 19c 常用函数为什么老手也要翻手册:从一次三分钱的差错说起
凌晨两点对一个账脚本,金额字段用 NVL 兜了底,结果汇总比明细少了三分钱——查了两小时,根子是隐式类型转换把 NUMBER 先变成了字符串再参与运算。Oracle 数据库 19c 版 SQL 语言参考手册里把常用函数列得足够全,但函数的行为边界散落在单行函数、类型转换、格式模型三处章节里,翻不到就只剩玄学。这篇笔记按「先给 19c 的函数分类建索引,再逐类给可复现的写法,最后把踩过的坑摊开」的顺序走,适合日常写 SQL、维护老系统、从其他数据库转过来的同行:新手能照着跑通,熟手可以对着参数边界挑毛病。
2. 19c 函数体系怎么分类:先建索引再查手册
函数查不到,往往是分类没建好。Oracle 19c 的函数按手册的组织方式大致分四块:单行函数(字符、数值、日期时间、转换、通用、正则)、聚合函数、分析函数,以及 JSON/XML/集合这类专用函数。日常写业务 SQL 时,真正高频的也就三十来个,剩下的属于「知道它在哪一类、真要用时能翻到」就够。这一章先把分类和索引立起来,再拿字符与空值函数做第一批落地样本。
2.1 手册的四类划分:哪些必须随手写,哪些查就行
把函数按「记忆优先级」分三档,比按字母表背效率高得多。第一档是每天都会用到的:NVL、COALESCE、DECODE、CASE、SUBSTR、INSTR、TRIM、TO_CHAR、TO_DATE、SYSDATE、ROUND、TRUNC、COUNT、SUM、ROW_NUMBER。这一档要闭着眼能写。第二档是每周可能碰一次的:REGEXP_* 系列、LISTAGG、LAG/LEAD、MONTHS_BETWEEN、LAST_DAY、EXTRACT。第三档是知道有、用的时候翻手册确认参数顺序的:JSON_TABLE、XMLAGG、VALIDATE_CONVERSION、REMAINDER。
| 类别 | 代表函数 | 记忆优先级 | 常见误用 |
|---|---|---|---|
| 字符 | SUBSTR、INSTR、LPAD、TRIM、REPLACE | 高 | 起点从 0 写、按字节和按字符混淆 |
| 空值通用 | NVL、NVL2、COALESCE、NULLIF、DECODE | 高 | 参数类型不一致触发隐式转换 |
| 数值 | ROUND、TRUNC、MOD、CEIL、FLOOR | 中 | 负数方向的截断与取整 |
| 日期时间 | ADD_MONTHS、LAST_DAY、TRUNC(date)、EXTRACT | 高 | 日期相减单位是天,不是秒 |
| 转换 | TO_CHAR、TO_DATE、TO_NUMBER、CAST | 高 | 格式模型写错不报错,只出错值 |
| 正则 | REGEXP_LIKE、REGEXP_SUBSTR、REGEXP_REPLACE | 中 | 大表上性能塌方 |
| 聚合 | COUNT、SUM、AVG、LISTAGG | 高 | NULL 参与分母、拼接超长 |
| 分析 | ROW_NUMBER、RANK、LAG、SUM OVER | 中 | 默认窗口不是全分区 |
建索引的具体做法很简单:在自己的笔记里按上面八行建八个标题,每用到一个新函数就把它记到对应标题下,顺带记一句「它的边界是什么」。三个月下来这三十来个高频函数的边界就长在脑子里了,翻手册的频率会明显下降。
2.2 空值四件套 NVL / NVL2 / COALESCE / NULLIF 的选型
这四个函数都能处理空值,但语义和类型推导完全不同,混用是隐式转换事故的高发区。先看一段能在 19c 里直接跑的对照:
-- 四种空值处理的最小对照,19c 可直接执行 select nvl(null, 0) as a, -- 两参数:为空则返回第二个参数 nvl2(null, '有值', '空值') as b, -- 三参数:为空返回第三,非空返回第二 coalesce(null, null, '兜底') as c, -- 变参:从左到右返回第一个非空值 nullif('A', 'A') as d -- 两参数相等返回 NULL,否则返回第一个 from dual;NVL的关键在于类型以第一个参数为准,第二个参数会被强制转换成第一个参数的类型。如果第一个参数是 NUMBER、第二个是字符串,运行期就会报 ORA-01722。下面这段就是典型翻车写法:
-- 反例:NVL 第二个参数类型与第一个不一致 create table t_num (v number); insert into t_num values (1.5); select nvl(v, 'abc') from t_num; -- ORA-01722: 无效数字 select nvl(to_char(v), 'abc') from t_num; -- 正确:先统一成字符类型COALESCE是 CASE 表达式的语法糖,变参、短路求值,从左到右碰到第一个非空就停,所以把开销大的表达式放在后面是合理优化。NULLIF常被用来做「除零保护」,比如amount / nullif(qty, 0),比手写 CASE 更短。选型上我一般这么定:只有两个参数、类型明确时用 NVL;三个以上候选值用 COALESCE;要做条件判断或除零保护用 NULLIF。
提示:NVL 的两个参数都会被求值,COALESCE 碰到非空就短路。如果第二个参数是子查询或复杂函数,性能敏感的场景优先选 COALESCE。
2.3 截取、拼接与正则三件套的参数表
字符函数里最容易被忽略的是「下标从几开始」和「按字符还是按字节」。SUBSTR的起点从 1 开始,起点写 0 会被当成 1,起点写负数则从尾部倒数;长度参数省略时截到结尾。这几个边界不写清楚,分页截串时就会出现差一位的问题。
-- SUBSTR 的四个边界行为,19c 直接可跑 select substr('ABCDEFG', 3, 2) as s1, -- 'CD',起点从 1 计 substr('ABCDEFG', -3, 2) as s2, -- 'EF',负起点从尾部倒数 substr('ABCDEFG', 0, 3) as s3, -- 'ABC',0 等价于 1 length('中文') as s4, -- 2,按字符计 lengthb('中文') as s5 -- 6,按字节计(UTF8 下一个汉字 3 字节) from dual;拼接优先用||而不是CONCAT,因为CONCAT只接受两个参数,嵌套三层就得套三层括号。LPAD/RPAD的第二个参数是目标总长度,不是要补的个数,长度小于原串时会直接截断,这一点经常被写错。TRIM一次只能去掉一个字符,要去多个字符得用LTRIM/RTRIM组合,或者干脆上REGEXP_REPLACE。
正则系列在 19c 里有五个成员,参数位置各不相同,建议直接记成一张表:
| 函数 | 参数序列 | 关键参数说明 |
|---|---|---|
| REGEXP_LIKE | source, pattern, match_param | 返回布尔,只能用在 WHERE/CHECK |
| REGEXP_SUBSTR | source, pattern, position, occurrence, match_param, subexpr | occurrence 取第几次匹配,subexpr 取第几个分组 |
| REGEXP_REPLACE | source, pattern, replace_string, position, occurrence, match_param | occurrence 为 0 时全部替换 |
| REGEXP_INSTR | source, pattern, position, occurrence, return_option, match_param, subexpr | return_option=0 返回起点,1 返回终点 |
| REGEXP_COUNT | source, pattern, position, match_param | 统计匹配次数,无 occurrence 参数 |
-- 从带分隔符的文本里取第 2 段、统计分隔符、给数字打码 select regexp_substr('A01|B02|C03', '[^|]+', 1, 2) as seg2, -- 'B02' regexp_count('A01|B02|C03', '\|') as sep_cnt, -- 2 regexp_replace('订单号 A123,金额 88', '[0-9]+', '#') as masked -- '订单号 A#,金额 #' from dual;match_param里最常用的是'i'(大小写不敏感)、'c'(敏感)、'n'(让点号匹配换行)、'm'(多行模式)、'x'(忽略模式里的空白)。这几个参数是正则能不能按预期工作的开关,写复杂模式时建议显式写出来,别依赖默认值。
性能方面有个血泪经验:正则在几十万行以上的表上做过滤,CPU 开销会明显高于普通字符串函数,尤其是模式里带.*这种贪婪匹配时,回溯会让执行时间成倍增长。能用LIKE或INSTR > 0解决的,就不要上REGEXP_LIKE。
3. 日期与类型转换函数:TO_CHAR/TO_DATE 的格式模型逐位拆
日期和转换是 19c 里最「静默出错」的一类函数——格式模型写错时数据库往往不报错,只是悄悄返回一个错误的日期或数字,等你半个月后对账时才发现。这一章把日期函数的最小集合、格式模型的每一位、隐式转换的触发条件分开讲清楚。
3.1 日期函数的最小集合:ADD_MONTHS、LAST_DAY、TRUNC 与 MONTHS_BETWEEN
19c 里日期运算的基本单位是「天」,这个前提不建立起来,后面所有换算都会错。两个日期相减得到的是天数,要转小时就乘 24,转分钟再乘 60。ADD_MONTHS遇到月末会做对齐处理(1 月 31 日加一个月得到 2 月 29 日,而不是 3 月 3 日),MONTHS_BETWEEN在两个日期都是月末时返回整数,其他情况返回小数。
-- 日期函数最小集合的可运行样例 select sysdate as now, add_months(date '2024-01-31', 1) as add1, -- 2024-02-29,月末对齐 last_day(date '2024-02-05') as last_d, -- 2024-02-29 next_day(date '2024-02-05', 'MONDAY') as next_mon, -- 2024-02-12 trunc(sysdate, 'MM') as mon_begin, -- 当月 1 号 00:00:00 months_between(date '2024-03-31', date '2024-01-31') as m_btw, -- 2,两端都是月末 date '2024-02-01' - date '2024-01-01' as diff_days -- 31,单位是天 from dual;TRUNC(date, fmt)的第二个参数决定截断粒度:'YYYY'截到年初,'MM'截到月初,'DD'截到当天零点,'HH24'截到小时,'MI'截到分钟。做按日、按月聚合的报表时,TRUNC(created_at, 'MM')配合 GROUP BY 是最常见的写法。EXTRACT只能取单个字段(年、月、日、时、分、秒、时区),不能做运算,需要组合时还是得回到 TO_CHAR。
注意:
SYSDATE返回的是数据库服务器时间,带时区信息的场景要用SYSTIMESTAMP或CURRENT_TIMESTAMP,两者在跨时区部署的系统里结果可能差好几个小时。
3.2 TO_CHAR 格式模型:9 与 0、FM、RR 与 YY 的差别
格式模型是 19c 里最值得花半小时系统过一遍的东西。日期部分常用的元素就十来个,但MM(月)和MI(分)写反、HH(12 小时制)和HH24(24 小时制)混用,这类错误不会报错,只会得到错值。
| 格式元素 | 含义 | 典型输出 |
|---|---|---|
| YYYY / YY / RR | 四位年 / 两位年 / 两位年带世纪推断 | 2024 / 24 / 1995 或 2095 |
| MM / MON / MONTH | 月份数字 / 缩写 / 全称 | 03 / 3月 / 3月 |
| DD / DDD / DY | 当月第几天 / 当年第几天 / 星期缩写 | 05 / 065 / 星期二 |
| HH / HH12 / HH24 | 12 小时制 / 12 小时制 / 24 小时制 | 02 / 02 / 14 |
| MI / SS | 分钟 / 秒 | 30 / 45 |
| FF | 小数秒 | 123456 |
RR和YY的区别是世纪推断规则:RR把两位年份映射到最近的世纪(00–49 归当前世纪,50–99 归上个世纪),YY则永远拼当前世纪。这个差异在解析历史数据时是致命的。
-- 同一个两位年份,RR 与 YY 解析出的世纪不同(假设当前是 2024 年) select to_char(to_date('95-03-01', 'RR-MM-DD'), 'YYYY-MM-DD') as r_result, -- 1995-03-01 to_char(to_date('95-03-01', 'YY-MM-DD'), 'YYYY-MM-DD') as y_result -- 2095-03-01 from dual; -- 数字格式:9 与 0 的差别在于是否补零 select to_char(12.5, 'FM999.99') as f1, -- 12.50 to_char(12.5, 'FM000.00') as f2, -- 012.50 to_char(1234567.8, 'FM9,999,999.99') as f3 -- 1,234,567.80 from dual;数字格式模型里,9表示「有值才显示、没值留空」,0表示「没值补零」;FM修饰符去掉前导和尾随空格;MI放在数字格式里表示负数尾缀;L取本地货币符号。做金额展示时我一般用FM999,999,990.00,既能补零到两位小数,又不会因为 FM 前后空格导致前端展示错位。
3.3 隐式转换:什么时候数据库替你转、什么时候它转错
隐式转换的规则是:当比较的两边类型不一致时,Oracle 会把字符串转成数字或把日期转成字符串,具体方向取决于上下文。这个规则本身不复杂,麻烦在于它一旦发生,绑定变量、索引、NLS 参数都会跟着出问题。
-- 反例一:字符列上做数值比较,触发隐式转换 create table t_order (order_no varchar2(32), amount varchar2(32)); select * from t_order where order_no = 10086; -- 列被转成数字,索引可能失效 select * from t_order where order_no = '10086'; -- 正确:类型一致 -- 反例二:日期列用字符串比较,依赖 NLS_DATE_FORMAT select * from t_order where created_at = '2024-01-01'; -- 结果取决于会话参数 select * from t_order where created_at = date '2024-01-01'; -- 正确:显式日期字面量判断有没有发生隐式转换,最直接的办法是看执行计划。只要过滤条件里出现TO_NUMBER("列名")或TO_CHAR("列名"),就说明转换发生在列这一侧,普通索引直接作废。
-- 看执行计划里有没有对列做函数调用 explain plan for select * from t_order where order_no = 10086; select * from table(dbms_xplan.display); -- 关注 Predicate Information 里的 TO_NUMBER(...) 字样19c 里对数据清洗场景还提供了一个VALIDATE_CONVERSION函数,用来判断一个值能不能安全转换,避免用 CASE + 异常处理去兜底:
-- 返回 1 表示可转换,0 表示不可转换,NULL 输入返回 NULL select validate_conversion('123' as number) as v1, -- 1 validate_conversion('12a' as number) as v2 -- 0 from dual;提示:只要 WHERE 条件里出现了对列的函数调用或类型不一致的比较,就默认索引失效,唯一例外是基于函数的索引专门建过。
4. 聚合与分析函数:GROUP BY 之后还得看窗口
聚合函数用错,结果不是报错而是「悄悄算偏」;分析函数用错,结果不是算偏而是「顺序不对」。这两类函数在报表类 SQL 里占比极高,19c 又对 LISTAGG 做了溢出增强,值得单独一章讲。
4.1 COUNT(*) 与 COUNT(col)、AVG 的 NULL 边界
COUNT(*)统计行数,COUNT(col)只统计该列非空的行数,两者在有 NULL 的列上差多少完全取决于数据。更隐蔽的是AVG:它的分母是COUNT(col)而不是COUNT(*),所以只要有 NULL,平均值就会比「先补零再平均」偏大。
-- 三行数据,其中一行为 NULL,看四个聚合结果 select count(*) as cnt_all, -- 3,统计所有行 count(v) as cnt_v, -- 2,跳过 NULL avg(v) as avg_v, -- 2,分母是 2 不是 3 sum(v) / count(*) as avg_wrong -- 1.33,分母用了总行数,偏小 from (select 1 as v from dual union all select null from dual union all select 3 from dual);业务上到底是「NULL 表示没有,不参与平均」还是「NULL 表示零」,必须由需求方明确。前者直接用AVG(v),后者写AVG(NVL(v, 0)),两种口径算出来的 KPI 可能差出一个量级。COUNT(DISTINCT col)会自动忽略 NULL,MEDIAN和STDDEV也遵循同样的忽略规则。
分组聚合还有个常见需求是「一次查出总计加小计」,19c 用ROLLUP和GROUPING SETS实现,比写 UNION ALL 短得多:
-- 按部门小计 + 全公司总计,一次扫描完成 select dept_id, sum(salary) as total from employees group by rollup(dept_id) order by dept_id nulls last;4.2 分析函数 OVER 子句的三条规则
分析函数的语法骨架是函数名(...) OVER (PARTITION BY ... ORDER BY ... 窗口子句),三个部分各管一件事:PARTITION BY 决定分组边界,ORDER BY 决定组内顺序,窗口子句决定「当前行能看到多远」。最容易踩的是默认窗口——一旦写了 ORDER BY,窗口默认变成RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,聚合类分析函数就变成了累积值,而不是全组值。
-- 三个排名函数的差别,以及 LAG 取上一行 select dept_id, emp_name, salary, row_number() over (partition by dept_id order by salary desc) as rn, -- 1,2,3,4 不重复 rank() over (partition by dept_id order by salary desc) as rk, -- 并列跳号 1,1,3 dense_rank() over (partition by dept_id order by salary desc) as drk, -- 并列不跳号 1,1,2 lag(salary, 1, 0) over (partition by dept_id order by salary desc) as prev_sal from employees;ROW_NUMBER适合做分页和去重(每组只留一条),RANK适合做「并列第几」的业务排名,DENSE_RANK适合做连续序号。三个函数在无并列时结果一致,一旦有并列就分道扬镳,测试用例里必须构造并列数据才验得出来。
LAG(expr, offset, default)的第二个参数是往回看几行,第三个参数是越界时的默认值。很多人只写LAG(salary),结果第一行返回 NULL,下游再一算就出偏差。加上默认值LAG(salary, 1, 0)是最省事的兜底。
分析函数还有一条硬规则:不能直接写在 WHERE 里。想按排名过滤,必须在外面套一层子查询或 WITH:
-- 取每个部门薪资前 3 名,必须先算出 rn 再过滤 select * from ( select e.*, row_number() over (partition by dept_id order by salary desc) as rn from employees e ) where rn <= 3;4.3 LISTAGG 超长与分组拼接的替代方案
LISTAGG是把多行拼成一行最顺手的函数,但它的返回类型是 VARCHAR2,19c 里上限是 4000 字节,超过就报 ORA-01489。12.2 之后引入了ON OVERFLOW TRUNCATE,可以在超长时截断并追加省略提示和计数,不用再靠外部处理。
-- 19c 中 LISTAGG 的溢出处理写法 select dept_id, listagg(emp_name, ',' on overflow truncate '...' with count) within group (order by emp_name) as names from employees group by dept_id;ON OVERFLOW TRUNCATE后面可以跟自定义的截断标记,WITH COUNT表示在末尾追加被截掉的数量,WITHOUT COUNT则不追加。不加这个子句时,只要拼接结果超过 4000 字节就直接报错,而不是静默截断,这一点反而是好事——至少你知道出问题了。
真要拼超长文本,替代方案是XMLAGG或JSON_ARRAYAGG,两者都返回 CLOB:
-- 超过 4000 字节时的替代方案,返回 CLOB select dept_id, rtrim(xmlagg(xmlelement(e, emp_name || ',')).extract('//text()').getclobval(), ',') as names from employees group by dept_id;XMLAGG的坑在于会对特殊字符(&、<、>)做 XML 转义,拼出来的字符串里可能带&这种东西,展示前需要再解一次。19c 里更推荐JSON_ARRAYAGG,返回标准 JSON 数组,前端解析成本更低。
5. 常用函数避坑排查:五条现象、原因与解决
函数用得对不对,最终要靠结果说话。这一章的五条记录都是从实际排查里攒下来的,每条按「现象 → 原因 → 解决」写,可以直接拿去做回归用例。
5.1 现象:NOT IN 查询返回空集,换成 NOT EXISTS 就正常
现象:select * from a where id not in (select id from b)返回 0 行,但 b 表里明明没有匹配的记录。
原因:NOT IN的语义是「不等于列表里的每一个值」,一旦子查询结果里包含 NULL,id <> NULL的结果是 UNKNOWN 而不是 TRUE,整个条件永远不成立,于是返回空集。这是 SQL 三值逻辑的经典表现,19c 里没有例外。
解决:要么在子查询里加where id is not null,要么改用NOT EXISTS,后者对 NULL 是安全的。数据量大时NOT EXISTS通常也更快,因为它可以走反连接(anti-join)。
-- 两种等价但行为不同的写法 select * from a where id not in (select id from b where id is not null); -- 安全写法一 select * from a where not exists (select 1 from b where b.id = a.id); -- 安全写法二5.2 现象:DECODE 判空得到「相等」,CASE 判空却得到「不等」
现象:同一份数据,用decode(col, null, '空', '非空')和case when col = null then '空' else '非空' end跑出来结果相反。
原因:DECODE在内部把两个 NULL 当作相等处理,而CASE遵循标准三值逻辑,NULL = NULL的结果是 UNKNOWN,落到 ELSE 分支。两者语义不同,不是 bug。
解决:判空统一用IS NULL,不要用= NULL;需要兼容 DECODE 语义的历史代码时,把CASE WHEN col IS NULL显式写出来,别指望优化器帮你转换。
select decode(null, null, 'eq', 'ne') as d, -- eq case when null = null then 'eq' else 'ne' end as c1, -- ne case when null is null then 'eq' else 'ne' end as c2 -- eq,正确写法 from dual;5.3 现象:ROWNUM 加 ORDER BY 取「前 10 条」,拿到的不是最大的 10 条
现象:select * from t where rownum <= 10 order by amount desc取出来的 10 行金额参差不齐,跟预期的最大的 10 条对不上。
原因:ROWNUM是在排序之前分配的,语句的执行顺序是先按过滤条件取行、边取边编号,编号到 10 就停,然后才对这 10 行排序。所以拿到的是「任意 10 行排序后的结果」,不是「排序后的前 10 行」。
解决:先排序再套一层取 ROWNUM,或者直接用 12c 之后支持的FETCH FIRST n ROWS ONLY。19c 里两种写法都可用,后者更清晰。
-- 正确写法一:子查询先排序 select * from ( select * from t order by amount desc ) where rownum <= 10; -- 正确写法二:19c 支持的 FETCH FIRST select * from t order by amount desc fetch first 10 rows only;5.4 现象:TO_DATE 解析出来的年份差了一百年
现象:to_date('95-03-01', 'YY-MM-DD')解析出 2095 年,而业务数据明明是 1995 年。
原因:YY永远拼当前世纪,当前是 2024 年就拼成 20xx;RR才会做世纪推断,把 50–99 映射到上个世纪。历史数据导入时用错格式元素,整批数据的年份都会错。
解决:解析两位年份统一用RR;更稳妥的做法是要求数据源提供四位年份,用YYYY解析,从根上避免歧义。已经入库的错误数据,可以用UPDATE ... SET col = add_months(col, -1200)之类的语句批量修正,但修正前一定要先在测试库上用 SELECT 验证范围。
5.5 现象:WHERE 里加了 REGEXP_LIKE,查询从毫秒级变成分钟级
现象:一张几百万行的表,原本走索引的查询加上regexp_like(code, '^A[0-9]{3}')之后,执行计划从 INDEX RANGE SCAN 变成全表扫描,耗时暴涨。
原因:正则是函数,对列做函数调用会让普通索引失效;同时正则引擎本身有编译和回溯开销,模式越复杂、数据量越大,CPU 消耗越明显。19c 不会自动把正则条件下推成索引扫描。
解决:优先用LIKE 'A___'或SUBSTR(code, 1, 1) = 'A'这类可索引的等价写法;确实需要正则时,考虑建基于函数的索引,或者先用其他条件把数据量缩小再上正则。
-- 可索引的等价写法 select * from t where code like 'A___' and length(code) = 4; -- 确需正则时,建基于函数的索引 create index idx_t_code_regex on t (case when regexp_like(code, '^A[0-9]{3}') then 1 end);6. 把 19c 常用函数做成自己的速查表:验证方法与进阶技巧
手册上的函数说明是给人查的,不是给人背的。我自己的做法是维护一张「函数 + 一句边界 + 一个可跑用例」的三列速查表,每踩一次坑就补一行。下面这张是核心部分的节选,可以直接拿去当模板。
| 函数 | 一句话边界 | 验证用例 |
|---|---|---|
| NVL | 类型以第一个参数为准,两个参数都求值 | select nvl(to_char(null), 'x') from dual; |
| COALESCE | 变参、短路求值 | select coalesce(null, null, 'x') from dual; |
| SUBSTR | 起点从 1 计,负起点从尾部数 | select substr('ABCDE', -2, 2) from dual; |
| TRUNC(date) | 第二个参数决定截断粒度 | select trunc(sysdate, 'MM') from dual; |
| TO_DATE | 两位年份用 RR,别用 YY | select to_date('95-01-01','RR-MM-DD') from dual; |
| LISTAGG | 超 4000 字节必须加 ON OVERFLOW | select listagg(x, ',' on overflow truncate) within group (order by x) from (...) ; |
| ROW_NUMBER | 不能写在 WHERE 里 | select * from (select t.*, row_number() over(order by 1) rn from t) where rn <= 3; |
速查表要配一套固定的验证动作,写完任何一条涉及函数的 SQL,
本文还有配套的精品资源,点击获取