PLSQL:一种编程语言,针对的对象是数据库中的数据(表,表里的数据等等)
--在sql中能写的东西,在plsql也都可以写
--PL/SQL对大小写不敏感。(现在学习时候得标准:所有的字母都大写)
--语法结构:
DECLARE
--声明/定义的地方(变量、常量、游标) (非必须)
BEGIN
--写逻辑/写操作的地方 (必须)
EXCEPTION
--处理异常的地方(非必须)
END;
--需要注意的是:
1、单词不要写错了
2、如果要使用变量,那么一定要提前声明
3、每写完一句,最后都要带分号表示结束(包括声明的时候)
4、最后的 END 后面的分号不要忘
5、如果不需要声明,那么 DECLARE 可以不写
--比如:写第一个程序:打印HELLO WORLD
/*DECLARE
*/
BEGIN
DBMS_OUTPUT.PUT_LINE('HELLO WORLD');
END;
---变量
1、使用变量之前,必须先声明
--声明变量的语法结构:
DECLARE
--声明的地方
变量名 数据类型 [:= 初始值];
BEGIN
--写逻辑的地方
END;
--开发规范:变量名以 V_ 开头
--需要注意的是:
1、声明变量的时候一定要带数据类型(NUMBER,VARCHAR2,DATE)
2、根据数据类型的不同,需要带长度
数字型:NUMBER : 可以带长度,可以不带 --- NUMBER / NUMBER(X[,Y])
字符型:VARCHAR2:必须带长度 --- VARCHAR2(X)
日期型:DATE: 不带长度 --- DATE
3、每声明一个变量,后面都需要带分号表示结束
4、变量的初始值可以在声明这个变量的时候给到它,也可以在写逻辑的时候再给
5、变量名取有意义的。不要用中文,标点符号,纯数字等作为变量名
6、变量的长度是贯穿整个代码块的(从 DECLARE 到 END;),
在这个代码块中,给到这个变量的值的长度不要超过定义时的长度
7、符号都是英文的
--使用变量的方式来打印HELLO WORLD
DECLARE
V_HI VARCHAR2(15) := 'HELLO WORLD!';
BEGIN
DBMS_OUTPUT.PUT_LINE(V_HI);
END;
--用字符串连接符来连接赋值 打印HELLO WORLD
DECLARE
V_HI VARCHAR2(15) := 'HELLO';
BEGIN
DBMS_OUTPUT.PUT_LINE(V_HI);
V_HI := V_HI || ' WORLD!'; --一个空格也占一个字符长度
DBMS_OUTPUT.PUT_LINE(V_HI);
END;
--小练习一把:声明变量,并在逻辑体里面(BEGIN到END的中间这一块)给它赋值,打印 hello world
DECLARE
V_X VARCHAR2(15);
BEGIN
V_X := 'HELLO WORLD!!';
DBMS_OUTPUT.PUT_LINE(V_X);
END;
--分别打印三种类型的变量的值
DECLARE
V_NUMBER NUMBER := 100;
V_NUMBER2 NUMBER(2) := 99;
V_CHAR VARCHAR2(10) := 'QWERQWER';
V_DATE DATE := TO_DATE('20200921','YYYYMMDD');
BEGIN
DBMS_OUTPUT.PUT(V_NUMBER);
DBMS_OUTPUT.NEW_LINE();
DBMS_OUTPUT.PUT(V_NUMBER2);
DBMS_OUTPUT.NEW_LINE();
DBMS_OUTPUT.NEW_LINE();
DBMS_OUTPUT.PUT(V_CHAR);
DBMS_OUTPUT.NEW_LINE();
DBMS_OUTPUT.NEW_LINE();
DBMS_OUTPUT.PUT_LINE(V_DATE);
DBMS_OUTPUT.PUT_LINE(TO_CHAR(V_DATE,'YYYY-MM-DD'));
END;
----几种打印相关的关键字
DBMS_OUTPUT.PUT(XXX) : 不换行打印(意思就是:打印xxx后不换行)
DBMS_OUTPUT.NEW_LINE(): 换行
DBMS_OUTPUT.PUT_LINE(XXX):换行打印(意思就是:打印xxx后自动换到下一行)
--需要注意:
DBMS_OUTPUT.PUT(XXX) :在不换行打印这一句后面,需要要有一个带有换行功能的操作。否则DBMS_OUTPUT.PUT(XXX)不会打印内容
---打印日期型的值,一般是将他转换成字符型来进行打印,否则它会自动转换成字符
---所以一般打印日期的时候,用TO_CHAR(D,'YYYY-MM-DD')
---打印的操作里面,可以写一些简单的逻辑(转换,一些简单的算术运算等)
--小练习一把
--1、分别打印三种类型的变量值
---2、将两个日期型的值相减后赋给第三个变量,打印该变量的值
DECLARE
V_DATE1 DATE := SYSDATE;
V_DATE2 DATE := TO_DATE('20200920','YYYYMMDD');
V_X NUMBER(3);
V_Y NUMBER(3,2);
V_Z NUMBER(7,6);
BEGIN
V_X := V_DATE1 - V_DATE2;
DBMS_OUTPUT.PUT_LINE(V_X);
V_Y := V_DATE1 - V_DATE2;
DBMS_OUTPUT.PUT_LINE(V_Y);
V_Z := V_DATE1 - V_DATE2;
DBMS_OUTPUT.PUT_LINE(V_Z);
END;
--定义的变量的类型是数字型的时候需要注意:
当变量的类型是数字型,并且是NUMBER(X,Y)的格式的时候,那么
1、带有长度的时候,如果该变量被赋值,而且该值是一个小数的话,那么该值会根据小数位的个数(Y)来进行四舍五入
2、但是该变量的整数位的长度(X-Y)还是得大于等于该值的长度
-----声明变量,接收用户的输入:&
--语法结构:
DECLARE
变量名 数据类型 [:= &提示信息];
BEGIN
--逻辑体
END;
--需要注意的是:
1、“ := &提示信息 ” 也可以在写逻辑体的时候再写
2、如果同时有多个变量接收用户的输入,那么&后面的 提示信息 要不一样
3、& 跟 它后面的提示信息中间不要有空格
4、用户输入的内容的数据类型 跟 变量的数据类型 需要保持一致,长度也不能超过变量定时的长度
5、输入的内容的数据类型不同时,输入格式(方法)不一样
数字型:用户直接输入一串数字(整数,小数,负数,正数)
字符型:用户输入的时候,需要用单引号引起来(怎么做到不需要用单引号?:将 &提示信息 用单引号引起来)
日期型:用户人数的时候,需要用TO_DATE()将内容转化成日期(怎么做到不需要用to_date()?:将 &提示信息 用to_date()括起来)
6、虽然第5个注意点的中针对输入有解决办法,但是一般都不用这种办法,而是在输入的时候输入对应的内容
--比如:输入一个字符串(hello oracle),并打印该字符串
DECLARE
V_HE VARCHAR2(15) := &请输入一串字符;
BEGIN
DBMS_OUTPUT.PUT_LINE(V_HE);
END;
/*小练习一把:输入一串字符,打印该字符的值,
输入两个数字x,y,打印X的Y次幂的结果和Y的X次幂的结果,
输入一个日期,打印该日期加上3个月后的日期*/
DECLARE
V_CHAR VARCHAR2(15) := &输入一串字符;
X NUMBER := &输入一个数字X;
Y NUMBER := &输入一个数字Y;
V_DATE DATE := &输入一个日期;
Z NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE(V_CHAR);
DBMS_OUTPUT.NEW_LINE();
Z := X ** Y;
DBMS_OUTPUT.PUT('----------' || TO_CHAR(Z,'fm0.0') || '----------'); ---可以将运算后的值赋给一个变量,然后打印该变量(更推荐这种)
--如果是一个小于1的小数,会省略掉小数点前面的0,(节省了存储空间。注意:它不会影响到值,只是显示的问题)
--如果需要加上0,那么用to_char(x,'fm0.0')
DBMS_OUTPUT.PUT(Y ** X); ---也可以在打印的时候做一些简单的运算
DBMS_OUTPUT.NEW_LINE();
DBMS_OUTPUT.NEW_LINE();
DBMS_OUTPUT.PUT_LINE(TO_CHAR(ADD_MONTHS(V_DATE, 3),'YYYY-MM-DD'));
END;
---------------变量的使用
涉及到几个点:
1、隐式游标:
SELECT ... INTO ....
2、另外2种定义变量的方法(针对于变量的数据类型)
--第一种定义变量方法:借助表里的某个列的类型
DECLARE
变量名 表名.列名%TYPE [:= 初始值];
BEGIN
--逻辑体
--隐式游标的语法结构
SELECT 要查询的信息
INTO 变量1,变量2,......
FROM 表
[WHERE 过滤条件
GROUP BY 分组字段
HAVING 分组后的过滤条件
ORDER BY 排序字段 ASC/DESC];
END;
--需要注意的是:
1、这种定义变量的数据类型的方式跟上面那种自定义数据类型的方式互不冲突,可以混用
2、它就是借助于某张表中的某个列的数据类型以及长度
3、借助的表和列是实际存在的表和该表中的实际存在的列(不是通过查询新生成的列),而不是通过查询得到的临时表
4、隐式游标:SELECT ... INTO ...
1)INTO 后面的变量的 个数和顺序 需要跟select后面查询的内容保持一致
2)类型,长度不要弄错
3)用隐式游标时,查询的结果必须是一行(不能是多行或者0行(没有结果))
--比如:打印emp表中员工编号为7788的员工的工号,姓名,入职日期,工资,部门编号
--小技巧:
1、需要打印多少个值,就先定义多少个变量来分别接收不同的值
DECLARE
V_EMPNO EMP.EMPNO%TYPE;
V_ENAME EMP.ENAME%TYPE;
V_HIREDATE EMP.HIREDATE%TYPE;
V_SAL EMP.SAL%TYPE;
V_DEPTNO NUMBER(2);
BEGIN
SELECT E.EMPNO, E.ENAME, E.HIREDATE, E.SAL, E.DEPTNO
INTO V_EMPNO, V_ENAME, V_HIREDATE, V_SAL, V_DEPTNO
FROM EMP E
WHERE E.EMPNO = 7788;
DBMS_OUTPUT.PUT_LINE('工号:' || V_EMPNO || ' 姓名:' || V_ENAME || ' 入职日期:' ||
TO_CHAR(V_HIREDATE, 'YYYY-MM-DD') || ' 工资:' ||
V_SAL || ' 部门编号:' || V_DEPTNO);
END;
--小练习一把:
--1、查询emp表中的部门10的平均工资(保留两位小数),最高工资,人数,并打印出来
DECLARE
V_AVGSAL EMP.SAL%TYPE;
V_MAXSAL EMP.SAL%TYPE; --可以同时借用多次
V_CT NUMBER;
BEGIN
SELECT ROUND(AVG(E.SAL), 2), MAX(E.SAL), COUNT(*)
INTO V_AVGSAL, V_MAXSAL, V_CT
FROM EMP E
WHERE DEPTNO = 10;
DBMS_OUTPUT.PUT_LINE('平均工资:' || V_AVGSAL || ' 最高工资:' || V_MAXSAL ||
' 人数:' || V_CT);
END;
--2、打印emp表中部门20最早入职的员工的工号,姓名,入职日期,部门编号
--第一步:写sql
SELECT E.EMPNO,E.ENAME,E.HIREDATE
FROM EMP E
WHERE E.HIREDATE = (
SELECT MIN(E.HIREDATE)
FROM EMP E
WHERE DEPTNO = 20)
AND E.DEPTNO = 20;
--第二步:套壳子
DECLARE
V_EMPNO EMP.EMPNO%TYPE;
V_ENAME EMP.ENAME%TYPE;
V_HIREDATE EMP.HIREDATE%TYPE;
BEGIN
SELECT E.EMPNO, E.ENAME, E.HIREDATE
INTO V_EMPNO, V_ENAME, V_HIREDATE
FROM EMP E
WHERE E.HIREDATE = (SELECT MIN(E.HIREDATE) FROM EMP E WHERE DEPTNO = 20)
AND E.DEPTNO = 20;
DBMS_OUTPUT.PUT_LINE('工号:' || V_EMPNO || ' 姓名:' || V_ENAME || ' 入职日期:' ||
TO_CHAR(V_HIREDATE, 'YYYY-MM-DD') || ' 部门编号:' || 20);
END;
--3、接收用户的输入的部门编号,查询该部门中工资排名第一的员工的工号,姓名,工资,工资排名,部门编号
SELECT F.EMPNO,F.ENAME,F.SAL,F.RN
FROM (
SELECT E.*,
ROW_NUMBER()OVER(ORDER BY E.SAL DESC) RN
FROM EMP E
WHERE DEPTNO = 20) F
WHERE F.RN = 1;
--套壳子
DECLARE
V_DEPTNO NUMBER(2) := &输入一个部门编号;
V_EMPNO EMP.EMPNO%TYPE;
V_ENAME EMP.ENAME%TYPE;
V_SAL EMP.SAL%TYPE;
V_RN NUMBER(2);
BEGIN
SELECT F.EMPNO, F.ENAME, F.SAL, F.RN
INTO V_EMPNO, V_ENAME, V_SAL, V_RN
FROM (SELECT E.*, ROW_NUMBER() OVER(ORDER BY E.SAL DESC) RN
FROM EMP E
WHERE DEPTNO = V_DEPTNO) F
WHERE F.RN = 1;
DBMS_OUTPUT.PUT_LINE('工号:' || V_EMPNO || ' 姓名:' || V_ENAME || ' 工资:' ||
V_SAL || ' 部门编号:' || V_DEPTNO);
END;
--第二种方式:借助于表(比较特别)
--语法结构:
DECLARE
变量名 表名%ROWTYPE [:= 初始值];
BEGIN
--逻辑体
END;
--特别的地方:
1、这种方式定义的变量,拥有借助的表里面的所有的列的类型以及这个表中的列名
--比如:打印emp表中员工编号为7788的员工的工号,姓名,入职日期,工资,部门编号
DECLARE
V_EMP EMP%ROWTYPE;
BEGIN
SELECT E.EMPNO, E.ENAME, E.HIREDATE, E.SAL, E.DEPTNO
INTO V_EMP.EMPNO, V_EMP.ENAME, V_EMP.HIREDATE, V_EMP.SAL, V_EMP.DEPTNO
FROM EMP E
WHERE E.EMPNO = 7788;
DBMS_OUTPUT.PUT_LINE('工号:' || V_EMP.EMPNO || ' 姓名:' || V_EMP.ENAME ||
' 入职日期:' || TO_CHAR(V_EMP.HIREDATE, 'YYYY-MM-DD') ||
' 工资:' || V_EMP.SAL || ' 部门编号:' || V_EMP.DEPTNO);
END;
--小练习一把:用%rowtype的方式打印部门30的平均工资,最高工资,最低工资,人数
DECLARE
X EMP%ROWTYPE;
Y EMP%ROWTYPE; ---同一张表可以借用多次,只不过变量名要不同
Z DEPT%ROWTYPE; ---同时也可以借用其他的表
BEGIN
SELECT AVG(SAL), MAX(SAL), MIN(SAL), COUNT(*)
INTO X.SAL, X.COMM, Y.SAL, Z.DEPTNO
FROM EMP E
WHERE DEPTNO = 30;
DBMS_OUTPUT.PUT_LINE('平均工资:' || X.SAL || ' 最高工资:' || X.COMM || ' 最低工资:' ||
Y.SAL || ' 人数:' || Z.DEPTNO);
END;
--------常量:在声明时赋予初值,并且在运行时不允许重新赋值
--语法结构:
DECLARE
常量名 CONSTANT 数据类型 := 初始值;
BEGIN
--逻辑体
END;
--比如:查询半径为10的圆的面积
DECLARE
PI CONSTANT NUMBER(3, 2) := 3.14;
R CONSTANT NUMBER(2) := 10;
AREA NUMBER;
BEGIN
AREA := PI * R * R;
DBMS_OUTPUT.PUT_LINE('半径为10的圆的面积为:' || AREA);
END;