源码-PL/SQL从入门到精通-第三章-变量和类型-Part 2

时间:2022-04-11 14:52:26

不吐不快:

本来以为清华大学出版的书,质量上应该没的说,但是,通过调试示例程序,还是发现不少问题:

1. 个别概念解释不清,容易误导读者

2. 有些文字词不达意,不准确

3. 代码解释中有错别字,有的代码标题张冠李戴

之前说过的问题就不提了。总之,目前看到的最好的教材还是Java how to program,堪称完美!


代码如下:

--代码3.11 NUMBER类型声明示例
--说明:根据调试结果,基本可以确认Number(precesion,scale)中的precesion(精度)是指有效数字的位数,而不能简单理解为数字的长度。
--如,v_num6 NUMBER(4,-1):=31451;输出结果为31450,数字长度为5,而不是4(31450=3.145*10^1,科学计数法中的有效数字个数)。
--而另一参数-scale,意为赋值时小数点的移动位数,正数表示向右移动;负数表示向左移动
DECLARE
v_num1 NUMBER:=3.1415926; --结果:3.1415926
v_num2 NUMBER(3):=3.1415926; --四舍五入等于3
--v_num2_1 NUMBER(3):=3145.1415926; --错误,精度太高:ORA-06502:PL/SQL:数字或值错误:数值精度太高
v_num3 NUMBER(4,3):=3.1415926; --结果:3.142
--v_num3_1 NUMBER(4,3):=314.123; --错误,精度太高:ORA-06502:PL/SQL:数字或值错误:数值精度太高
v_num4 NUMBER(8,3):=31415.9267; --四舍五入3位小数,结果为:31415.927
v_num5 NUMBER(4,-3):=3145611.789; --由于为负3,要小数点左侧进行舍入3位结果为3146000
v_num5_1 NUMBER(4,-3):=314.567895; --舍入后的结果为0
v_num6 NUMBER(4,-1):=31451; --舍入后结果31450
v_num6_1 NUMBER(4,-1):=3145123; --错误,精度太高:ORA-06502:PL/SQL:数字或值错误:数值精度太高
BEGIN
DBMS_OUTPUT.put_line('v_num1:='||v_num1);
DBMS_OUTPUT.put_line('v_num2:='||v_num2);
DBMS_OUTPUT.put_line('v_num3:='||v_num3);
DBMS_OUTPUT.put_line('v_num4:='||v_num4);
DBMS_OUTPUT.put_line('v_num5:='||v_num5);
DBMS_OUTPUT.put_line('v_num5_1:='||v_num5_1);
DBMS_OUTPUT.put_line('v_num6:='||v_num6);
-- DBMS_OUTPUT.put_line('v_num6_1:='||v_num6);
END;

--代码3.12 PLS_INTEGER与BINARY_INTEGER的使用示例
DECLARE
v_num1 PLS_INTEGER :=2147483647;
BEGIN
--当为v_num1+1时,产生了溢出,会触发异常
v_num1:=v_num1+1-1;
EXCEPTION
WHEN OTHERS THEN
--输出:ORA-01426: 数字溢出
DBMS_OUTPUT.put_line(SQLERRM);
END;


--BINARY_INTEGER使用示例
--实际调试结果:会产生编译错误
DECLARE
v_num1 BINARY_INTEGER :=2147483647;
BEGIN
--以下论述与实际调试结果不符:
--当为v_num1+1时,产生了溢出,
--此时v_num1会被当作NUMBER进行处理,不会触发异常

v_num1:=v_num1+1-1;
EXCEPTION
WHEN OTHERS THEN
--输出:ORA-01426: 数字溢出
DBMS_OUTPUT.put_line(SQLERRM);
END;

select power(2,8) from dual;
select * from product_component_version;
select * from v$instance;
select * from V$VERSION

--代码3.13 Date类型使用示例
--to_char函数中的'D'的含义,当前日期在本周中的天数序号,星期日为第一天(这个可不符合中国人的习惯啊☺
--Day of week (1-7). This element depends on the NLS territory of the session.
DECLARE
v_weekday DATE := trunc(SYSDATE) - to_char(SYSDATE, 'D') + 1;
v_now DATE := SYSDATE;
BEGIN
dbms_output.put_line('上个星期天的日期为:'||to_char(v_weekday, 'yyyy-mm-dd'));
dbms_output.put_line('当前时间:'||to_char(v_now, 'yyyy-mm-dd hh24:mi:ss'));
END;


--代码3.14 TIMESTAMP类型使用示例
DECLARE
v_now TIMESTAMP(8) := SYSDATE;
v_nowdate DATE := SYSDATE;
BEGIN
dbms_output.put_line(v_now);
dbms_output.put_line(v_nowdate);
END;


--代码3.15 TIMESTATMP WITH TIME ZONE类型使用示例
DECLARE
v_timestamp TIMESTAMP; --定义日期类型的变量
v_timestampwithzone TIMESTAMP WITH TIME ZONE;
v_timestampwithlocalzone TIMESTAMP WITH LOCAL TIME ZONE;
BEGIN
v_timestamp := SYSDATE; --为日期类型的变量赋初值
v_timestampwithzone := SYSDATE;
v_timestampwithlocalzone :=SYSDATE;
DBMS_OUTPUT.put_line (v_timestamp); --输出信息
DBMS_OUTPUT.put_line (v_timestampwithzone);
DBMS_OUTPUT.put_line ( v_timestampwithlocalzone); --和timestamp的输出相同
END;

--INTERVAL YEAR TO MONTH类型使用示例
DECLARE
v_start TIMESTAMP; --定义起始与结束时间戳类型
v_end TIMESTAMP;
v_interval INTERVAL YEAR TO MONTH;
v_year NUMBER;
v_month NUMBER;
v_date DATE;
v_uinterv interval YEAR TO MONTH;
BEGIN
v_start := TO_TIMESTAMP('&v_date', 'yyyy-MM-dd'); --赋指定的时间戳值(此处优化了代码,允许用户任意输入一个日期(年月日,如2015-08-03))
v_end := CURRENT_TIMESTAMP; --赋当前的时间戳值
v_interval := (v_end - v_start) YEAR TO MONTH; --YEAR TO MONTH是INTERVAL表达式语法。
v_year := EXTRACT(YEAR FROM v_interval); --提取年份和月份
v_month := EXTRACT(MONTH FROM v_interval);
--输出当前的INTERVAL类型的值
DBMS_OUTPUT.put_line(to_char(v_start, 'yyyy-mm-dd') || '与当前时间的间隔INTERVAL值为:' ||
v_interval);
--输出年份与月份值
DBMS_OUTPUT.put_line('INTERVAL年份为:' || v_year || CHR(13) || CHR(10) ||
'INTERVAL月份为:' || v_month);
--直接为INTERVAL赋值
--此处增强代码,允许用户任意输入一个间隔(年月,如10年8个月,10-08),并计算经过该间隔定义的年月后的日期,
--如,17年后,您的孩子上大学时,是哪一年呢?☺
--彩蛋:如何在英文输入法下,输入笑脸?
--答案:1.打开Numlock开关(即小键盘/数字区)
--2. 按住Alt的同时,在小键盘区域连续输入9786,☺就会出现*
v_interval := INTERVAL '&v_uinterv' YEAR TO MONTH;
--输出INTERVAL的值
DBMS_OUTPUT.put_line('当前的INTERVAL值为:' || v_interval);
v_year :=EXTRACT(YEAR FROM v_interval);
v_month :=EXTRACT(month FROM v_interval);
DBMS_OUTPUT.put_line(v_year||'年'||v_month||'月后的日期为:'||to_char((sysdate+v_interval),'yyyy-mm-dd'));
v_interval := INTERVAL '01' YEAR; --直接为INTERVAL赋年份值
DBMS_OUTPUT.put_line('当前的INTERVAL值为:' || v_interval);
--提取年份和月份
v_year := EXTRACT(YEAR FROM v_interval);
v_month := EXTRACT(MONTH FROM v_interval);
--输出值
DBMS_OUTPUT.put_line('INTERVAL年份为:' || v_year || CHR(13) || CHR(10) ||
'INTERVAL月份为:' || v_month);
v_interval := INTERVAL '03' MONTH; --直接为INTERVAL赋月份
--输出月份值
DBMS_OUTPUT.put_line('当前的INTERVAL值为:' || v_interval);
END;

--代码 3.17 布尔类型使用示例
DECLARE
v_condition BOOLEAN;
BEGIN
v_condition := TRUE;
-- v_condition := 'FALSE';
IF v_condition
THEN
dbms_output.put_line('布尔值为True');
END IF;
END;

--代码3.18 弱类型REF CURSOR使用示例
CREATE OR REPLACE FUNCTION selectallemployments
RETURN sys_refcursor --定义一个返回sys_refcursor的函数
AS
st_cursor sys_refcursor;
BEGIN
OPEN st_cursor FOR --使用该函数查询所有的员工记录
SELECT *
FROM emp;
--返回指向游标的指针
RETURN st_cursor;
END;


--代码3.19 使用引用游标示例
DECLARE
x sys_refcursor; --定义引用游标变量
v_emp emp%ROWTYPE; --定义获取游标结果的记录类型
BEGIN
x := selectallemployments;--调用函数获取游标指针
--循环遍历游标指针
LOOP
FETCH x --提取游标数据
INTO v_emp;
--当没有找到游标记录时则退出
EXIT WHEN x%NOTFOUND;
--输出记录信息
DBMS_OUTPUT.put_line ( '员工编号:'
|| v_emp.empno
|| ' 员工名称:'
|| v_emp.ename
);
END LOOP;
END;


--代码3.20 用户自定义类型示例
DECLARE
SUBTYPE empcounttype IS INTEGER;
empcount empcounttype;
BEGIN
SELECT COUNT(*)
INTO empcount
FROM emp;
dbms_output.put_line('员工人数为:' || empcount);
END;