系列文章目录
本文对Oracle 常用的语法进行汇总
文章目录
- 系列文章目录
- 一、Oracle 表&表字段操作:
- 1.1 DDL语句(数据定义语言)Create、Alter、Drop、Truncate:
- 1.1.1 建表:
- 建表:
- 注释COMMENT :
- 表中字段的约束:
- 表中字段的默认值:
- 1.1.2 删除表:
- 1.1.3 清空表数据
- 1.1.4 表中字段处理:
- 1.2 DML语句(数据操作语言)Insert、Update、Delete、Merge:
- 1.2.1 INSERT 语句
- 1.2.2 UPDATE 语句:
- 1.2.3 DELETE 语句:
- 1.2.4 MERGE 语句(合并操作):
- 1.2.5 ROW_NUMBER() OVER(窗口函数):
- 1.3 DCL语句(数据控制语言)Grant、Revoke:
- 1.3.1 Grant 授权语句:
- 1.3.2 Revoke 撤销授权:
- 1.4 事务控制语句Commit、Rollback、Savepoint:
- 1.4.1 提交事务Commit:
- 1.4.2 回滚事务Rollback:
- 1.4.3 Savepoint:
- 二、Oracle 常用的数据类型:
- 2.1 字符数据类型
- 2.2 数字数据类型
- 2.3 日期和时间数据类型
- 2.4 二进制数据类型
- 三、Oracle 常用函数:
- 3.1 字符串处理:
- 3.2 数字处理:
- 3.3 日期处理:
- 3.4 数据转换:
- 3.5 case 和decade:
- 3.6 数据聚合:
- 3.7 数据筛选:
- 3.8 表连接:
- 3.9 集合操作:
- 3.10 单行子查询,多行子查询:
- 3.11 EXISTS 和IN
- 3.12 表信息查询
- 四、sql 语句的执行顺序
- 4.1 select 语句的执行顺序:
- 4.2 插入语句(INSERT)执行顺序:
- 4.3 更新语句(UPDATE)执行顺序
- 4.4 删除语句(DELETE)执行顺序:
- 五、存储过程:
- 5.1 goto 语句:
- 5.2 循环遍历语句:
- 5.3 SIGN(COUNT(1)) 表达式:
- 总结
一、Oracle 表&表字段操作:
1.1 DDL语句(数据定义语言)Create、Alter、Drop、Truncate:
1.1.1 建表:
建表:
CREATE TABLE 表名(
)
COMMENT ON COLUMN 注释;
-- 创建一个学生表
CREATE TABLE STUDENTS (
STUDENT_ID NUMBER(10) PRIMARY KEY, -- 学生ID,主键
FIRST_NAME VARCHAR2(50) NOT NULL, -- 学生名字,不允许为空
LAST_NAME VARCHAR2(50) NOT NULL, -- 学生姓氏,不允许为空
EMAIL VARCHAR2(100) UNIQUE, -- 学生电子邮箱,唯一
AGE NUMBER(3) CHECK (AGE >= 0 AND AGE <= 100), -- 学生年龄,检查范围在0到100之间
ENROLLMENT_DATE DATE, -- 学生入学日期
COMMENTS CLOB -- 学生备注信息
)
COMMENT ON COLUMN STUDENTS.STUDENT_ID IS '唯一标识学生的ID';
COMMENT ON COLUMN STUDENTS.FIRST_NAME IS '学生的名字';
COMMENT ON COLUMN STUDENTS.LAST_NAME IS '学生的姓氏';
COMMENT ON COLUMN STUDENTS.EMAIL IS '学生的电子邮箱地址,必须唯一';
COMMENT ON COLUMN STUDENTS.AGE IS '学生的年龄,必须在0到100岁之间';
COMMENT ON COLUMN STUDENTS.ENROLLMENT_DATE IS '学生的入学日期';
COMMENT ON COLUMN STUDENTS.COMMENTS IS '学生的备注信息';
注释COMMENT :
在Oracle中,COMMENT关键字不能在建表的时候直接放入来添加字段注释。Oracle的建表语句(CREATE TABLE)主要用于定义表的结构,包括字段名、数据类型、约束等,但不直接支持在创建表的同时为字段添加注释。
字段注释的添加通常是在表创建之后,通过单独的COMMENT ON COLUMN语句来实现的。这个语句允许你为已经存在的表的字段添加或修改注释,以提高数据库的可读性和可维护性。
表中字段的约束:
主键约束(PRIMARY KEY)
功能:主键用于唯一标识表中的每一行记录,一个表只能有一个主键。它可以是一个列或者多个列的组合,主键列的值不能为空(NOT NULL)且不能重复。
设置方法:
建表时设置:
例如,创建一个名为employees的表,其中employee_id为主键。
CREATE TABLE employees (
employee_id NUMBER(5) PRIMARY KEY,
employee_name VARCHAR2(100),
department_id NUMBER(3)
);
建表后添加:
如果表已经创建,要为employee_id添加主键约束,可以使用ALTER TABLE语句。
ALTER TABLE employees
ADD CONSTRAINT pk_employee_id PRIMARY KEY (employee_id);
唯一约束(UNIQUE)
功能:确保表中的一列或多列组合的值在整个表中是唯一的,但与主键不同的是,唯一约束列可以为空(NULL)。不过,只能有一个NULL值,因为多个NULL在唯一约束的定义下是无法区分的。
设置方法:
建表时设置:
假设在employees表中,employee_email列需要设置为唯一。
CREATE TABLE employees (
employee_id NUMBER(5),
employee_name VARCHAR2(100),
department_id NUMBER(3),
employee_email VARCHAR2(100) UNIQUE
);
建表后添加:
若表已存在,要为employee_email添加唯一约束,可以这样操作。
ALTER TABLE employees
ADD CONSTRAINT uk_employee_email UNIQUE (employee_email);
非空约束(NOT NULL)
功能:强制列不接受空值(NULL),用于确保表中的某些列必须有值。
设置方法:
建表时设置:
在employees表中,要求employee_name列不能为空。
CREATE TABLE employees (
employee_id NUMBER(5),
employee_name VARCHAR2(100) NOT NULL,
department_id NUMBER(3)
);
建表后添加:
对于已创建的表,如果要为department_id列添加非空约束。
ALTER TABLE employees
MODIFY department_id NOT NULL;
外键约束(FOREIGN KEY)
功能:用于建立两个表之间的关联关系,确保一个表(子表)中的列值是另一个表(父表)中主键或唯一键列值的引用,维护数据的完整性和一致性。
设置方法:
建表时设置:
假设有departments表(父表)和employees表(子表),employees表中的department_id列引用departments表中的department_id列(主键)作为外键。
CREATE TABLE departments (
department_id NUMBER(3) PRIMARY KEY,
department_name VARCHAR2(100)
);
CREATE TABLE employees (
employee_id NUMBER(5),
employee_name VARCHAR2(100),
department_id NUMBER(3),
FOREIGN KEY (department_id) REFERENCES departments(department_id)
);
建表后添加:
若表已存在,为employees表的department_id列添加外键约束,关联departments表的department_id列。
ALTER TABLE employees
ADD CONSTRAINT fk_department_id FOREIGN KEY (department_id) REFERENCES departments(department_id);
检查约束(CHECK)
功能:用于限制列中的值必须满足一个特定的条件。这个条件可以是一个简单的表达式,比如列值大于某个数、列值在某个范围内等。
设置方法:
建表时设置:
在employees表中,假设要限制employee_age列的值必须大于等于 18。
CREATE TABLE employees (
employee_id NUMBER(5),
employee_name VARCHAR2(100),
department_id NUMBER(3),
employee_age NUMBER(3) CHECK (employee_age >= 18)
);
建表后添加:
对于已有的表,要为employee_salary列添加检查约束,要求工资大于 0。
ALTER TABLE employees
ADD CONSTRAINT ck_employee_salary CHECK (employee_salary > 0);
表中字段的默认值:
DEFAULT
进行默认值的设置;
SYSDATE是 Oracle 中的一个函数,用于获取系统当前日期。当插入新记录时,如果没有为hire_date列提供值,该列将自动填充为系统当前日期;对于department_id列,如果没有提供值,将默认插入 10。
CREATE TABLE employees (
employee_id NUMBER(5),
employee_name VARCHAR2(100),
hire_date DATE DEFAULT SYSDATE,
department_id NUMBER(3) DEFAULT 10
);
修改已有表列的默认值:
需要注意的是,这种修改默认值的操作不会影响已经存在于表中的记录,只会对之后插入的新记录生效。如果要更新现有记录的值,可以使用UPDATE语句单独进行操作。
ALTER TABLE employees
MODIFY department_id NUMBER(3) DEFAULT 20;
使用序列作为默认值(用于数字类型列)创建序列:
首先创建一个序列,例如创建一个名为employee_id_seq的序列,用于为employee_id列提供默认值。
这个序列从 1 开始,每次递增 1,没有最大值限制,并且不会循环。
CREATE SEQUENCE employee_id_seq
START WITH 1
INCREMENT BY 1
NOMAXVALUE
NOCYCLE;
设置默认值为序列值:
在创建表或者修改表时,可以将列的默认值设置为序列的下一个值。例如,在创建employees表时,设置employee_id列的默认值为序列employee_id_seq的下一个值。
这样,每次插入新记录时,如果没有为employee_id列提供值,该列将自动填充为序列employee_id_seq的下一个值。
CREATE TABLE employees (
employee_id NUMBER(5) DEFAULT employee_id_seq.NEXTVAL,
employee_name VARCHAR2(100),
hire_date DATE DEFAULT SYSDATE,
department_id NUMBER(3)
);
1.1.2 删除表:
drop table 表名
1.1.3 清空表数据
TRUNCATE TABLE 表名;
DELETE FROM 表名 WHERE 条件
1.1.4 表中字段处理:
添加字段、列
-- alter table 表名 add 字段名 数据类型
alter table CBS_SCAM_FC_ITEM_CONTAINER add REF_TYPE VARCHAR2(30);
添加注释
comment on column CBS_SCAM_FC_ITEM_CONTAINER.REF_TYPE is 'REF_TYPE';
修改字段的数据类型
-- alter table 表名 modify 字段名 新的数据类型
alter table CBS_SCAM_FC_ITEM_PRODUCT MODIFY GROSS_PROFIT_MARGIN NUMBER(20,4);
alter table CBS_SCAM_FC_ITEM_PRODUCT MODIFY CARGO_COUNT NUMBER(20,4);
修改字段的名字
-- alter table 表名 rename column 旧名 to 新名
ALTER TABLE table_name RENAME COLUMN old_column_name TO new_column_name;
删除字段
-- alter table 表名 drop column 要删除的字段名
ALTER TABLE table_name DROP COLUMN column_name;
重命名表名
-- alter table 表名 rename to 新表名
1.2 DML语句(数据操作语言)Insert、Update、Delete、Merge:
1.2.1 INSERT 语句
用于向表中插入新的数据行
:
- 插入单条记录(指定列):
INSERT INTO table_name (column1, column2, column3,...) VALUES (value1, value2, value3,...);
INSERT INTO employees (employee_name, department_id) VALUES ('John Doe', 10);
- 插入单条记录(所有列):
INSERT INTO table_name VALUES (value1, value2, value3,...);
INSERT INTO employees VALUES (employee_id_seq.NEXTVAL, 'John Doe', SYSDATE, 10);
- 插入多条记录(指定列):
INSERT INTO table_name (column1, column2, column3,...) SELECT value1, value2, value3,... FROM dual UNION ALL SELECT value4, value5, value6,... FROM dual;
INSERT INTO employees (employee_name, department_id) SELECT 'Alice', 20 FROM dual UNION ALL SELECT 'Bob', 30 FROM dual;
1.2.2 UPDATE 语句:
用于修改表中已存在的数据行的内容。
UPDATE table_name SET column1 = value1, column2 = value2,... WHERE condition;
UPDATE employees SET salary = salary * 1.1 WHERE department_id = 10;
1.2.3 DELETE 语句:
用于从表中删除数据行
:
DELETE FROM table_name WHERE condition;
DELETE FROM employees WHERE department_id IS NULL;
1.2.4 MERGE 语句(合并操作):
可以根据源表中的数据对目标表进行插入(INSERT)、更新(UPDATE)或删除(DELETE)操作,用于将数据从一个数据源合并到另一个数据源;
MERGE INTO target_table
USING source_table ON (join_condition)
WHEN MATCHED THEN
UPDATE SET target_table.column1 = source_table.column1, target_table.column2 = source_table.column2,...
WHEN NOT MATCHED THEN
INSERT (column1, column2,...) VALUES (source_table.column1, source_table.column2,...);
```sql
MERGE INTO employees e
USING (SELECT * FROM new_employees) ne
ON (e.employee_id = ne.employee_id)
WHEN MATCHED THEN
UPDATE SET e.name = ne.name,
e.department = ne.department,
e.salary = ne.salary
WHEN NOT MATCHED THEN
INSERT (e.employee_id, e.name, e.department, e.salary)
VALUES (ne.employee_id, ne.name, ne.department, ne.salary);
1.2.5 ROW_NUMBER() OVER(窗口函数):
ROW_NUMBER() OVER 是 SQL 中的一个窗口函数(Window Function),用于为查询结果的每一行分配一个唯一的连续整数。这个函数非常有用,特别是在需要对查询结果进行分组排名或排序时。OVER 子句定义了窗口函数的执行范围,即数据行集(也称作窗口)上,该函数将在这个范围内执行。
- PARTITION BY 是可选的,用于将结果集分成多个分区,每个分区内的行将独立地计算 ROW_NUMBER()。如果不指定 PARTITION BY,则整个结果集被视为一个单一的分区。
- ORDER BY 是必需的,用于指定在每个分区内行的排序方式,这决定了 ROW_NUMBER() 的分配顺序
ROW_NUMBER() OVER (
PARTITION BY column1, column2, ...
ORDER BY columnA, columnB, ... )
伪代码参考:
MERGE INTO tableA xx
USING (
WITH xxxa AS
(SELECT xxx from xxx)
,sql_1 as (
SELECT A.* FROM (
SELECT column1, column2, column3, column4
row_number() over(PARTITION BY xxx.co1, xxx.co2 ORDER BY xxx.co1 desc,xxx.co2 DESC) rn
FROM tableB b INNER JOIN Tablec c
on b.col1 = c.col2
) A WHERE A.RN = 1
)
SELECT
col1,col2,,,,,
FROM table1 LEFT JOIN sql_1
ON col1 = col2) temp1
ON (xx.col1 = temp1.col2 )
WHEN MATCHED THEN
UPDATE
SET xx.col1 = temp1.col2
WHERE xx.col2 <> 'system';
1.3 DCL语句(数据控制语言)Grant、Revoke:
1.3.1 Grant 授权语句:
GRANT语句用于向用户或角色授予各种数据库对象(如表、视图、存储过程等)的权限。这些权限包括对对象的查询(SELECT)、插入(INSERT)、更新(UPDATE)、删除(DELETE)操作权限,以及执行存储过程(EXECUTE)的权限等。通过GRANT语句,可以精细地控制不同用户对数据库资源的访问级别。
授予对象权限:
基本语法为GRANT privilege [, privilege,…] ON object TO user [, user,…] [WITH GRANT OPTION];
其中,privilege是要授予的权限,如SELECT、INSERT、UPDATE、DELETE、EXECUTE等;object是数据库对象,如table_name(表名)、view_name(视图名)、procedure_name(存储过程名)等;user是被授予权限的用户或角色;WITH GRANT OPTION是一个可选子句,若包含此子句,则被授权的用户可以将该权限再授予其他用户。
-- 例如,授予用户user1对employees表的查询和插入权限:
GRANT SELECT, INSERT ON employees TO user1;
-- 若要授予用户user2对存储过程get_employee_info的执行权限,并允许user2将此权限授予他人,可以这样写:
GRANT EXECUTE ON get_employee_info TO user2 WITH GRANT OPTION;
-- 授予用户user3创建表和创建视图的系统权限:
GRANT CREATE TABLE, CREATE VIEW TO user3;
1.3.2 Revoke 撤销授权:
REVOKE语句与GRANT语句相反,用于撤销用户或角色已经被授予的权限。通过撤销权限,可以确保数据库的安全性和数据的完整性,防止用户对数据库对象进行未经授权的操作。
基本语法为REVOKE privilege [, privilege,…] ON object FROM user [, user,…];
注意:如果之前授予用户user2对存储过程get_employee_info的执行权限且带有WITH GRANT OPTION,撤销user2的权限同时,也会自动撤销user2授予其他用户的相同权限:
-- 撤销用户user1对employees表的插入权限:
REVOKE INSERT ON employees FROM user1;
REVOKE EXECUTE ON get_employee_info FROM user2;
-- 撤销用户user3的创建视图的系统权限:
REVOKE CREATE VIEW FROM user3;
- 权限继承和级联撤销:
当撤销带有WITH GRANT OPTION授予的权限时,会级联撤销相关用户的权限。另外,在一些复杂的权限结构中,要注意用户可能通过角色继承权限,撤销角色权限可能会影响多个用户。 - 对象所有者和权限管理:
数据库对象的所有者默认拥有所有权限,并且可以授予和撤销其他用户对该对象的权限。在进行权限管理时,要明确对象所有者的角色和责任,确保权限分配符合安全策略和业务需求。 - 权限验证和审计:
为了确保权限的正确使用,数据库管理员可以设置权限验证和审计机制,监控用户对数据库对象的操作,及时发现和处理权限滥用的情况。
1.4 事务控制语句Commit、Rollback、Savepoint:
在 Oracle 数据库中,事务是一组逻辑上相关的操作单元,这些操作要么全部成功执行(提交),要么全部不执行(回滚)。事务用于确保数据库的一致性和完整性,特别是在涉及多个数据库操作(如多个INSERT、UPDATE或DELETE操作)的情况下。
1.4.1 提交事务Commit:
COMMIT语句用于提交事务。当执行COMMIT操作时,事务中对数据库所做的所有更改(如插入、更新、删除记录等)将被永久保存到数据库中。这些更改对其他用户和后续的数据库操作将可见。
在一个银行转账的场景中,从账户 A 转账一定金额到账户 B,这涉及两个更新操作(减少账户 A 的余额和增加账户 B 的余额),可以将这两个操作放在一个事务中,最后提交事务。
UPDATE accounts SET balance = balance - 100 WHERE account_id = 'A';
UPDATE accounts SET balance = balance + 100 WHERE account_id = 'B';
COMMIT;
COMMIT语句确保了两个UPDATE操作对数据库的修改被持久化。如果在COMMIT之前出现任何错误(如数据库故障、违反约束条件等),整个事务可以通过ROLLBACK(后面会介绍)进行回滚,恢复到事务开始之前的状态。
1.4.2 回滚事务Rollback:
ROLLBACK语句用于回滚事务。当执行ROLLBACK操作时,事务中对数据库所做的所有更改将被撤销,数据库将恢复到事务开始之前的状态。这在事务执行过程中出现错误或者需要取消事务的情况下非常有用。
由于第二个UPDATE操作出现错误,ROLLBACK语句将撤销第一个UPDATE操作对数据库所做的修改,数据库中员工的工资数据将恢复到事务开始之前的状态。
BEGIN
UPDATE employees SET salary = salary * 1.1 WHERE department_id = 10;
-- 假设下面这行代码出现错误,例如违反了某个约束条件
UPDATE employees SET invalid_column = 'invalid_value' WHERE department_id = 10;
ROLLBACK;
END;
1.4.3 Savepoint:
SAVEPOINT语句用于在事务中设置保存点。保存点是事务中的一个标记点,它允许在事务执行过程中部分回滚到该标记点的状态。这在一个复杂的事务中,当只有部分操作出现问题时,可以只回滚到指定的保存点,而不是回滚整个事务。
一个事务包含多个更新操作,其中部分操作可能需要单独控制回滚。
BEGIN
UPDATE products SET price = price * 1.05 WHERE category_id = 1;
SAVEPOINT sp1;
UPDATE products SET quantity = quantity - 10 WHERE category_id = 1;
-- 假设发现第二个更新操作有问题,想要回滚到保存点sp1
ROLLBACK TO SAVEPOINT sp1;
-- 可以继续执行其他操作或者提交事务
COMMIT;
END;
在这个例子中,首先更新了产品价格并设置了保存点sp1,然后更新产品数量。当发现产品数量更新操作有问题时,通过ROLLBACK TO SAVEPOINT sp1回滚到保存点sp1的状态,撤销了产品数量更新操作,然后可以继续进行其他操作或者提交事务,此时只有产品价格的更新操作会被提交。
二、Oracle 常用的数据类型:
2.1 字符数据类型
CHAR
:
- 用于存储固定长度的字符串。
- 最大长度可达2000字节。
- 如果存储的字符串长度小于定义的长度,Oracle会自动用空格填充至指定长度。
- 常用于存储长度固定的数据,如性别、省份简称等。
VARCHAR2
:
- 用于存储可变长度的字符串。
- 最大长度可达4000字节(在某些数据库版本中可能有所不同,但通常是4000字节)。
- 与CHAR不同,VARCHAR2不会自动用空格填充至指定长度,只存储实际的数据长度。
- 是最常用的字符数据类型之一,特别适用于存储长度不固定的文本数据。
NCHAR 和 NVARCHAR2
:
- 这两个数据类型分别用于存储固定长度和可变长度的Unicode字符集数据。
- 最大长度分别为2000字节和4000字节(但长度根据Unicode字符集的实际编码可能会有所不同)。
- 用于支持多语言环境中的字符数据存储。
CLOB(Character Large Object)
:
- 用于存储大量文本数据,最大长度可达4GB。
- 适用于存储书籍、文章等长文本内容。
NCLOB
:
- 类似于CLOB,但用于存储Unicode字符集的大量文本数据。
- 最大长度同样可达4GB。
2.2 数字数据类型
NUMBER
:
- 用于存储整型或浮点型数值。
- 可以指定精度(总位数)和小数位数。
- 例如,NUMBER(5,2)表示总共5位数字,其中2位是小数。
- 范围从10的-130次方到10的126次方(精度和范围可能因Oracle版本和配置而异)。
INTEGER、FLOAT、REAL
:
- 这些数据类型也是用于存储数值的,但它们在精度和存储方式上有所不同。
- INTEGER用于存储整数。
- FLOAT和REAL用于存储浮点数,但REAL在Oracle中通常作为NUMBER的一种同义词使用,并不提供额外的精度或范围优势。
2.3 日期和时间数据类型
DATE
:
- 用于存储日期和时间信息,包括年、月、日、时、分、秒。
- 内部以7个字节存储,但用户看到的通常是格式化的日期和时间字符串。
TIMESTAMP
:
- 提供比DATE更高的日期和时间精度,包括纳秒级的时间信息。
2.4 二进制数据类型
RAW 和 LONG RAW
:
- RAW用于存储固定长度的二进制数据,最大长度2000字节。
- LONG RAW用于存储可变长度的二进制数据,最大长度可达2GB。
- 但请注意,LONG RAW是一个较老的数据类型,在现代Oracle数据库应用中可能较少使用,取而代之的是BLOB数据类型。
BLOB(Binary Large Object)
:
- 用于存储大量二进制数据,如图片、视频、音频文件等。
- 最大长度可达4GB。
`BFILE:
- 用于将二进制文件存储在数据库外部的文件系统中,但在数据库中保存一个指向这些文件的指针。
- 最大长度同样可达4GB,但实际大小受限于外部文件系统的限制。
除了上述类型外,Oracle还提供了其他数据类型,如ROWID(用于存储表中行的物理地址)、UROWID(用于存储表示任何类型列地址的二进制数据)等。这些数据类型在特定场景下非常有用,但通常不是日常应用开发中的主流数据类型。
请注意,Oracle数据库的版本和配置可能会影响数据类型的具体实现和限制。因此,在设计和实现数据库时,建议查阅最新的Oracle官方文档以获取准确的信息。
三、Oracle 常用函数:
3.1 字符串处理:
-- 转大写
select upper('name') from dual;
-- 转小写
select lower('Name') from dual;
-- 首字母大写
select INITCAP('name') from dual;
-- 字符串拼接
select concat('a','b') from dual;
select 'a'||'b'||'c' from dual;
-- 字符串截取,下标从1 开始 ,截取几位
select substr('abcde',1,2) from dual;
-- 字符串长度
select LENGTH('123') from dual;
-- 返回子串在字符串中第一次出现的位置,下标从1 开始
select instr('123',3) from dual ;
-- 字符串替换,source 原有的字符串,查找的字符串,替换为的字符串
select REPLACE('hello world', 'world', '123') from dual;
-- 字符串左侧填充使用'123',填充完后总共10 位
select LPAD('abc', 10, '123') from dual;
-- 字符串右侧填充使用'123',填充完后总共10 位
select RPAD('abc', 10, '123') from dual;
-- 去掉字符串前后空格
select trim(' 12 ') from dual;
3.2 数字处理:
-- 四舍五入 小数保留多少位
-- 小数保留2位
select ROUND(123.2312, 2) from dual;
-- 小数保留3位
select ROUND(123.2367, 2) from dual;
-- 直接截取
-- 小数保留2位
select trunc(123.2312, 2) from dual;
-- 小数保留3位
select trunc(123.2367, 3) from dual;
-- 绝对值
select abs(-123.12) from dual;
-- 返回余数
select mod(5.23,2) from dual;
3.3 日期处理:
-- 当前系统时间
select SYSDATE from dual;
-- 月份加 2个月
select ADD_MONTHS(SYSDATE, 2) from dual;
-- date1 和date2 的月份差 结果为负数,表示 date1 比 date2 早几个月
select MONTHS_BETWEEN(SYSDATE, ADD_MONTHS(SYSDATE, 2)) from dual;
-- 日期的最后一天
select LAST_DAY(sysdate) from dual;
select FIX_DAYS
-- 给定日期之后的下一个星期几
-- 'SUNDAY':星期日,'MONDAY':星期一,'TUESDAY':星期二,'WEDNESDAY':星期三,'THURSDAY':星期四,'FRIDAY':星期五,'SATURDAY':星期六
select NEXT_DAY(sysdate, 'FRIDAY') from dual;
-- 默认将日期截断到当天的午夜 00:00:00如: 2024-12-02 00:00:00
select TRUNC(SYSDATE) from dual;
-- 日期截断到年 当前年的第一天 2024-01-01 00:00:00
select TRUNC(SYSDATE,'yyyy') from dual;
-- 日期截断到月 2024-12-01 00:00:00
select TRUNC(SYSDATE,'MM') from dual;
-- 日期截断到天 2024-12-02 00:00:00
select TRUNC(SYSDATE,'dd') from dual;
-- 日期截断到小时 2024-12-02 17:00:00
select TRUNC(SYSDATE,'HH') from dual;
-- 日期截断到分钟 2024-12-02 17:15:00
select TRUNC(SYSDATE,'MI') from dual;
3.4 数据转换:
-- 转换为字符串
-- 日期转换为字符串 2024-12
select TO_CHAR(SYSDATE,'yyyy-MM') from dual;
-- 日期转换为字符串 2024-12-02 17:17:00
SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') FROM DUAL
-- 数字转换为字符串 123.12
select TO_CHAR(123.12) from dual;
-- 周中的第几天 2
select TO_CHAR(SYSDATE,'D') from dual;
-- 字符串转换为日期
SELECT TO_DATE('2024-12-02 08:17:00','YYYY-MM-DD HH12:MI:SS') FROM DUAL;
SELECT TO_DATE((SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') FROM DUAL),'YYYY-MM-DD HH24:MI:SS') FROM DUAL;
-- 字符串转换为数字
select TO_NUMBER('123.23') from dual;
-- 按照给定的格式进行转换 9 代表数字为 几个9 代表最多几位
select to_number('222','9999') from dual;
-- 金额转换
select to_number('$22.21','L99.99') from dual;
3.5 case 和decade:
select
case substr(to_char(TRUNC(SYSDATE,'MM') ,'yyyy-MM-dd'),6,2)
WHEN '12' THEN
'12月份'
WHEN '11' THEN
'11月份'
ELSE
'其它月份'
End as month
from dual;
select
decode ( substr(to_char(TRUNC(SYSDATE,'MM') ,'yyyy-MM-dd'),6,2) ,
'12','12月份',
'11' ,'11月份',
'其它月份')
month
from dual;
3.6 数据聚合:
-- 取表中某一列的最大值 max ,min ,avg ,sum 会忽略null
select max('ABC') from dual;
select max(100) from dual;
-- 取表中某一列的最大值
select min('DEV') from dual;
select min(100) from dual;
-- 对表中某一列求和
select sum(100) from dual;
-- 表中某一列求平均
select avg(12) from dual;
-- 表中的行数,计算表中的总行数,包括包含NULL值的行
select count(*) from dual;
-- COUNT(column_name):计算指定列column_name中非空值的数量
-- group by 进行条件分组,having 对分组后的组内数据进行过滤,select 对分组的后的数据进行获取
select coulmna,coulmnb,sum(coulmnc),min(coulmnd),max(coulmne) from dual
group by coulmna,coulmnb having count(*)/sum(coulmn)/mincoulmn()/max(coulmn)/avg(coulmn) > 2;
3.7 数据筛选:
-- 算术运算符
select (2+3) as addNumber from dual;
select (2-3) as subNumber from dual;
select (2*3) as multiplyNumber from dual;
select (2/3) as divideNumber from dual;
select trunc(2/3,2) from dual;
select trunc(2/3,0) from dual;
select round(2/3,2) from dual;
-- 比较
select * from tablea where coulmna > coulmnb ;
select * from tablea where coulmna >= coulmnb ;
select * from tablea where coulmna = coulmnb ;
select * from tablea where coulmna < coulmnb ;
select * from tablea where coulmna <= coulmnb ;
select * from tablea where coulmna != coulmnb ;
select * from tablea where coulmna <> coulmnb ;
-- and 并且查询
select * from tablea where coulmna between 100 and 200 and cloumb = 'xxx';
-- between and 范围查询
select * from tablea where coulmna between 100 and 200;
-- null
select * from tablea where coulmn is null;
select * from tablea where coulmn is not null;
select * from tablea where coulmn = '';
-- in not in
select * from tablea where coulmna in ('a','b');
select * from tablea where coulmna not in ('a','b')
-- AND (列1, 列2, 列3) in (select 列1, 列2, 列3 from table)多列关联筛选的方式
-- AND 是用于在查询条件中添加多个条件的逻辑运算符,表示 “并且” 的关系
-- (列1, 列2, 列3)表示要进行比较的一组列 in (select 列1, 列2, 列3 from table)是一个子查询部分。
-- 它从指定的table表中查询出列1、列2和列3这三列的数据,形成一个结果集。外部查询中的(列1, 列2, 列3)
-- 整体会与这个子查询结果集中的每一行(每一组列1、列2、列3的值)进行比较
select * from tablea where coulmna in ('a','b') AND (列1, 列2, 列3) in (select 列1, 列2, 列3 from table);
-- like not like % 一个或多个字符 _ 代表一个字符
select * from tablea where coulmna like '%a%';
select * from tablea where coulmna like 'a%';
select * from tablea where coulmna like '%a';
-- ESCAPE关键字主要用于LIKE操作符的模式匹配中。当我们需要在模式字符串中搜索包含通配符(%代表任意字符序列
-- ,_代表单个任意字符)本身的内容时,就需要使用ESCAPE来指定一个转义字符,这样可以避免通配符被当作模式匹配的通配符来使用
-- 查找名字中包含%字符的产品
SELECT * FROM products WHERE product_name LIKE '%\%%' ESCAPE '\';
-- distinct 去重 coulmna,coulmnb,coulmnc 组合去重
select distinct coulmna,coulmnb,coulmnc from tablea ;
-- 排序
select * from tablea
order by tablea,tableb
-- OFFSET 10 ROWS表示跳过前 10 行记录 ,FETCH NEXT 10 ROWS ONLY表示只获取接下来的 10 行记录
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;
3.8 表连接:
CREATE TABLE TEST_LGX_STUDENTS (
student_id NUMBER(8) PRIMARY KEY, -- 学生编号,使用NUMBER类型,假设最多8位数字,设置为主键用于唯一标识学生
student_name VARCHAR2(50) NOT NULL, -- 学生姓名,VARCHAR2类型,最大长度50字符,不能为空
gender VARCHAR2(10) CHECK (gender IN ('Male', 'Female', 'Other')), -- 性别,限定取值范围为Male、Female、Other
birth_date DATE, -- 出生年月日
major VARCHAR2(100), -- 所学专业
class_id NUMBER(5), -- 所在班级编号,与班级表关联,这里先定义为NUMBER类型,最多5位数字
enrollment_date DATE, -- 入学日期
contact_phone VARCHAR2(20), -- 联系电话
email VARCHAR2(100) -- 电子邮箱
);
CREATE TABLE test_lgx_courses (
course_id NUMBER(5) PRIMARY KEY, -- 课程编号,使用NUMBER类型并设置为主键,假设编号最多5位数字
course_name VARCHAR2(100) NOT NULL, -- 课程名称,使用VARCHAR2类型,最大长度100字符,且不能为空
teacher_id NUMBER(5), -- 授课教师编号,与教师表关联,这里先定义为NUMBER类型,最多5位数字
credit_hours NUMBER(2,1) DEFAULT 3.0, -- 学分,使用NUMBER类型,总长度2位,其中小数位1位,默认值为3.0学分
start_date DATE, -- 课程开始日期
end_date DATE, -- 课程结束日期
department_id NUMBER(3) -- 所属系部编号,使用NUMBER类型,最多3位数字
);
INSERT INTO "TEST_LGX_STUDENTS" ("STUDENT_ID", "FIRST_NAME", "LAST_NAME", "EMAIL", "AGE", "ENROLLMENT_DATE", "COMMENTS") VALUES ('1', 'li', '三', NULL, NULL, NULL, NULL);
INSERT INTO "TEST_LGX_STUDENTS" ("STUDENT_ID", "FIRST_NAME", "LAST_NAME", "EMAIL", "AGE", "ENROLLMENT_DATE", "COMMENTS") VALUES ('2', 'wang', '五', NULL, NULL, NULL, NULL);
INSERT INTO "CBS_MAINT"."TEST_LGX_COURSES" ("COURSE_ID", "COURSE_NAME", "TEACHER_ID", "CREDIT_HOURS", "START_DATE", "END_DATE", "DEPARTMENT_ID") VALUES ('1', '数学', '1', '2', NULL, NULL, NULL);
INSERT INTO "CBS_MAINT"."TEST_LGX_COURSES" ("COURSE_ID", "COURSE_NAME", "TEACHER_ID", "CREDIT_HOURS", "START_DATE", "END_DATE", "DEPARTMENT_ID") VALUES ('2', '语文', '2', '3', NULL, NULL, NULL);
INSERT INTO "CBS_MAINT"."TEST_LGX_COURSES" ("COURSE_ID", "COURSE_NAME", "TEACHER_ID", "CREDIT_HOURS", "START_DATE", "END_DATE", "DEPARTMENT_ID") VALUES ('3', '英语', '3', '4', NULL, NULL, NULL);
-- 笛卡尔集 2*3
select * from TEST_LGX_STUDENTS a,test_lgx_courses b ;
-- 等值连接 等值匹配
select * from TEST_LGX_STUDENTS a,test_lgx_courses b where a.STUDENT_ID = b.teacher_id;
-- 内连接
select * from TEST_LGX_STUDENTS a inner join test_lgx_courses b
on a.STUDENT_ID = b.teacher_id;
-- 外连接
-- 左外连接
select * from TEST_LGX_STUDENTS a left join test_lgx_courses b
on a.STUDENT_ID = b.teacher_id;
-- 右外连接
select * from TEST_LGX_STUDENTS a right join test_lgx_courses b
on a.STUDENT_ID = b.teacher_id;
3.9 集合操作:
-- UNION 操作符 用于合并两个或多个查询结果集,并去除重复的行
-- 因为UNION会去除重复行,所以数据库需要对结果进行额外的处理来比较和去除重复记录,这可能会对性能产生一定影响。
-- 要求每个查询结果集中的列数、列的数据类型和顺序都必须相同
SELECT employee_id, employee_name, department_id FROM employees_branch1
UNION
SELECT employee_id, employee_name, department_id FROM employees_branch2;
-- UNION ALL,UNION ALL不会去除重复的行,它会将所有的行合并在一起
SELECT employee_id, employee_name, department_id FROM employees_branch1
UNION ALL
SELECT employee_id, employee_name, department_id FROM employees_branch2;
-- INTERSECT返回两个或多个查询结果集的交集,即只返回同时存在于所有参与操作的结果集中的行
-- 同样要求参与操作的查询结果集的列数、列的数据类型和顺序相同。
-- 在性能方面,数据库需要比较每个结果集中的行来确定交集,对于大型数据集,这可能会比较耗时
SELECT employee_id, employee_name, department_id FROM employees_branch1
INTERSECT
SELECT employee_id, employee_name, department_id FROM employees_branch2;
-- MINUS 返回在第一个查询结果集中存在,但在第二个(以及后续,如果有多个)查询结果集中不存在的行。这类似于数学中的集合的差集运算
--对结果集的列数、列的数据类型和顺序的要求与前面的操作符相同。
-- 结果的顺序取决于第一个查询结果集的顺序,因为它是基于第一个结果集来筛选行的
SELECT employee_id, employee_name, department_id FROM employees_branch1
MINUS
SELECT employee_id, employee_name, department_id FROM employees_branch2;
3.10 单行子查询,多行子查询:
单行子查询
:
-- 单行子查询是指子查询(嵌套在主查询中的查询)只返回一行结果。它通常用于在主查询中进行条件筛选、计算等操作,
-- 并且子查询返回的值可以与主查询中的列进行比较。
-- 子查询(SELECT AVG(salary) FROM employees)只返回一个值,即所有员工的平均工资。主查询则筛选出工资高于这个平均值的员工姓名
SELECT employee_name
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
-- 单行子查询必须返回恰好一行数据。如果子查询返回多行或者没有返回行,根据具体的比较操作符,可能会导致错误。
-- 例如,在使用=、>、<等比较操作符时,如果子查询返回多行,Oracle 会报错,提示 “单行子查询返回多行”
多行子查询
:
-- 多行子查询是指子查询返回多行结果。这种子查询通常用于与主查询中的多个值进行比较,以筛选出符合条件的行。
-- 多行子查询会使用一些特殊的操作符来与主查询进行关联,如IN、ANY、ALL等。
-- 子查询(SELECT department_id FROM departments WHERE department_id IN (10, 20, 30))返回了多行部门编号。
-- 主查询使用IN操作符来筛选出部门编号在这些返回值中的员工姓名
SELECT employee_name
FROM employees
WHERE department_id IN (SELECT department_id FROM departments WHERE department_id IN (10, 20, 30));
-- IN 操作符:用于判断主查询中的列值是否在子查询返回的结果集中。如果在,则条件为真,对应的行被选中。例如,WHERE column_name IN (subquery)。
-- ANY 操作符:与比较操作符(如>、<、>=、<=)一起使用。只要主查询中的列值与子查询返回的任何一个值满足比较条件,该行就被选中。例如,WHERE column_name > ANY (subquery)表示只要列值大于子查询返回的任何一个值,条件就成立。
-- ALL 操作符:同样与比较操作符一起使用。主查询中的列值必须与子查询返回的所有值都满足比较条件,该行才被选中。例如,WHERE column_name > ALL (subquery)表示列值必须大于子查询返回的所有值,条件才成立
3.11 EXISTS 和IN
-- 在 Oracle 中,EXISTS是一个用于检查子查询是否返回任何行的条件关键字。
-- 它主要用于关联子查询,返回一个布尔值(TRUE或FALSE),如果子查询返回至少一行数据,则条件为TRUE;如果子查询没有返回任何行,则条件为FALSE
-- 外部查询从table1中选择列,并且只有当内部子查询(从table2中查询,关联条件是table1.column = table2.column)返回至少一行数据时,
-- 外部查询的行才会被选中。
SELECT column1, column2,... FROM table1 WHERE EXISTS (SELECT * FROM table2 WHERE table1.column = table2.column);
- 与IN关键字的比较:
IN关键字用于检查一个值是否在一个子查询返回的结果集中。例如,WHERE column_value IN (SELECT column_value FROM…)。而EXISTS是检查子查询是否返回任何行,更侧重于行的存在性。在性能方面,对于某些情况,EXISTS可能比IN更高效,特别是当子查询的关联条件比较复杂或者涉及到大型数据集时。 - 与JOIN操作的比较:
JOIN操作主要用于将多个表中的列组合在一起,返回一个合并后的结果集。例如,SELECT * FROM table1 JOIN table2 ON table1.column = table2.column。EXISTS则更侧重于条件筛选,它不会返回子查询中的列,而是根据子查询是否返回行来决定外部查询的行是否被选中。在一些场景下,使用EXISTS可以更简洁地表达查询意图,特别是当只关心外部查询表中的行是否满足某个关联条件,而不需要返回关联表中的详细信息时。
3.12 表信息查询
-- 查询用户下的所有表
select table_name from all_tables where owner = 'CBS_MAINT';
-- 表中所有列
select * from all_tab_columns where table_name = 'TEST_LGX_STUDENTS';
-- 表中所有约束
select * from all_constraints where table_name = 'TEST_LGX_STUDENTS';
四、sql 语句的执行顺序
4.1 select 语句的执行顺序:
- FROM 子句:
这是查询执行的第一步。数据库首先会确定要从哪些表(或视图、子查询等数据源)中获取数据。例如,在查询SELECT * FROM employees JOIN departments ON employees.department_id = departments.department_id;中,数据库会先定位employees表和departments表。 - WHERE 子句:
在确定了数据源后,会根据WHERE子句中的条件对行进行筛选。只有满足条件的行才会被保留用于后续的操作。例如,WHERE salary > 5000会过滤掉工资小于或等于 5000 的员工记录。 - GROUP BY 子句:
如果查询中包含GROUP BY子句,在经过WHERE筛选后的行将按照GROUP BY指定的列进行分组。例如,GROUP BY department_id会将员工记录按照部门编号进行分组,以便后续进行聚合操作。 - HAVING 子句:
对于分组后的结果,HAVING子句会进行进一步的筛选。它类似于WHERE子句,但作用于分组后的组。例如,HAVING COUNT(*) > 10会筛选出包含 10 个以上员工的部门组。 - SELECT 子句:
在经过前面的步骤后,SELECT子句会确定要返回的列或表达式。这可以包括原始列、通过函数计算得到的列(如AVG(salary)计算平均工资)或者常量。例如,SELECT department_id, AVG(salary)会返回部门编号和该部门的平均工资。 - ORDER BY 子句:
最后,ORDER BY子句会对结果集进行排序。可以按照升序(ASC,默认)或降序(DESC)排列。例如,ORDER BY department_id DESC会按照部门编号的降序对结果进行排序。
4.2 插入语句(INSERT)执行顺序:
-
确定插入目标表:
首先,数据库会确定要将数据插入到哪个表中。这是通过INSERT INTO后面的表名来指定的。例如,INSERT INTO employees表明要将数据插入到employees表。 -
检查约束和默认值(建表时定义):
在插入每一行数据时,数据库会检查表的列定义中的约束条件(如非空约束、唯一约束、检查约束等)。对于没有提供值的列,如果有默认值定义,会自动填充默认值。例如,如果employees表中的hire_date列有默认值为系统日期(DATE DEFAULT SYSDATE),在插入时没有提供hire_date的值,数据库会自动填充当前日期。 -
插入数据行:
根据VALUES子句(插入指定值)或SELECT子句(插入查询结果)中的内容,将数据插入到目标表中。例如,INSERT INTO employees (employee_name, department_id) VALUES (‘John Doe’, 10);会将包含员工姓名为John Doe,部门编号为 10 的数据行插入到employees表中。
4.3 更新语句(UPDATE)执行顺序
- 确定更新目标表:
首先明确要更新的表,通过UPDATE后面的表名来指定。例如,UPDATE employees表示要对employees表进行更新。 - WHERE 子句筛选(可选):
如果有WHERE子句,会根据其中的条件筛选出要更新的行。只有满足条件的行才会被更新。例如,WHERE department_id = 10会筛选出部门编号为 10 的员工记录进行更新。 - 更新列值:
根据SET子句中的内容,对筛选出的行的列值进行更新。例如,SET salary = salary * 1.1会将筛选出的员工的工资提高 10%。
4.4 删除语句(DELETE)执行顺序:
- 确定删除目标表:
通过DELETE FROM后面的表名确定要从哪个表中删除数据。例如,DELETE FROM employees表示要从employees表中删除数据。 - WHERE 子句筛选(可选):
如果存在WHERE子句,会根据其中的条件筛选出要删除的行。只有满足条件的行才会被删除。例如,WHERE salary < 3000会删除工资低于 3000 的员工记录。
五、存储过程:
5.1 goto 语句:
- 注释: goto 在 Oracle 存储过程中,GOTO语句用于无条件地跳转到存储过程中的另一个标签位置。
CREATE OR REPLACE PROCEDURE my_proc AS
v_count NUMBER := 0;
BEGIN
-- 一些逻辑判断
IF v_count = 0 THEN
DBMS_OUTPUT.PUT_LINE('Count is zero');
GOTO END_OF_PRC;
END IF;
-- 这里的代码在v_count为0时不会执行
-- 其他可能的逻辑操作
<<END_OF_PRC>>
-- 存储过程结束前的清理操作或者其他通用操作可以放在这里
DBMS_OUTPUT.PUT_LINE('Procedure is about to end');
END my_proc;
5.2 循环遍历语句:
FOR loop_variable IN (query_expression) LOOP
-- 在这里编写要循环执行的逻辑代码
END LOOP;
-
loop_variable(在你给出的示例中就是COUR_C)是一个隐式声明的记录类型变量,它的结构会根据(query_expression)返回的结果集的列定义自动匹配。这个变量在循环内部可以用来访问结果集中每一行对应的各个列的值。
-
(query_expression)通常是一个SELECT语句,用于查询返回需要遍历的数据集合,不过也可以是其他能返回一个集合的表达式,比如基于SYS_REFCURSOR等游标类型的返回结果等。
假设存在一个名为students的表,有student_id(学生编号)、student_name(学生姓名)等列,以下代码使用FOR…IN…LOOP来遍历查询出的所有学生记录并输出相关信息:
BEGIN
FOR COUR_C IN (SELECT student_id, student_name FROM students) LOOP
DBMS_OUTPUT.PUT_LINE('学生编号: ' || COUR_C.student_id || ', 学生姓名: ' || COUR_C.student_name);
END LOOP;
END;
循环变量COUR_C会依次获取SELECT语句查询出来的每一行数据,在循环内部,通过COUR_C.student_id和COUR_C.student_name可以访问到当前行对应的学生编号和姓名这两个字段的值,并将其输出显示(这里使用DBMS_OUTPUT包来输出,实际应用中可以根据需求进行相应的处理,比如插入到其他表等操作)。
特点和注意事项:
- 自动打开和关闭游标:当使用FOR…IN这种形式去遍历查询结果时,Oracle 会自动帮我们打开游标、获取每一行数据并在循环结束后自动关闭游标,开发者不需要显式地去进行游标相关的打开、关闭等操作,简化了代码编写并且避免了因忘记关闭游标而导致的资源问题。
-
变量作用范围:循环变量(如COUR_C)的作用范围仅限定在循环内部,在循环外部是无法访问该变量的。
- 查询语句限制:放在FOR循环中的查询语句需要符合 Oracle 的语法规则,如果查询语句有语法错误或者执行时出现数据库层面的异常(比如权限不足无法访问某些表等),那么整个循环所在的代码块(比如存储过程、匿名块等)都会报错,执行失败。
5.3 SIGN(COUNT(1)) 表达式:
COUNT(1):这是一个聚合函数用法,在这里它会统计符合后续 WHERE 子句限定条件的记录数量。具体而言,不管表中的每行实际数据是什么,只要满足条件,就会计数。例如,若有 5 条记录的 REF_TYPE 值为 ‘AR_DND’,那 COUNT(1) 的结果就是 5。使用 COUNT(1) 与使用 COUNT(*) 在功能上基本等价,都是用于统计行数。
SIGN() 函数:它的作用是根据传入的参数确定返回值的符号。规则如下:
若传入参数大于 0,SIGN() 函数返回 1。
若传入参数等于 0,SIGN() 函数返回 0。
若传入参数小于 0(不过在此处由于 COUNT(1) 统计的是记录数量,通常不会出现小于 0 的情况),SIGN() 函数返回 -1。所以在这个场景下,SIGN(COUNT(1)) 的结果要么是 0(对应没有满足条件的记录时,COUNT(1) 为 0),要么是 1(存在满足条件的记录时,COUNT(1) 大于 0)。
BEGIN
SELECT SIGN(COUNT(1))
INTO V_CHK_FLAG
FROM DUAL
WHERE EXISTS (SELECT 1
FROM CBS_B_TMP_VAT_RATE_INPUT T
WHERE T.REF_TYPE = 'AR_DND' );
EXCEPTION
WHEN OTHERS THEN
-- 在这里可以记录错误日志,或者进行一些合适的错误提示操作
DBMS_OUTPUT.PUT_LINE('出现错误: ' || SQLERRM);
END;
总结
本文从实践出发对oracle 的基础语法进行整理。