--开始介绍变量之前,我们先看下怎么在PLSQL写程序,如下我们写了一个块

  1. declare
  2. --声明部分,声明变量
  3. v_name varchar2(30) :='hello world';
  4.  
  5. begin
  6. --执行区域
  7. dbms_output.put_line(v_name);--如果使用SQL*PLUS,需要先执行set SERVEROUTPUT ON SIZE XXXXXX 才能显示打印信息
  8.  
  9. exception
  10. when others then
  11. null ; --异常处理区域
  12. end ; --结束符
  13. /

--以上是一个匿名块,它包含三部分,声明部分(声明变量、常量,游标,定义过程、函数)、执行区(执行SQL代码或PLSQL代码),异常处理部分
-- 声明部分和异常处理部分是可选的,比如可以这样,这是最简单的块

  1. begin
  2.  
  3. dbms_output.put_line('hello world');--
  4.  
  5. end ;
  6. /
  7.  
  8. --也可以什么都不做,可以使用NULL
  9.  
  10. begin
  11.  
  12. null ;
  13. end ;
  14. /

--需注意的是,每个语句结束都是用;(分号)作为结束符,比如END后面需要带上;,如果是匿名库,后面需要带上/,不然使用客户端比如SQLPLUS执行代码,是不会执行的

--有匿名块,相反也有命名块,命名块包含过程块,函数块。

-- 过程块,过程块接受(多个)入参和出参,但不直接返回值,不可以在SQL或查询语句中调用

  1. create or replace procedure test_procedure(i_name in varchar2/*,o_age out number*/) is
  2.  
  3. begin
  4.  
  5. dbms_output.put_line(i_name) ;
  6. /* o_age := 25;*/
  7.  
  8. end test_procedure;
  9.  
  10. --怎么调用过程块呢?
  11.  
  12. begin
  13.  
  14. test_procedure('hello world'); --在匿名块中直接调用
  15.  
  16. end ;
  17. /

--在sqlplus中也可以使用 execute test_procedure('Hi');
--也可以使用execute immediate 来执行动态语句,比如

  1. declare
  2. v_sql varchar2(300);
  3. v_name varchar2(30):='hello execute immediate';
  4. begin
  5.  
  6. v_sql := 'begin test_procedure(:v_name); end;';
  7.  
  8. execute immediate v_sql using v_name;
  9.  
  10. end ;
  11. /
  12. --与过程块不同,函数可以直接返回值,但不接受出参,可以在sql或查询语句中直接调用
  13. create or replace function test_function(i_name in varchar2)
  14. return varchar2 is
  15.  
  16. begin
  17. return i_name ||' hello world';
  18.  
  19. end test_function;
  20.  
  21. --可以在查询语句中调用,或直接赋值给变量,比如
  22. select test_function('Bob') from dual ;
  23. declare
  24. v_name varchar2(300);
  25. begin
  26. v_name := test_function('Bob');
  27. dbms_output.put_line(v_name);
  28. end ;
  29. /

-- 在SQLPLUS不能使用execute调用函数,因为前者不管理函数返回值
-- 可以使用VARIABLE 定义一个会话级的变量,然后使用CALL XXXXX INTO 变量

--块与块之间可以互相嵌套,比如在匿名块中,可以定义命名块,在命名块中可以使用匿名块,比如

  1. declare
  2.  
  3. procedure test_inline_block(i_name in varchar2,i_age in number) is
  4. begin
  5. dbms_output.put_line(i_name||',今年'||i_age||'岁了');
  6.  
  7. end ;
  8. begin
  9.  
  10. test_inline_block('my baby',5);
  11. end ;
  12. /
  13.  
  14. BEGIN
  15.  
  16. test_inline_block('my baby',5);
  17. END ;
  18. --现在来看下面的例子,能否执行成功?
  19.  
  20. DECLARE
  21. --定义过程
  22. PROCEDURE TEST_B IS
  23. BEGIN
  24.  
  25. DBMS_OUTPUT.put_line('Hello '||TEST_A); --调用函数
  26. END TEST_B;
  27.  
  28. --定义函数
  29. FUNCTION TEST_A RETURN VARCHAR2 IS
  30. BEGIN
  31. RETURN 'WORLD';
  32. END TEST_A ;
  33.  
  34. BEGIN
  35.  
  36. TEST_B;
  37.  
  38. END;
  39. /

--执行结果是报错了,TEST_A没有定义。
-- 为什么呢?PL/SQL是按照自顶向下的顺序,将标识符读取到内存中,这是一个单次分析过程。
--命名块也是标识符。程序在编译时,会对程序进行分析,并且识别标识符。在TEST_B调用TEST_A时,TEST_A还没有定义
--这种情况怎么解决呢?比较笨的方法是,把TEST_A放到TEST_B之前,比如

  1. DECLARE
  2.  
  3. --定义函数
  4. FUNCTION TEST_A RETURN VARCHAR2 IS
  5. BEGIN
  6. RETURN 'WORLD';
  7. END TEST_A ;
  8. --定义过程
  9. PROCEDURE TEST_B IS
  10. BEGIN
  11.  
  12. DBMS_OUTPUT.put_line('Hello '||TEST_A); --调用函数
  13. END TEST_B;
  14.  
  15. BEGIN
  16.  
  17. TEST_B;
  18.  
  19. END;
  20. /
  21. --也可以使用前向引用的方法,可以先声明命名块,占个位(STUB),再进行定义,比如
  22.  
  23. DECLARE
  24. PROCEDURE TEST_B;
  25. FUNCTION TEST_A RETURN VARCHAR2;
  26.  
  27. --定义过程
  28. PROCEDURE TEST_B IS
  29. BEGIN
  30.  
  31. DBMS_OUTPUT.put_line('Hello '||TEST_A); --调用函数
  32. END TEST_B;
  33.  
  34. --定义函数
  35. FUNCTION TEST_A RETURN VARCHAR2 IS
  36. BEGIN
  37. RETURN 'WORLD';
  38. END TEST_A ;
  39.  
  40. BEGIN
  41.  
  42. TEST_B;
  43.  
  44. END;
  45. /

--结果可以执行成功。
--接下来,我们来了解下变量
--基本上每一个PLSQL程序都要定义和操作数据,变量可以用来临时存储数据,操作以存储的数据,并且可复用。
--前面说过,变量可以在块中的声明部分进行定义,那么怎么命名变量呢,有如下几个规则

--名字必须以字母开始
--名字的长度最长可以30个字符
--第一个字母之后,可以使用字符包括,字母、数字、$、#、_
--所有的名字是不区分大小写的(除非名字被放在双引号里)
--不能使用关键字作为变量,比如number等

  1. DECLARE
  2.  
  3. D_$ NUMBER ;
  4. --d_$ number ;
  5. BEGIN
  6. dbms_output.put_line(d_$) ;
  7. END ;
  8.  
  9. --现在看一段代码
  10.  
  11. select * from MA_USERS;
  12. update ma_users t
  13. set t.user_score = null
  14. where 1=1;
  15. commit;
  16.  
  17. DECLARE
  18.  
  19. USER_Score NUMBER := 100;
  20. BEGIN
  21. dbms_output.put_line(USER_Score) ;
  22. UPDATE MA_USERS T
  23. SET T.User_Score = NVL(USER_Score,0)
  24. WHERE T.USER_SCORE IS NULL ;
  25.  
  26. END ;
  27. /

-- 大家可以先考虑下,这段代码的结果是什么,表中字段USER_CORE最终被更新成什么了?
--这是初学常会犯的错误,变量的名称跟表中的字段一样,结果使用的时候,很困惑,为什么变量的值为NULL,明明已经初始化了。
--命名有两点建议
-- 保证每个名字能准确地反映它的用途且一望即知
--建立一个一致的,明显的命名规范
--一般局部变量需要带一个前缀,我一般喜欢带"v_",游标变量带"cur_",全局变量带“g_”

--怎样声明一个变量呢?

  1. declare
  2. -- 变量名称 数据类型 [not null] [:= |defualt 初始化值]
  3. v_num number ;--数值,浮点小数
  4. v_date date ;--日期
  5. v_vch varchar2(100);--字符串,可变长度
  6. v_ch char(100);
  7. v_bool boolean ; -- 布尔值 true ,false null
  8.  
  9. v_num1 number(10,2):=0.3; -- 定义小数,左边八位,小数点右边两位
  10. v_num2 number(2) := 1;--整数
  11. -- 指定了not null 这个变量值不能为空
  12. v_date1 date not null default sysdate;
  13.  
  14. begin
  15. v_date1 := date '2015-9-1' ;
  16. v_vch :='test';
  17. end;
  18. /
  19. --赋值操作语法跟DEFAULT语法是等价的,可以互换使用,一般常用赋值操作语法(:=)
  20. --如果需要从数据库表或者其他PLSQL程序结构获得数据,一个好的事件方式是将变量和对象“锚定”在一起
  21. --如果是标量变量使用%TYPE,如果是复合变量,则使用%ROWTYPE
  22.  
  23. DECLARE
  24.  
  25. V_USER_NAME MA_USERS.USER_NAME%TYPE;--此变量数据类型与表中字段类型一样,有依赖关系
  26.  
  27. BEGIN
  28.  
  29. SELECT t.user_name into V_USER_NAME FROM MA_USERS t where rownum =1;
  30.  
  31. dbms_output.put_line(V_USER_NAME);
  32.  
  33. END ;
  34. /
  35. --使用“锚定”的好处
  36. --与数据库列的同步
  37. --局部变量的标准化
  38. --对于第一个种情况,我们假设原来ma_user表中的USER_NAME 原来是varchar2(50),这样使用
  39.  
  40. DECLARE
  41.  
  42. V_USER_NAME varchar2(50);
  43.  
  44. BEGIN
  45.  
  46. SELECT t.user_name into V_USER_NAME FROM MA_USERs t where rownum =1;
  47.  
  48. dbms_output.put_line(V_USER_NAME);
  49.  
  50. END ;
  51. /

--后来由于业务发展需要,或者满足更长的用户名的需要,这个字段需要扩展到varchar2(100),这样问题就来了,上面的代码需要改造,不然会报错(想想为什么?)
--如果程序里使用的情况多的话,需要一一找出来,做影响分析,然后进行修改,修改的点还要一一测试,这需要耗费很多的人力
--如果一开始使用%type方式声明,就不存在这种情况

--前面,有涉及到标量一词,在PL/SQL中,有标量和复合变量之分

--什么是标量?标量只是一个单独的值构成,比如一个数字或者字符串

--常用的标量数据类型,有char,varchar2,number,date,boolean

  1. declare
  2. -- 变量名称 数据类型 [not null] [:= |defualt 初始化值]
  3. v_num number ;--数值,浮点小数
  4. v_date date ;--日期
  5. v_vch varchar2(100);--字符串,可变长度
  6. v_bool boolean ; -- 布尔值
  7.  
  8. begin
  9. null ;
  10. end;
  11. /
  12. --关于CHARVARCHAR2,一个是定长,一个是可变长度,比较常用的是可变长度的字符串,节省空间,看以下例子
  13. DECLARE
  14. V_CH CHAR(100);
  15. V_VAR VARCHAR2(100);
  16. BEGIN
  17. V_CH := 'TEST';
  18. V_VAR :='TEST';
  19. DBMS_OUTPUT.put_line(LENGTH(V_CH));
  20. DBMS_OUTPUT.put_line(LENGTH(V_VAR));
  21. END ;
  22. /
  23. --数值类型的变量使用时,需主要值的精度,比如以下两个变量,v_num打印的结果是保留了两位小数,四舍五入
  24. --V_NUM1直接报错,精度不一致。
  25. declare
  26. v_num number(5,2) := 52.2365;
  27. --v_num1 number(3,2) := 52.2365;
  28. begin
  29.  
  30. dbms_output.put_line(v_num);
  31.  
  32. end ;
  33. /

-- 什么是复合变量?有多个值组成,比如一个记录,一个集合或者一个对象
--复合变量包括记录,表,嵌套表,VARRAY
--记录类型是一种复合的数据结构,由多个元素或成员组成的,每个成员都有自己的值,类似与C中的结构体
--PL/SQL中的记录类型从结构上和概念上非常类似于数据库表的行
--记录有三种定义方式

  1. --第一种是基于表的记录类型,使用表名加上%rowtype声明一个记录类型
  2. DECLARE
  3. V_USER MA_USERS%ROWTYPE;
  4.  
  5. BEGIN
  6. SELECT * INTO V_USER FROM MA_USERS
  7. WHERE ROWNUM =1;
  8. DBMS_OUTPUT.put_line(V_USER.USER_NAME);
  9.  
  10. END ;
  11. /
  12. --基于游标的记录类型,使用显示声明的游标或游标变量加上%rowtype声明基于一个游标的记录类型
  13. DECLARE
  14. CURSOR CUR_USER IS
  15. SELECT *
  16. FROM MA_USERS T
  17. WHERE T.USER_NAME LIKE 's%';
  18.  
  19. v_user_cur CUR_USER%ROWTYPE;
  20.  
  21. BEGIN
  22. OPEN CUR_USER;
  23. LOOP
  24. FETCH CUR_USER
  25. INTO V_USER_CUR;
  26. EXIT WHEN CUR_USER%NOTFOUND;
  27. DBMS_OUTPUT.put_line(V_USER_CUR.USER_NAME);
  28. END LOOP;
  29. CLOSE CUR_USER;
  30. END;
  31. /
  32. --另外一种声明记录的方式是隐式的,主要见于游标型FOR循环中,关于游标详细的内容在下节课详讲
  33. --自定义记录类型
  34. --使用TYPE XXXX IS RECORD定义,比如
  35.  
  36. DECLARE
  37. --定义记录类型
  38. TYPE USER_TYP IS RECORD(
  39. USER_NAME MA_USERS.USER_NAME%TYPE,
  40. USER_SCORE MA_USERS.USER_SCORE%TYPE);
  41. --声明记录类型变量
  42. V_USER USER_TYP;
  43.  
  44. BEGIN
  45.  
  46. SELECT USER_NAME, USER_SCORE
  47. INTO V_USER
  48. FROM MA_USERS T
  49. WHERE T.USER_NAME = 'scott';
  50.  
  51. DBMS_OUTPUT.put_line(V_USER.USER_NAME || ',积分' || V_USER.USER_SCORE);
  52.  
  53. END;
  54. /

---------------------------------------------------------

假如现在要做一个线上订购系统,这个系统首先有一个用户模块,所以我们要设计这个模块,
首先要设计一张用户表存放用户信息要求记录用户的用户名,密码,证件号,真实姓名,性别,生日,手机号,地址,email地址,积分,激活状态等,其它信息可各自补充
1. 请大家设计这个用户表
2.假如现在系统已经完成,网站要做一个贴心活动,对于注册且激活了且生日即将到达的用户(生日前五天),
我们根据其注册时间的长短,送积分,积分数=注册时间月数*100,请大家用PLSQL实现这个功能

  1. oracle环境
  2. Oracle Database 12c Enterprise Edition Release 12.1.0.1.0 - 64bit Production
  3. PL/SQL Release 12.1.0.1.0 - Production
  4. CORE 12.1.0.1.0 Production
  5. TNS for Linux: Version 12.1.0.1.0 - Production
  6. NLSRTL Version 12.1.0.1.0 - Production
  7. 使用工具
  8. Toad for Oracle
  9.  
  10. 1 创建表
  11. CREATE TABLE PLSQLXUNLIAN_USERS
  12. (
  13. USER_ID VARCHAR2(32 BYTE) DEFAULT sys_guid(),
  14. USER_NAME VARCHAR2(64 BYTE) NOT NULL,
  15. USER_PASSWORD VARCHAR2(64 BYTE) NOT NULL,
  16. ID_NO VARCHAR2(64 BYTE) NOT NULL,
  17. REAL_NAME VARCHAR2(20 BYTE),
  18. SEX NUMBER(1),--0=woman,1=man
  19. BIRTHDAY DATE NOT NULL,
  20. MOBILE_PHONE NUMBER(15),
  21. ADDRESS VARCHAR2(100 BYTE),
  22. EMAIL VARCHAR2(100 BYTE),
  23. USER_INTEGRAL NUMBER DEFAULT 0,
  24. USER_STATUS NUMBER(2) NOT NULL,--0=inactive,1=active,-1=delete
  25. USER_PHOTO VARCHAR2(100 BYTE),
  26. REGISTER_DATE DATE NOT NULL,
  27. CREATE_ON DATE DEFAULT sysdate NOT NULL,
  28. UPDATE_ON DATE
  29. )
  30. 2 插入测试数据
  31. insert into plsqlxunlian_users(user_name,user_password,id_no,real_name,sex,birthday,user_status,register_date)
  32. select ename,sys_guid(),empno,empno,1,hiredate,1,sysdate-50 from emp
  33.  
  34. 3 plsql代码
  35. /* Formatted on 2016/2/24 18:24:30 (QP5 v5.149.1003.31008) */
  36. DECLARE
  37. v_user_id VARCHAR2 (100);
  38. v_birthday DATE;
  39. v_user_integral NUMBER;
  40. v_user_status NUMBER;
  41. v_register_date DATE;
  42. v_sysdate DATE DEFAULT SYSDATE;
  43. v_months NUMBER DEFAULT 0;
  44.  
  45. CURSOR cur_user
  46. IS
  47. SELECT user_id,
  48. birthday,
  49. user_integral,
  50. user_status,
  51. register_date
  52. FROM plsqlxunlian_users t
  53. WHERE t.user_status = 1
  54. AND TO_CHAR (birthday, 'mm-dd') BETWEEN TO_CHAR (v_sysdate ,
  55. 'mm-dd')
  56. AND TO_CHAR (v_sysdate + 5,
  57. 'mm-dd');
  58. /*
  59. 对于注册且激活了且生日即将到达的用户(生日前五天),
  60. 我们根据其注册时间的长短,送积分,积分数=注册时间月数*100,请大家用PLSQL实现这个功能
  61. */
  62. BEGIN
  63. FOR user_c IN cur_user
  64. LOOP
  65. v_user_id := user_c.user_id;
  66. v_user_integral := user_c.user_integral;
  67. v_register_date := user_c.register_date;
  68.  
  69. SELECT CEIL (MONTHS_BETWEEN (v_sysdate,v_register_date))
  70. INTO v_months
  71. FROM DUAL;
  72.  
  73. ---update user's integral
  74.  
  75. UPDATE plsqlxunlian_users
  76. SET user_integral = v_user_integral + v_months * 100,
  77. update_on = v_sysdate
  78. WHERE user_id = v_user_id;
  79.  
  80. --可根据需要添加赠送积分的记录表
  81. DBMS_OUTPUT.put_line (user_c.user_id);
  82. END LOOP;
  83.  
  84. COMMIT;
  85. END;
  86. /

PL/SQL 训练01--基础介绍的更多相关文章

  1. PL/SQL 训练05--游标

    --隐式游标--通过一个简单的SELECT ...INTO 语句提取一行数据,并放在一个局部变量中,最简单获取数据的途径 --显示游标--可以在声明单元明确的声明一个查询,这样可以在一个或多个程序中打 ...

  2. PL/SQL 训练13--plsql 优化

    --数据缓存技术 --PGA和SGA---SGA:系统全局区域--PGA:Process Global Area是为每个连接到Oracle的用户进程保留的内存. ---PLSQL从PGA获取信息的速度 ...

  3. PL/SQL 训练10--io及文件操作

    多数程序只需要通过SQL和底层数据库进行交互--有些情况,不可避免的还是会有一些场景,需要从PL/SQL给外部环境发送信息--或是从一些外部的源读入信息 --这节课介绍下面这些内置包 dbms_out ...

  4. PL/SQL 训练04--事务

    --pl/sql通过SQL和ORACLE数据库紧密的整合在一起--在pl/sql中可以执行任何操作语句(DML语句),包括INSERT,UPDATE,DELETE,MERGE,也包括查询语句--可否执 ...

  5. Oracle PL/SQL学习之基础篇(1)

    1.PL/SQL,全称Procedure Language/SQL,过程化sql语言 PL/SQL的程序结构 declare --声明部分(包括变量.光标.例外声明) begin --语句序列(DML ...

  6. Oracle PL/SQL编程之基础

    1.简介:pl/sql块由三个部分组成:定义部分.执行部分.例外处理部分,如下所示: declare: /*定义部分---定义常量.变量.游标.例外.复杂数据类型 begin /*执行部分---要执行 ...

  7. PL/SQL 训练12--动态sql和绑定变量

    --什么是动态SQL?动态PL/SQL--动态SQL是指在运行时刻才构建执行的SQL语句--动态PL/SQL是指整个PL/SQL代码块都是动态构建,然后再编译执行 --动态SQL来可以用来干什么? - ...

  8. PL/SQL 训练11--包

    --所谓包,就是把一组PL/SQL的代码元素组织在一个命名空间下.--一种可以把程序或者其他的PL/SQL元素比如游标.类型.变量的组织结构在一起的结构(包括逻辑结构和物理结构)--包提供了非常重要的 ...

  9. PL/SQL 训练08--触发器

    --什么是触发器呢?--一触即发,某个事件发生时,执行的程序块?--数据库触发器是一个当数据库发生某种事件时作为对这个事件的响应而执行的一个被命名的程序单元 --适合场景--对表的修改做验证--数据库 ...

随机推荐

  1. struts中操作request,session

    在Action类中操作request,session 方法一.利用ActionContext.getContext().get("request"); //返回的是Map集合 Ma ...

  2. a, b交换与比较问题

    1. 求a, b中较大的数,不使用if.?.switch等判断语句. 答案: 另一种思路是求两者的差,然后通过位运算判断差值的正负,不过个人觉得还是第一种各位简洁优雅. 2. 交换a, b的值,要求不 ...

  3. 剑指offer--37.和为S的两个数字

    链接:https://www.nowcoder.com/questionTerminal/390da4f7a00f44bea7c2f3d19491311b来源:牛客网@华科渣硕 不要被题目误导了!证明 ...

  4. PHP生成UTF-8编码的CSV文件用Excel打开乱码的问题

    在你要输出的内容前先输出"\xEF\xBB\xBF",例如:你要输出的内容保存在$content里$content = "\xEF\xBB\xBF".$cont ...

  5. js控制iframe的刷新(页面局部刷新)

    今天遇到个问题,后台会员审核之后,页面内的会员审核状态要及时改变,但又不能指望用户手动刷新(用户体验很不好) 如果审核页面和显示审核状态时同在一个html页面的话,那么直接用js改变div内部的文本就 ...

  6. 【android】下载文件至本应用程序的file目录或者sdcard

     一.判断是否有sdcard卡 //判断是否有SD卡 //ture:有SD卡 //false:没有SD卡 public boolean avaiableMedia(){ String status ...

  7. Unity 2D 入门

    原文:Introduction to Unity 2D 作者:Sean Duffy 译者:kmyhy 3/15/17 更新说明: 升级至 Unity 5.5. Unity 是一个非常流行和强大的游戏引 ...

  8. Arcgis for Javascript之featureLayer图和属性的互操作

    说明:主要实现加载FeatureLayer与显示属性表,并实现属性表与地图的联动,首先,看看实现后的效果: 显示效果 如上图所示,本文章主要实现了以下几个功能:1.FeatureLayer属性表的分页 ...

  9. beego配置文件

    关于App配置: #App配置 for Api AppName = ApiService RunMode = dev RouterCaseSensitive = true ServerName = A ...

  10. SQLServer清空数据库中所有表的数据

    今早同事跟进客户反馈的问题时,提了个要求,要求清空数据库中所有表的数据. 记得之前用游标遍历所有的表名 + exec 动态语句 truncate table 表名 实现过这个功能. 网上搜了下,有更简 ...