PL/SQL笔记1

来源:互联网 发布:linux svn 删除库 编辑:程序博客网 时间:2024/05/29 00:31

1、  NVL(v_counter,0)

2、
set serveroutput on
declare
    v_descript varchar2(35);
   
    v_number_test number(8,2);
    v_location constant varchar2(4) := '603D';
   
    v_boolean_test boolean;
    v_start_date date := trunc(sysdate) + 7;
begin
    if v_descript = 'Introduction to underwater'
    then
        dbms_output.put_line('This course is ' || v_descript || ',');
    elsif v_location = '603D'
    then
        if v_descript is not null
        then
            dbms_output.put_line('The course is '|| v_descript || '.' || ' The
            location is ' || v_location || '.');
        end if;
    else
        dbms_output.put_line('The course and location ' || 'could not be determined');
    end if;
exception
    when others then
    dbms_output.put_line('An error occurred.');
   
end;

3、
set serveroutput on
declare
    v_student_id number := 102;
    v_section_id number := 89;
    v_letter_grade char(1);
begin
    select case
                when final_grade >= 90 then 'A'
                when final_grade >= 80 then 'B'
                when final_grade >= 70 then 'C'
                when final_grade >= 60 then 'D'
                else 'F'
            end
      into v_letter_grade from enrollment
      where student_id = v_student_id and section_id = v_section_id;
     
      dbms_output.put_line(v_letter_grade);
end;
     
4、 本地动态SQL
OPEN-FOR,FETCH和CLOSE语句被用来处理多行查询或者游标  
实例一:
declare
    --弱游标类型
    type student_cur_type is ref cursor;
    --游标变量
    student_cur student_cur_type;
   
    v_zip varchar2(5) := '&sv_zip';
    v_first_name varchar2(25);
    v_last_name varchar2(25);  
    sql_stmt    varchar2(500);
begin
    --select查询语句也可以放到动态SQL变量里面
    /* sql_stmt := 'select first_name,last_name' ||
                '  from student ' ||
                'where zip = :1';
   
    open student_cur for sql_stmt using v_zip;     */
    open student_cur for
       'select first_name,last_name
       from student ' ||
       'where zip = :1'
       using v_zip;
      
      
    loop
        fetch student_cur into v_first_name,v_last_name;
        exit when student_cur%NOTFOUND;
       
        dbms_output.put_line('First Name: ' || v_first_name);
        dbms_output.put_line('Last Name: ' || v_last_name);
    end loop;

    close student_cur;
exception
    when others then
        if student_cur%ISOPEN
        then
            close student_cur;
        end if;
        dbms_output.put_line('ERROR: ' || substr(SQLERRM,1,200));
end;

实例二:可以将select返回的结果放入到record记录中
set serveroutput on
declare
    type zip_cur_type is ref cursor;
    zip_cur zip_cur_type;
   
    type zip_rec_type is record
    (
        zip varchar2(5),
        total number
    );
   
    zip_rec zip_rec_type;
   
    v_table_name varchar2(20) := '&sv_table_name';
    sql_stmt    varchar2(500);
    v_count number;
begin
    dbms_output.put_line('Totals from ' || v_table_name || ' table');
    sql_stmt := 'select zip, count(*) total ' ||
                '  from ' || v_table_name || ' ' ||
                ' group by zip';
    v_count := 0;
    open zip_cur for sql_stmt;   
    loop
        fetch zip_cur into zip_rec;
        exit when zip_cur%NOTFOUND;
       
        v_count := v_count + 1;
        if v_count <= 10
        then
            dbms_output.put_line('zip: ' || zip_rec.zip);
            dbms_output.put_line('total: ' || zip_rec.total);
        end if;
    end loop;
   
    close zip_cur;
exception
    when others then
        if zip_cur%ISOPEN
        then
           close zip_cur;
        end if;
end;       
       
       
5、 批量SQL
PL/SQL和SQL引擎之间的通信称为上下文切换
FORALL语法
forall loop_counter in bounds_clause
  SQL_STATEMENT[SAVE EXCEPTIONS];
 
bounds_clause有三种形式:
lower_limit..upper_limit
indices of collection_name between lower_limit..upper_limit
values of collection_name

当使用values of选项时,注意以下的限制:
如果values of子句中所使用的集合是联合数组,它必须使用PLS_INTEGER和BINAY_INTEGER进行索引
values of子句中所使用集合的元素必须是PLS_INTEGER或者BINARY_INTEGER
当values of子句所引用的集合是空的, ForALL子句会导致异常

实例:
create table test (row_num number, row_text varchar2(10));

declare
  type row_num_type is table of number index by pls_integer;
  type row_text_type is table of varchar2(10) index by pls_integer;
 
  row_num_tab row_num_type;
  row_text_tab row_text_type;
 
  v_total number;
begin
  for i in 1..10 loop
    row_num_tab(i) := i;
    row_text_tab(i) := 'row ' || i;
  end loop;
 
  forall i in 1..10
    insert into test(row_num,row_text)
    values (row_num_tab(i), row_text_tab(i));
   
  commit;
 
  select count(*) into v_total from test;
  dbms_output.put_line(v_total);
end;


--create table test (row_num number, row_text varchar2(10));

declare
  type row_num_type is table of number index by pls_integer;
  type row_text_type is table of varchar2(10) index by pls_integer;
 
  row_num_tab row_num_type;
  row_text_tab row_text_type;
 
  v_total number;
 
  v_start_time integer;
  v_end_time integer;
begin
  for i in 1..10000 loop
    row_num_tab(i) := i;
    row_text_tab(i) := 'row ' || i;
  end loop;
 
  v_start_time := dbms_utility.get_time;
  for i in 1..10000
  loop
    insert into test(row_num,row_text)
    values (row_num_tab(i), row_text_tab(i));
  end loop;
 
  v_end_time := dbms_utility.get_time;
 
  dbms_output.put_line('Duration of the for for loop: ' || (v_end_time-v_start_time));
 
  v_start_time := dbms_utility.get_time;
  forall i in 1..10000
    insert into test(row_num,row_text)
    values (row_num_tab(i), row_text_tab(i));
  v_end_time := dbms_utility.get_time;
 
  dbms_output.put_line('Duration of the for for loop: ' || (v_end_time-v_start_time));
 
  commit;
end; 
/
输出结果如下:
Duration of the for for loop: 32
Duration of the for for loop: 0
 
PL/SQL procedure successfully completed
注释:使用dbms_utility包的get_time函数,Oracle用户sys拥有这个包。
get_time函数会返回当前时间,精度为百分之一秒

indices of选项
可以循环处理稀疏的集合
--看下面的例子,如果直接使用forall会报错
declare
  type row_num_type is table of number index by pls_integer;
  type row_text_type is table of varchar2(10) index by pls_integer;
 
  row_num_tab row_num_type;
  row_text_tab row_text_type;
 
  v_total number;
begin
  for i in 1..10 loop
    row_num_tab(i) := i;
    row_text_tab(i) := 'row ' || i;
  end loop;

  row_num_tab.delete(1);row_text_tab.delete(1);
  row_num_tab.delete(5);row_text_tab.delete(5);
  row_num_tab.delete(7);row_text_tab.delete(7);
 
  forall i in 1..10
    insert into test(row_num,row_text)
    values (row_num_tab(i), row_text_tab(i));
 
  commit;
end; 
/
ORA-22160: 下标 [1] 中的元素不存在
ORA-06512: 在 line 20

使用indices of就不会有问题:
--create table test (row_num number, row_text varchar2(10));

declare
  type row_num_type is table of number index by pls_integer;
  type row_text_type is table of varchar2(10) index by pls_integer;
 
  row_num_tab row_num_type;
  row_text_tab row_text_type;
 
  v_total number;
begin
  for i in 1..10 loop
    row_num_tab(i) := i;
    row_text_tab(i) := 'row ' || i;
  end loop;

  row_num_tab.delete(1);row_text_tab.delete(1);
  row_num_tab.delete(5);row_text_tab.delete(5);
  row_num_tab.delete(7);row_text_tab.delete(7);
 
  forall i in indices of row_num_tab
    insert into test(row_num,row_text)
    values (row_num_tab(i), row_text_tab(i));
 
  commit;
end; 
/
PL/SQL procedure successfully completed
 
SQL> select * from test;
 
   ROW_NUM ROW_TEXT
---------- ----------
         2 row 2
         3 row 3
         4 row 4
         6 row 6
         8 row 8
         9 row 9
        10 row 10
 
7 rows selected

values of选项
实例:

drop table test_exc;
create table test_exc (row_num number,row_text varchar2(50));
truncate table test;
declare
    type row_num_type is table of number index by binary_integer;
    type row_text_type is table of varchar2(11) index by binary_integer;
    type exc_ind_type is table of pls_integer index by binary_integer; --可以在values of中引用这个集合
    row_num_tab row_num_type;
    row_text_tab row_text_type;
    exc_ind_tab exc_ind_type;
 
    errors exception;
    pragma exception_init(errors, -24381);
   
begin
    for i in 1..10
    loop
        row_num_tab(i) := i;
        row_text_tab(i) := 'row ' || i;
    end loop;
   
    row_text_tab(1) := rpad(row_text_tab(1), 11, ' ');
    row_text_tab(5) := rpad(row_text_tab(5), 11, ' ');
    row_text_tab(7) := rpad(row_text_tab(7), 11, ' ');
   
    forall i in 1..10 save exceptions
        insert into test (row_num,row_text)
        values (row_num_tab(i),row_text_tab(i));
   
    commit;
exception
    when errors then
        --统计插入表test中异常数据的行数
        for i in 1.. SQL%BULK_EXCEPTIONS.COUNT
        loop
            --存储异常的索引值
            exc_ind_tab(i) := SQL%BULK_EXCEPTIONS(i).error_index;
        end loop;

    forall i in values of exc_ind_tab
        insert into test_exc (row_num,row_text)
        values (row_num_tab(i), row_text_tab(i));

    commit;
end;   
/
输出结果为:
SQL> select * from test;--可以正常插入
 
   ROW_NUM ROW_TEXT
---------- ----------
         2 row 2
         3 row 3
         4 row 4
         6 row 6
         8 row 8
         9 row 9
        10 row 10
 
7 rows selected
 
SQL> select * from test_exc; --插入表test失败后,插入到test_exc表里面
 
   ROW_NUM ROW_TEXT
---------- --------------------------------------------------
         1 row 1
         5 row 5
         7 row 7
                   
注释:
save exceptions选项:即使当对应的SQL语句导致异常,forall语句仍旧能够继续执行。这些异常
被存储在名为SQL%EXCEPTIONS的游标属性中,SQL%EXCEPTIONS游标是个集合属性,其中每个记录包含两个字段ERROR_INDEX和ERROR_CODE


使用bulk collect子句
批量检索数据
create table my_instructor as select * from instructor;
declare
    type instructor_id_type is table of my_instructor.instructor_id%TYPE;
    type first_name_type is table of my_instructor.first_name%TYPE;
    type last_name_type is table of my_instructor.last_name%TYPE;
   
    instructor_id_tab instructor_id_type;
    first_name_tab first_name_type;
    last_name_tab last_name_type;
begin
    select instructor_id,first_name,last_name
        bulk collect into instructor_id_tab,first_name_tab,last_name_tab
        from my_instructor;
   
    for i in instructor_id_tab.first..instructor_id_tab.last
    loop
        dbms_output.put_line('instructor_id: ' || instructor_id_tab(i));
        dbms_output.put_line('first_name: ' || first_name_tab(i));
        dbms_output.put_line('last_name: ' || last_name_tab(i));
    end loop;

end;
/
输出:
instructor_id: 101
first_name: Fernand
last_name: Hanks
instructor_id: 102
first_name: Tom
last_name: Wojick
instructor_id: 103
first_name: Nina
last_name: Schorin
instructor_id: 104
first_name: Gary
last_name: Pertez
instructor_id: 105
first_name: Anita
last_name: Morris
instructor_id: 106
first_name: Todd
last_name: Smythe

bulk collect 子句类似于游标循环,因为当select语句不返回任何数据行时,它不会抛出no_data_found异常

如果将my_instructor里面的数据清空时,再执行就会报错
ORA-06502: PL/SQL: 数字或值错误
ORA-06512: 在 line 15
 
修改上面的SQL代码:
create table my_instructor as select * from instructor;
declare
    type instructor_id_type is table of my_instructor.instructor_id%TYPE;
    type first_name_type is table of my_instructor.first_name%TYPE;
    type last_name_type is table of my_instructor.last_name%TYPE;
   
    instructor_id_tab instructor_id_type;
    first_name_tab first_name_type;
    last_name_tab last_name_type;
begin
    select instructor_id,first_name,last_name
        bulk collect into instructor_id_tab,first_name_tab,last_name_tab
        from my_instructor;
   
    if instructor_id_tab.count >0
    then
        for i in instructor_id_tab.first..instructor_id_tab.last
        loop
            dbms_output.put_line('instructor_id: ' || instructor_id_tab(i));
            dbms_output.put_line('first_name: ' || first_name_tab(i));
            dbms_output.put_line('last_name: ' || last_name_tab(i));
        end loop;
    end if;
end;

 

过程
1. 创建过程
create or replace procedure Discount
as
    cursor c_group_discount
    is
      select distinct s.course_no, c.description     
      from section s, enrollment e, course c
      where s.section_id = e.section_id
        and c.course_no = s.course_no
        group by s.course_no, c.description,
                 e.section_id, s.section_id
        having count(*) >= 8; --course_no记录至少为8条
       
begin
    for r_group_discount in c_group_discount
    loop
        update course
            set cost = cost * .95
          where course_no = r_group_discount.course_no;
        dbms_output.put_line('A 5% discount has been given to ' ||
        r_group_discount.course_no || ' ' ||
        r_group_discount.description);
    end loop;
end;
/

2.执行过程
execute Discount
SQL> execute Discount
 
A 5% discount has been given to 120 Intro to Java Programming
A 5% discount has been given to 140 Systems Analysis
A 5% discount has been given to 230 Intro to the Internet
A 5% discount has been given to 25 Intro to Programming
A 5% discount has been given to 122 Intermediate Java Programming
A 5% discount has been given to 240 Intro to the BASIC Language     

查看错误
show error

比如将上面的course表写错
Warning: Procedure created with compilation errors
 
SQL> show error;
Errors for PROCEDURE JIANG.DISCOUNT:
 
LINE/COL ERROR
-------- ---------------------------------
16/16    PL/SQL: ORA-00942: 表或视图不存在
16/9     PL/SQL: SQL Statement ignored

3.从数据字典查询有关过程的信息
在数据字典中,两个重要的视图用于提供有关存储代码的信息
USER_OBJECTS会显示有关对象的信息
USER_SOURCE会显示源代码文本

数据字典也有这些视图的ALL_和DBA_版本

查询上面创建的discount存储过程信息
select object_name, object_type, status
  from user_objects
  where object_name = 'DISCOUNT';
列status表示存储编译的是否成功

使用USER_SOURCE视图查询DISCOUNT的源代码
SQL> desc user_source;
Name Type           Nullable Default Comments                                                                                                                            
---- -------------- -------- ------- ------------------------------------------------------------------------------------------------------------------------------------
NAME VARCHAR2(30)   Y                Name of the object                                                                                                                  
TYPE VARCHAR2(12)   Y                Type of the object: "TYPE", "TYPE BODY", "PROCEDURE", "FUNCTION",
"PACKAGE", "PACKAGE BODY", "LIBRARY", "ASSEMBLY" or "JAVA SOURCE"
LINE NUMBER         Y                Line number of this line of source                                                                                                  
TEXT VARCHAR2(4000) Y                Source text

SQL> column text format a70 --设置text列的每行显示长度
SQL> select to_char(line,99) || '>', text
  2  from user_source where name = 'DISCOUNT';
 
TO_CHAR(LINE,99)||'>' TEXT
--------------------- --------------------------------------------------------------------------------
  1>                  procedure Discount
  2>                  as
  3>                      cursor c_group_discount
  4>                      is
  5>                        select distinct s.course_no, c.description
  6>                        from section s, enrollment e, course c
  7>                        where s.section_id = e.section_id
  8>                          and c.course_no = s.course_no
  9>                          group by s.course_no, c.description,
 10>                                   e.section_id, s.section_id
 11>                          having count(*) >= 8; --course_no记录至少为8条
 12>                 
 13>                  begin
 14>                      for r_group_discount in c_group_discount
 15>                      loop
 16>                          update coursse
 17>                              set cost = cost * .95
 18>                            where course_no = r_group_discount.course_no;
 19>                          dbms_output.put_line('A 5% discount has been given to ' ||
 20>                          r_group_discount.course_no || ' ' ||
 
TO_CHAR(LINE,99)||'>' TEXT
--------------------- --------------------------------------------------------------------------------
 21>                          r_group_discount.description);
 22>                      end loop;
 23>                  end;
 24>                 
 
注:如果某过程所基于的表被删除或者修改,则该过程就会变为无效。重新编译如下:
alter procedure procedure_name compile;


过程传入和传出参数
使用IN和OUT过程参数
create or replace procedure find_name
    (i_student_id in number,
     o_first_name out varchar2,
     o_last_name out varchar2
    )
as
begin
  select first_name, last_name
    into o_first_name, o_last_name
    from student
    where student_id = i_student_id;
exception
    when others
    then
        dbms_output.put_line('Error in finding student_id: ' ||
        i_student_id);

end find_name;       
       
declare
    v_local_first_name student.first_name%TYPE;
    v_local_last_name student.last_name%TYPE;
begin
    find_name(145, v_local_first_name, v_local_last_name);
    dbms_output.put_line('Student 145 is: ' ||
    v_local_first_name || ' ' ||
    v_local_last_name || '.');
   
end;
/

输出:   
Student 145 is: Paul Lefkowitz.   


函数
创建和使用函数
函数和过程区别:
函数式能够返回单个值的PL/SQL语句块,函数可以接收一个或者多个参数,也可以不接收参数,但是函数的可执行部分
必须有一个返回子句。必须在函数的头部声明返回值的数据类型。
实例1:
create or replace function show_description
    (i_course_no course.course_no%TYPE)
    return varchar2
as
    v_description varchar2(50);
begin
    select description
      into v_description
      from course
     where course_no = i_course_no;
     return v_description;
exception
    when no_data_found then
        return('The Course is not in the database');
    when others
    then
        return('Error in returning show_description');
end;
/

使用:
declare
    v_description varchar2(50);
begin
    v_description := show_description(&sv_number);
    dbms_output.put_line(v_description);
end;


       
实例2:
create or replace function id_is_good
    (i_student_id in number)
    return boolean
as
    v_id_cnt number;
begin
    select count(*)
      into v_id_cnt
      from student
     where student_id =  i_student_id;
    return 1 = v_id_cnt;
exception
    when others
    then
        return false;
end id_is_good;
/
使用:
declare
    v_id number;
begin
    v_id := &id;
    if id_is_good(v_id)
    then
        dbms_output.put_line('Student ID: ' || v_id || ' is a valid.');
    else
        dbms_output.put_line('Student ID: ' || v_id || ' is not valid.');   
    end if;
end;
   
在SQL语句中调用函数
对于在SQL表达式中使用用户定义的函数,它必须是ROW函数,而不是group函数,并且数据类型必须是SQL数据类型。
数据类型不是PL/SQL数据类型。初次之外,该函数不能使用任何的DML(insert,update,delete)
SQL> select course_no , show_description(course_no) from course;
 
COURSE_NO SHOW_DESCRIPTION(COURSE_NO)
--------- --------------------------------------------------------------------------------
       10 Technology Concepts
       20 Intro to Information Systems
       25 Intro to Programming
       80 Programming Techniques
      100 Hands-On Windows
      120 Intro to Java Programming
      122 Intermediate Java Programming
      124 Advanced Java Programming
      125 Java Developer I
     
实例3:    
SQL> create or replace function new_instructor_id
  2      return instructor.instructor_id%TYPE
  3  as
  4      v_new_instid instructor.instructor_id%TYPE;
  5  begin
  6      select instructor_id_seq.nextval
  7        into v_new_instid
  8      from dual;
  9      return v_new_instid;
 10  exception
 11      when others
 12      then
 13          declare
 14              v_sqlerrm varchar2(250) := substr(sqlerrm,1,250);
 15          begin
 16              raise_application_error(-20003,'Error in instructor_id: ' || v_sqlerrm);
 17      end;
 18  end new_instructor_id;
 19  /
 
Function created
 
SQL> declare
  2      v_new_instid instructor.instructor_id%TYPE;
  3  begin
  4      v_new_instid := new_instructor_id();
  5      dbms_output.put_line(v_new_instid);
  6  end;
  7  /
 
114     

 

 

 



(1) 创建包规范
--调用此代码时,会编译但不会执行
create or replace package manage_students
as
    procedure find_name
    (i_student_id in student.student_id%TYPE,
     o_first_name out student.first_name%TYPE,
     o_last_name out student.last_name%TYPE
    );
   
    function id_is_good
    (i_student_id in student.student_id%TYPE)
    return boolean;
end manage_students;
/

创建名为school_api的包规范
create or replace package school_api
as
    procedure discount;
    function new_instructor_id
    return instructor.instructor_id%TYPE;
end school_api;
/

(2) 创建包体
create or replace package body manage_students
as
    procedure find_name
    (i_student_id in student.student_id%TYPE,
     o_first_name out student.first_name%TYPE,
     o_last_name out student.last_name%TYPE
    )
    is
        v_student_id student.student_id%TYPE;
    begin
      select first_name, last_name
        into o_first_name, o_last_name
        from student
        where student_id = i_student_id;
    exception
        when others
        then
            dbms_output.put_line('Error in finding student_id: ' ||
            i_student_id);

    end find_name;       
           
    function id_is_good
    (i_student_id in student.student_id%TYPE)
    return boolean
    as
        v_id_cnt number;
    begin
        select count(*)
          into v_id_cnt
          from student
         where student_id =  i_student_id;
        return 1 = v_id_cnt;
    exception
        when others
        then
            return false;
    end id_is_good;
end manage_students;
/

(3) 调用存储包

set serveroutput on
declare
    v_first_name student.first_name%TYPE;
    v_last_name student.last_name%TYPE;
begin
    if manage_students.id_is_good(&&v_id)
    then
        manage_students.find_name(&&v_id, v_first_name, v_last_name);
        dbms_output.put_line('Student No. ' ||
        &&v_id ||' is ' ||
        v_last_name || ' '  ||
        v_first_name);
       
    else
        dbms_output.put_line('Student ID: ' ||
        &&v_id ||' is not in the database');

    end if;           
end;
/
输入145,结果为:
Student No. 145 is Lefkowitz Paul
 
 
(4) 创建私有对象
首先创建包规范
create or replace package manage_students
as
    procedure find_name
    (i_student_id in student.student_id%TYPE,
     o_first_name out student.first_name%TYPE,
     o_last_name out student.last_name%TYPE
    );
   
    function id_is_good
    (i_student_id in student.student_id%TYPE)
    return boolean;
   
    procedure display_student_count;
   
end manage_students;
/
再创建包体
create or replace package body manage_students
as
    procedure find_name
    (i_student_id in student.student_id%TYPE,
     o_first_name out student.first_name%TYPE,
     o_last_name out student.last_name%TYPE
    )
    is
        v_student_id student.student_id%TYPE;
    begin
      select first_name, last_name
        into o_first_name, o_last_name
        from student
        where student_id = i_student_id;
    exception
        when others
        then
            dbms_output.put_line('Error in finding student_id: ' ||
            i_student_id);

    end find_name;       
           
    function id_is_good
    (i_student_id in student.student_id%TYPE)
    return boolean
    as
        v_id_cnt number;
    begin
        select count(*)
          into v_id_cnt
          from student
         where student_id =  i_student_id;
        return 1 = v_id_cnt;
    exception
        when others
        then
            return false;
    end id_is_good;
   
    function student_count_priv
    return number
    is
        v_count number;
    begin
        select count(*)
        into v_count from student;
        return v_count;
    exception
        when others then
            return(0);
    end student_count_priv;
   
    procedure display_student_count
    is
        v_count number;
    begin
        v_count := student_count_priv;
        dbms_output.put_line('There are ' || v_count || ' students.');
    end display_student_count;   
end manage_students;

注释:添加一个私有函数student_count_priv和一个调用私有过程的公开过程display_student_count.
student_count_priv没有在包规范中定义,为私有。
declare
    v_count number;
begin
    v_count := manage_students.student_count_priv;
    dbms_output.put_line(v_count);
end;
 
ORA-06550: 第 5 行, 第 32 列:
PLS-00302: 必须声明 'STUDENT_COUNT_PRIV' 组件
ORA-06550: 第 5 行, 第 5 列:
PL/SQL: Statement ignored

执行如下代码:
SQL> execute manage_students.display_student_count;
 
There are 268 students

注意:
如果在包规范中包含某过程或者函数,它就变成私有的。在另一方面,如果在包规范中声明了一个过程
或者函数,然后当创建包体时不定义它,就会报错