ORACLE 之 最简单的排序、计数、随机操作大集合

在ORACLE数据库世界里,我们常常会遇到各种各样的数据处理需求。今天,我要带大家走进ORACLE的“趣味操作区”,看看那些看似简单却充满智慧的操作到底是怎样的。

1. 按列排序: salaries in a nutshell
SELECT department_id, employee_id, salary,
RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS salary_rank
FROM employees;

这是一段经典的“按部门排名”语句。它不仅让我们看到每位员工的薪资,还顺便给了我们一个秩(rank)。想象一下,就像在公司内部发工资条时,领导用最简单的排序就能让每个人都知道自己的收入水平。

有趣的是,“RANK() OVER (PARTITION BY department_id ORDER BY salary DESC)”这部分,就像是给每个部门里的员工们排了一个小小的竞赛排行榜。最高薪的人得到1,其次是2,依此类推。简单到几乎让人怀疑人生。


2. 茶壶里显山露水:按列计数
SELECT glfducsite, glfduc001,
glfduc002, glfduc003, glfduc004,
1 / COUNT(*) OVER(PARTITION BY glfduc004) AS l_glfduc006
FROM glfduc_t
WHERE glfducent = 88
AND glfduc002 = 2023
AND glfduc003 = 10;

这段代码看起来像是在计算某个特定群体中的“稀有度”。比如,“glfduc004”可能是某种分类,而COUNT(*) OVER(PARTITION BY glfduc004)就是这个分类的总人数。最后的结果,就是每个人在这个分类中所占的比例。

想象一下,就像你在茶水间看到某个特别受欢迎的咖啡饮品,想知道它的“稀有度”,也就是有多少人和你一样喜欢它。这就是这段代码想要表达的意思!


3. 随机排序: shuffle the deck
SELECT *
FROM xmdc_t
WHERE xmdcdocno = 'HK2002-241100032'
ORDER BY dbms_random.value;

这是一段让人忍俊不禁的随机排序语句。dbms_random.value()生成的是一个介于0和1之间的随机数,而“ORDER BY”让结果按照这个随机数值来排列。

想象一下,在一条很长的查询结果中,每一行都像是被随机打乱过的牌。就像是在K歌网站上,歌曲推荐完全随机,完全没有规律可循——这就是随机排序的魅力!


4. 一行转多行: data explosion
SELECT pjba001,
substr(pjbaua002,
instr(pjbaua002, ',', 1, levels.lvl) + 1,
instr(pjbaua002, ',', 1, levels.lvl + 1) -
(instr(pjbaua002, ',', 1, levels.lvl) + 1)) as pjbaua002
FROM (SELECT pjba001,
',' || pjbaua002 || ',' AS pjbaua002,
length(pjbaua002) - nvl(length(REPLACE(pjbaua002, ',')), 0) + 1 AS cnt
FROM pjba_t
WHERE pjba001 = 'P000001273'
AND pjbaent = 88
AND pjbastus = 'Y') a,
(SELECT rownum AS lvl
FROM (SELECT MAX(length(pjbaua002 || ',') -
nvl(length(REPLACE(pjbaua002, ',')), 0)) max_len
FROM pjba_t)
CONNECT BY LEVEL <= max_len) levels
WHERE levels.lvl <= a.cnt
order by pjba001;

这段代码看起来像是一种“一行转多行”的技术。通过substr()instr()函数,它将一个字段拆分成多个子字段,并按照特定的规则排列。

想象一下,在Excel里,当你把一个很长的地址拆分成行的时候,每一行都像是被独立处理过的——这就是这段代码想要实现的效果!


5. 茶壶里显山露水:随机数生成器
SELECT 1 / COUNT(*) OVER (PARTITION BY glfduc004) AS l_glfduc006
FROM glfduc_t
WHERE glfducent = 88
AND glfduc002 = 2023
AND glfduc003 = 10;

这看起来像是在计算某个群体中的“稀有度”。COUNT(*) OVER (PARTITION BY glfduc004)是这个群体的总人数,而最后的结果就是每个人在这个群体中所占的比例。

想象一下,在一个班级里,每个学生的成绩排名会被计算出来——这就是这段代码想要表达的意思!


6. 九九八十一变:随机日期时间
SELECT to_date((to_char(sysdate, 'yyyyMMdd') ||
lpad(floor(dbms_random.value(18, 24)), 2, '0') ||
lpad(floor(dbms_random.value(0, 59)), 2, '0') ||
lpad(floor(dbms_random.value(0, 59)), 2, '0')),
'yyyyMMddhh24miss')
FROM dual;

sysdate作为基准日期。
使用dbms_random.value()分别生成小时、分钟和秒部分,确保格式正确。
padright函数用于填充不足的零。

SELECT SYSDATE + (1 / 24 / floor(dbms_random.value(10, 60))) FROM dual;

dbms_random()返回一个介于0到1之间的随机数。
乘以60得到随机的分钟数(0到59分钟)。
使用interval类型将结果转换为时间间隔。

SELECT TO_CHAR(TO_DATE('2024/01/01', 'yyyy/mm/dd') + ROWNUM - 1,
'yyyy/mm/dd') DAY_DATE
FROM DUAL
CONNECT BY ROWNUM <= TO_DATE('2024/12/31', 'yyyy-mm-dd') -
TO_DATE('2024/01/01', 'yyyy-mm-dd') + 1;

随机产生某个区间内的日期时间范围。

WITH employee_data AS (
    SELECT department_id,
           rownum AS employee_id,
           RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS salary_rank,
           dbms_random.value() * 1000000 + current_date - INTERVAL '1 year' AS birthday
    FROM employees
),
department_stats AS (
    SELECT department_id,
           COUNT(*) AS total_employees,
           MIN(birthday) AS min_birthday,
           MAX(birthday) AS max_birthday,
           AVERAGE(birthday) AS avg_birthday,
           STDDEV(birthday) AS std_birthday
    FROM employee_data
    GROUP BY department_id
)
SELECT d.department_id, 
       d.total_employees, 
       d.min_birthday, 
       d.max_birthday, 
       ROUND(d.avg_birthday, 4) AS avg_birthday,
       ROUND(d.std_birthday, 4) AS std_birthday
FROM department_stats d
ORDER BY d.department_id;

代码解析
WITH employee_data AS (…)
这一部分生成了一个临时标量子查询 employee_data,为每个员工(employee_id)生成一个随机的出生日期 (birthday)。
使用 dbms_random.value() * 1000000 + current_date - INTERVAL ‘1 year’ AS birthday 来生成虚拟的出生日期。
dbms_random.value() 生成的是一个介于 0 和 1 之间的随机小数,乘以 1,000,000 转换为毫秒单位。
加上 current_date 并减去一年的区间,确保所有生成的日期都落在合理的范围内(例如,假设当前日期是 2023 年 7 月,那么这些日期会从 1970 年到 2023 年)。

WITH department_stats AS (…)
这一部分生成了一个临时查询 department_stats,按部门统计:
total_employees:每个部门的员工总数。
min_birthday、max_birthday:每个部门员工的出生日期范围。
avg_birthday:每个部门员工的平均出生日期(四舍五入到小数点后四位)。
std_birthday:每个部门员工出生日期的标准差(四舍五入到小数点后四位)。

SELECT d.department_id, … FROM department_stats d ORDER BY d.department_id;
最终的查询按部门排序,输出每个部门的基本统计信息:
department_id:部门编号。
total_employees:员工总数。
min_birthday、max_birthday:出生日期范围。
avg_birthday 和 std_birthday:平均值和标准差(单位为 UNIX 时间戳,需在实际应用中进行格式化输出)。

运行结果分析
每个部门的员工总数 total_employees:直接统计人数。
min_birthday 和 max_birthday:展示了部门内员工出生日期的范围。
avg_birthday:反映了部门员工的“平均年龄”(以 UNIX 时间戳表示)。
std_birthday:衡量了部门员工出生日期分布的离散程度。

代码优化建议
如果需要更精确的时间格式,可以对 birthday 进行格式化输出:

SELECT employee_id, 
       department_id,
       dbms_random.value() * 1000000 + current_date - INTERVAL '1 year' AS birthday
FROM employees;

将上述代码中的 dbms_random.value() * 1000000 + current_date 替换为:

to_char(dbms_random.value(), 'YYYY-MM-DD') AS birthday

这样可以输出更直观的日期格式(例如:‘1985-04-23’)。


7. 真正的大神: salary ranking
SELECT department_id, employee_id, salary,
RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS salary_rank
FROM employees;

这是一段经典的“按部门排名”语句。它不仅让我们看到每位员工的薪资,还顺便给了我们一个秩(rank)。想象一下,就像在公司内部发工资条时,领导用最简单的排序就能让每个人都知道自己的收入水平。

有趣的是,“RANK() OVER (PARTITION BY department_id ORDER BY salary DESC)”这部分,就像是在每个部门里进行了一次小型的竞赛。最高薪的人得到1,其次是2,依此类推。简单到几乎让人怀疑人生。


ORACLE的世界真是妙趣横生!从简单的排序、计数,到复杂的随机操作,这些小技巧不仅能帮助我们高效地处理数据,还能让我们在编程中体验到无与伦比的乐趣。下次当你面对一个数据库问题时,不妨试试这些“趣味操作”。说不定下一个大神就是你!

你可能感兴趣的:(数据库,ORACLE,oracle,数据库)