若没有指定 USING LONG ROW 存储选项时,插入 VARCHAR 数据类型的实际最大存储长度由数据库页面大小决定;如果指定了 USING LONG ROW 存储选项,则插入 VARCHAR 数据类型的长度不受数据库页面大小限制。VARCHAR 类型在表达式计算中的长度上限不受页面大小限制,为 32767。
-- 用正则表达式替换非数字字符 SELECT data, TO_NUMBER (REGEXP_REPLACE (data, '[^0-9]', '')) AS deptno FROM V ORDERBY2; -- 使用 translate 函数,直接替换掉非数字字符 SELECT data, TO_NUMBER (TRANSLATE (data, '0123456789'|| data, '0123456789')) AS deptno FROM V ORDERBY2;
创建分隔列表
使用 listagg 分析函数实现合并显示,同 sum 一样,listagg 函数也起到汇总结果作用。sum 将数值结果累计求和,而 listagg 是把字符串的结果连在一起。
1 2 3 4 5
SELECT deptno,SUM (sal) AS total_sal, LISTAGG (name, ',') WITHINGROUP (ORDERBY name) AS total_name FROM v GROUPBY deptno;
提取子串与分解 IP 地址
通过 regexp_substr 实现第 n 个子串的分割,字符串拆分。
1 2 3 4 5 6 7 8
-- 比如在"CLARK,KATE,JAMES"字符串中提取出“KATE” SELECT REGEXP_SUBSTR (v.name,'[^,]+',1,2) AS 子串 FROM v; -- 将 IP 地址“192.168.1.111”中的各段取出来 SELECT REGEXP_SUBSTR (v.ip,'[^.]+',1,1) a, REGEXP_SUBSTR (v.ip,'[^.]+',1,2) b, REGEXP_SUBSTR (v.ip,'[^.]+',1,3) c, REGEXP_SUBSTR (v.ip,'[^.]+',1,4) d FROM (SELECT'192.168.1.111'AS ip FROM DUAL) v;
-- 查询每个部门员工的平均薪资、最小薪资、最大薪资、总工资及总记录 SELECT job_id, AVG (salary) AS 平均值, MIN (salary) AS 最小值, MAX (salary) AS 最大值, SUM (salary) AS 工资合计, COUNT (*) AS 总行数 FROM dmhr.employee GROUPBY job_id;
当表中没有数据时,不加 group by 会返回一行数据,加了 group by 无数据返回。
生成累计和
使用分析函数 sum (…) over (order by…) 可以生成累计和。
1 2 3 4 5 6 7 8 9
-- 按员工编号排序对员工的工资进行累加,用 listagg 模拟出每一行是哪些值相加 SELECT employee_id AS 编号, employee_name AS 姓名, salary AS 人工成本, SUM (salary) OVER (ORDERBY employee_id) AS 成本累计, ( SELECTLISTAGG (salary, '+') WITHINGROUP (ORDERBY employee_id) FROM dmhr.employee b WHERE b.job_id =11AND b.employee_id <= a.employee_id ) 计算公式 FROM dmhr.employee WHERE job_id =11;
求总和的百分比
使用分析函数 SUM() 和 OVER() 计算各部门工资合计及该合计工资占总工资的比例。用 group by 语句可以用到合计工资。
当 OVER() 后不加任何内容时,就是对所有的数据进行汇总。
1 2 3 4
SELECT job_id, 工资合计, SUM (工资合计) OVER () AS 总合计 FROM ( SELECT job_id, SUM (salary) 工资合计 FROM dmhr.employee GROUPBY job_id ) x;
位串数据类型 BIT
BIT 类型语法:BIT
BIT 类型用于存储整数数据 1、0 或 NULL,只有 0 才转换为假,其他非空、非 0 值都会自动转换为真,可以用来支持 ODBC 和 JDBC 的布尔数据类型。DM 的 BIT 类型与 SQL Server 2000 的 BIT 数据类型相似。
-- 根据某个员工的入职日计算其前五天、后五天、前五个月、后五个月、前五年及后5年的具体时间 SELECT hire_date AS 聘用日期, add_days (hire_date, -5) AS 减5天, add_days (hire_date, 5) AS 加5天, add_months (hire_date, -5) AS 减5月, add_months (hire_date, 5) AS 加5月, add_months (hire_date, -5*12) AS 减5年, add_months (hire_date, 5*12) AS 加5年 FROM dmhr.employee WHERE ROWNUM <=1;
WITH tmp AS ( SELECT 日期, TO_CHAR (日期, 'DY', 'NLS_DATE_LANGUAGE = American') AS dy FROM ( SELECT min_hd + (dmhr.t500.id -1) AS 日期 FROM ( SELECTMIN (hire_date) AS min_hd, MAX (hire_date) AS max_hd FROM dmhr.employee WHERE employee_name IN ('马学铭', '陈仙') ) x, dmhr.t500 WHERE dmhr.t500.id <= ( (max_hd - min_hd) +1) ) ) -- 过滤并汇总 SELECTCOUNT (*) FROM tmp WHERE dy NOTIN ('SAT', 'SUN');
求一年中周内各日期的天数
比如,计算一年内有多少天是星期一,多少天是星期二等,可以按照如下步骤实现:
取得大当前年度信息。
计算一年有多少天。
生成日期列表。
转换为对应的星期标识。
汇总统计。
1 2 3 4 5 6
WITH x0 AS (SELECT TO_DATE ('2020-01-01', 'yyyy-mm-dd') AS 年初 FROM DUAL), x1 AS (SELECT 年初, ADD_MONTHS (年初, 12) AS 下年初 FROM x0), x2 AS (SELECT 年初, 下年初, 下年初 - 年初 AS 天数 FROM x1), x3 AS (SELECT 年初 + (LEVEL -1) AS 日期 FROM x2 CONNECTBY LEVEL <= 天数), x4 AS (SELECT 日期, TO_CHAR (日期, 'DY') AS 星期 FROM x3) SELECT 星期, COUNT (*) AS 天数 FROM x4 GROUPBY 星期;
确定当前记录和下一条记录之间相差的天数
1 2 3 4 5 6 7
-- 使用 lead() over() 分析函数,将下一条记录的雇佣日期作为当前行 WITH tmp AS ( SELECT employee_id,employee_name,hire_date, LEAD (hire_date) OVER (ORDERBY hire_date) next_hd FROM dmhr.employee WHERE job_id =11 ) SELECT employee_name,hire_date,next_hd,next_hd - hire_date diff FROM tmp;
SELECTEXTRACT (YEARFROM SYSTIMESTAMP) AS "YEAR", EXTRACT (MONTHFROM SYSTIMESTAMP) AS "MONTH", EXTRACT (DAYFROM SYSTIMESTAMP) AS "DAY", EXTRACT (HOURFROM SYSTIMESTAMP) AS "HOUR", EXTRACT (MINUTEFROM SYSTIMESTAMP) AS "MINUTE", EXTRACT (SECONDFROM SYSTIMESTAMP) AS "SECOND" FROM DUAL;
to_char 函数可以用来取日期时间类型字段中的时、分、秒。
1 2 3
SELECT created, TO_CHAR (created, 'dd') AS d, TO_CHAR (created, 'hh24') AS h FROM dba_objects WHERE object_id =2;
EXTRACT 函数可以用来取 INTERVAL 中的信息,to_char 函数不支持。
1 2
SELECTEXTRACT (HOURFROM it) AS "hour" FROM (SELECTINTERVAL'2 12:30:59'DAYTOSECONDAS it FROM DUAL);
-- 计算年初 SELECT TRUNC (hire_date, 'y') 年初 FROM dmhr.employee WHERE ROWNUM <=1; -- 计算二月初 SELECT ADD_MONTHS (TRUNC (hire_date, 'y'), 1) 二月初 FROM dmhr.employee WHERE ROWNUM <=1; -- 计算二月底 SELECT LAST_DAY (ADD_MONTHS (TRUNC (hire_date, 'y'), 1)) AS 二月底 FROM dmhr.employee WHERE ROWNUM <=1; -- 计算二月底对应的日期 SELECT TO_CHAR (LAST_DAY (ADD_MONTHS (TRUNC (hire_date, 'y'), 1)), 'DD') AS 日 FROM dmhr.employee WHERE ROWNUM <=1;
周的计算
使用 TO_CHAR、 NEXT_DAY、TRUNC 函数共同实现周的计算。
1 2 3 4 5 6 7 8 9 10
WITH x AS ( SELECT TRUNC (SYSDATE, 'yy') + (LEVEL -1) AS 日期 FROM DUAL CONNECTBY LEVEL <=8 ) SELECT 日期, TO_CHAR (日期, 'd') AS d, TO_CHAR (日期, 'day') ASday, NEXT_DAY (日期, 1) AS 下个周日, TO_CHAR (日期, 'ww') AS ww, TO_CHAR (日期, 'iw') AS iw FROM x;
-- 返回指定年份内的所有周五对应的日期。 WITH x AS ( SELECT TRUNC (SYSDATE, 'yy') + (LEVEL -1) dy FROM DUAL CONNECTBY LEVEL <= ADD_MONTHS (TRUNC (SYSDATE, 'y'), 12) - TRUNC (SYSDATE, 'y') ) SELECT dy, TO_CHAR (dy, 'day') AS 周五 FROM x WHERE TO_CHAR (dy, 'd') =6;
SELECT sn AS 季度, (sn -1) *3+1AS 开始月份, ADD_MONTHS (TO_DATE (年, 'yyyy'), (sn -1) *3) AS 开始日期, ADD_MONTHS (TO_DATE (年, 'yyyy'), sn *3) -1AS 结束日期 FROM ( SELECT'2020'AS 年, LEVEL AS sn FROM DUAL CONNECTBY LEVEL <=4 );
按照给定的时间单位查找
使用 to_char 函数查询给定时间单位的时间。
1 2 3 4
-- 查询如入职日期在 1 月或者 12 月且非星期三的员工信息。 SELECT employee_name 姓名, hire_date 入职日期, TO_CHAR (hire_date, 'day') AS 星期 FROM dmhr.employee WHERE TO_CHAR (hire_date, 'mm') IN ('01', '12') AND TO_CHAR (hire_date, 'd') !='4';
使用日期的特殊部分比较记录
1 2 3 4 5 6 7
-- 使用 to_char 函数统计相同月份与周内日期入职的员工。 SELECT employee_name 姓名, hire_date 入职日期, TO_CHAR (hire_date, 'MON day') AS 月周 FROM ( SELECT employee_name, hire_date, COUNT (*) OVER (PARTITIONBY TO_CHAR (hire_date, 'MON day')) AS ct FROM dmhr.employee ) WHERE hire_date LIKE'2015%';
SELECT employee_name, employee_id, NVL (commission_pct, 0) AS commission_pct FROM dmhr.employee;
转换函数 (nvl),只能转换 null 值为同类型或者可以隐式转换成同类型的值。
函数对空值的处理方式各不一样,返回结果不同:
1 2 3 4
-- 返回空值 SELECT GREATEST(16,NULL) FROM dual; -- 返回预期的值 SELECT REPLACE('123456',3,NULL) FROM dual;
临时表
1 2 3 4 5
WITH x AS ( SELECT ROWNUM AS seq, a.* FROM ( SELECT 编号, 项目, 金额 FROM dmhr.detail ORDERBY 编号) a ) SELECT*FROM x;
列别名及部分列检索
列名通常是英文表示,所以可以给列取个别名,增强可读性。可使用 as 关键字或直接跟别名。示例语句如下所示:
1 2 3 4
-- 使用 as 关键字或直接跟别名 SELECT employee_id AS "员工编号", employee_name AS "员工姓名" FROM dmhr.employee; -- 检索部分列需要明确指定要查询的列,而不是用 * 号替代 SELECT employee_id, employee_name, salary FROM dmhr.employee WHERE salary >20000;
where 子句中引用别名列
where 条件引用别名一定要嵌套一层,因为别名是在 select 之后才有效。
1
SELECT*FROM (SELECT employee_id emid, email emna, salary sal FROM dmhr.employee) WHERE sal >10000;
列拼接
1 2 3 4 5 6 7
-- 使用 || 可以把字符串拼接起来 SELECT employee_name ||' salary is '|| salary AS col1 FROM dmhr.employee; -- 也可以将需要批量执行的 SQL 语句拼接起来 SELECT'TRUNCATE TABLE '|| schema_name ||'.'|| table_name FROM ALL_TABLES_DIS_INFO WHERE schema_name ='DMHR'; -- 字符串连接符 || 也可以改成 concat 函数 SELECT CONCAT ('ABC','BCD','DDD','BBB') AS "OUTPUT" FROM DUAL;
条件逻辑
1 2 3 4 5 6 7 8 9
SELECT employee_name, salary, CASE WHEN salary <=4000THEN'low' WHEN salary >=12000THEN'high' ELSE'ok' ENDAS salary_status FROM dmhr.employee;
限制返回的行数
1 2 3 4 5 6
-- 在查询的时候并不是每次都要求返回所有的行,比如抽查的时候只要求返回 10 行,可以使用伪列 `rownum` 来过滤 SELECT* FROM (SELECT ROWNUM AS rn, t.*FROM dmhr.employee t WHERE ROWNUM <=10) WHERE rn =2; -- 使用 LIMIT 子句返回两行 SELECT*FROM dmhr.employee LIMIT 2;
SELECT EMPLOYEE_NAME AS 姓名, SUBSTR (PHONE_NUM, -4) AS 尾号 FROM dmhr.employee WHERE ROWNUM <5 ORDERBY2;
TRANSLATE 应用
1 2 3 4 5 6 7 8
-- 实现字符替换 TRANSLATE(expr,from_string,to_string) -- from_string 与 to_string 以字符为单位,对应字符逐个替换 SELECTTRANSLATE ('ab你好bcadefg', 'abcdefg', '1234567') AS new_str FROM DUAL; -- 输出 12你好2314567 -- 如果 to_string 为空,则直接返回空值 SELECTTRANSLATE('ab你好bcadefg','abcdefg','') AS new_str FROM DUAL; -- 输出 NULL -- 如果 to_string 对应的位置没有字符,from_string 中列出的字符将会被删除 SELECTTRANSLATE('ab你好bcadefg','1abcdefg','1') AS new_str FROM DUAL; --输出 你好
按数字和字母混合字符串中的字母排序
1 2 3 4 5 6 7
-- 为了实现混合字符串排序,首先需创建视图 CREATEOR REPLACE VIEW v AS SELECT postal_code ||' '|| city_id AS data FROM dmhr.location; -- 查询视图信息 SELECT*FROM v; -- 使用 translate 替换功能,把数字和空格都替换为空,然后再进行排序 SELECT data, TRANSLATE (data, '- 0123456789', '-') AS oper_type FROM v WHERE ROWNUM <5ORDERBY2;
MERGEINTO dmhr.dup_emp USING dmhr.emp_salary ON (dmhr.dup_emp.employee_id = dmhr.emp_salary.employee_id) WHEN MATCHED THEN UPDATESET dmhr.dup_emp.salary = dmhr.emp_salary.new_salary WHENNOT MATCHED THEN INSERTVALUES (dmhr.emp_salary.employee_id,'dm2024',410107197103257999,dmhr.emp_salary.new_salary,102);
-- 通过 group by + having 子句分组查询的方式 SELECT employee_name, count(*) FROM dmhr.dup_emp GROUPBY employee_name HAVINGCOUNT(*) >1; -- 通过 group by + rowid 的方式 SELECT*FROM dmhr.dup_emp WHERE ROWID NOTIN ( SELECTMAX (ROWID) FROM dmhr.dup_emp GROUPBY employee_name ); -- 可在查找到重复记录后直接删除 DELETEFROM dmhr.dup_emp WHERE ROWID NOTIN ( SELECTMAX (ROWID) FROM dmhr.dup_emp GROUPBY employee_name ); DELETEFROM dmhr.dup_emp t WHERE ROWID <> ( SELECTMAX (ROWID) FROM dmhr.dup_emp WHERE employee_name = t.employee_name );