Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1184584
  • 博文数量: 398
  • 博客积分: 10110
  • 博客等级: 上将
  • 技术积分: 4055
  • 用 户 组: 普通用户
  • 注册时间: 2007-12-23 20:01
个人简介

新博客http://www.cnblogs.com/zhjh256 欢迎访问

文章分类

全部博文(398)

文章存档

2012年(1)

2011年(41)

2010年(16)

2009年(98)

2008年(142)

2007年(100)

我的朋友

分类: Oracle

2008-01-06 21:19:07

       必须接受的限制:

       ·完整是不可能的;

       ·一致性是不可能的;

         通常保留字和关键字应该大写,但是自定义函数通常不会那么严格。

后缀名命名规范

       在文本文件中维护代码,而不要依赖于工具,这样可以进行版本控制,同时保护代码的不被破坏。

       如下:

       Packagepkg

       包体:pkb

       Procedureprc

       Functionfnc

       对象类型:typ

       对象类型体:tyb

       Triggertrg

       其他PL/SQLpls

       其他SQLsql

标识符命名规范

       5部分组成:primary identifier最为重要。

Scope    

       通常g代表全局;

       l代表本地;

       p代表进出参数;

Type

       两种典型的标量值为常量(k)或者变量(v)。但是如果再分为VARCHAR2DATE就过于复杂,不过通常使用g代表gv。还有一些集合类型包括:

       c代表游标;

       cp代表游标参数;

       r代表记录类型;

       除了自定义类型外,还包括用户自定义类型和子类型。

       --使用子类型的唯一意图是构建一个内部控制的自我描述的类型;

Primary Identifier

       应该能够清晰地描述变量的意图,通常为单词或者短语。通常避免缩写,除非真的能够自我描述,否则考虑其他的名称。

Suffix

       通常为IN, OUT, or IN OUTin通常省略。

标识符命名例子

游标声明

       游标名通常以c_开头;

循环索引

       通常不要使用FOR i IN 1..12 LOOPFOR r IN c_discrepancies LOOP形式的,而是使用FOR r_emp IN c_emp LOOPFOR i_month IN 1..12 LOOP

集合(PL/SQL)类型

       通常使用_TT作为后缀比较好,如TYPE account_tt IS TABLE OF accounts%ROWTYPE;

PL/SQL记录类型

       通常使用rectype作为后缀,如G_ACCOUNT_RECTYPE

       TYPE g_address_rectype IS RECORD (...); 

PL/SQL纪录实例

       定义纪录实例是不需要rectype,命名规范同正常的变量;

PL/SQL对象实例

       通常使用o_作为前缀

模式级别的对象类型

       通常使用_OT作为后缀;

模式级别的集合类型

       通常也以_TT作为后缀;

代码格式

缩进

       使用tab键作为PL/SQL块缩进的单元,不要使用空格;

       SQL中使用空格键,不要使用tab键;

对齐

       ·在过程/函数中,在每一行声明一个新的参数;

FUNCTION invoice_address

    ( p_account_id ACCOUNTS.ACCOUNT_ID%TYPE

    , p_date DATE )

    RETURN ADDRESS;

       ·不一定需要这种格式:

v_account_id           ACCOUNTS.ACCOUNT_ID%TYPE;

v_customer_id          CUSTOMERS.CUSTOMER_ID%TYPE;

使用大小写增加可读性

       通常变量应该是小写的,其方法和过程应该为大写,如v_timer.SHOW('Punctuation check completed.');而不要混合大小写,这只会降低可读性。

例子:

       应该使用以下格式:

v_firstname := 'Lynne';

       而非:

         v_firstname:='Lynne';

注释风格

       应该使注释容易输入和维护,代码应该越少越好,如下:

          /*********************************************************/

         / +-----------------------------------------------------+ /

        / /                                                     / /

       / /  Main section:                                      / /

      / /  Added for version 1.23.4 WR 01/02/03               / /

     / /  Delete all the customers and fire the employees.   / /

    / /                                                     / /

   / +-----------------------------------------------------+ /

  /*********************************************************/

应该使用一下代码代替:

-- Delete all the customers and fire the employees:

同时注视也要注意缩进。

 

SQL布局指南

       不要使用以下格式:

SELECT last_name, first_name
  FROM employees
 WHERE department_id = 15
   AND hire_date < SYSDATE;


UPDATE employees
   SET hire_date = SYSDATE
 WHERE hire_date IS NULL
   AND termination_date IS NULL;

 

而是使用以下格式:

SELECT last_name, first_name
FROM   employees
WHERE  department_id = 15
AND    hire_date < SYSDATE;


INSERT INTO employees
( emp_id
, emp_firstname
, emp_lastname )
VALUES
( emp_seq.NEXTVAL
, r_emp.firstname
, r_emp.lastname );

UPDATE  employees
SET    salary = salary * v_raise_factor
WHERE   department_id = v_department_id
AND     termination_date IS NULL;
使用左对齐可以很清楚地明白是否为相同的语句,以及语句之间同等级的条件;

在代码中留出空间

       ·对SELECT中的每个列使用单独的行;

       ·将FROM中的每个表放在单独的行上;

       ·WHERE中的每个条件一行;

SELECT last_name
     , c.name
     , MAX(sh.salary) best_salary_ever
FROM   employees e
     , companies c
     , salary_history sh
WHERE  e.company_id = c.company_id
AND    e.employee_id =sh.employee_id
AND    e.hire_date > ADD_MONTHS(SYSDATE, -60);

不要使用过多的空格

       ·代码之间空行不要超过一行;

       ·子程序之间空两行;

表和列的别名使用有意义的缩写

       不要使用以下形式的缩写:

SELECT cols
FROM   employees a
     , companies b
     , profiles c
     , sales d
WHERE  b.com_id = a.emp_com_id
AND    c.pro_com_id = b.com_id
AND    d.sal_com_id (+)= c.pro_com_id
而是使用以下形式:

SELECT cols
FROM   employees emp
     , companies com
     , profiles pro
     , sales sal
WHERE  com.com_id = emp.emp_com_id
AND    pro.pro_com_id = com.com_id
AND    sal.sal_com_id (+)= pro.pro_com_id

虽然Oracle不会以出现的顺序进行连接,但是为了更好的理解,通常从左到右进行编

写连接。

SELECT da.
     , da.object_type
     , da.owner
     , DECODE(aa.object_name, NULL, 'No', 'Yes' ) granted
     , s.synonym_name
FROM   all_objects aa
     , dba_objects da
     , all_synonyms s
WHERE  (   da.object_name LIKE UPPER('&object')
        OR (    da.object_name LIKE UPPER(SUBSTR('&obj',INSTR('&obj','.')+1))
            AND da.owner = UPPER(SUBSTR('&object',1,INSTR('&object','.')-1))
           )
       )
AND    aa.object_name (+)= da.object_name
AND    aa.object_type (+)= da.object_type
AND    aa.owner (+)= da.owner
AND    s.table_name (+)= da.object_name
AND    s.table_owner (+)= da.owner
ORDER BY  da.object_name, da.object_type, da.owner;

另一种风格如下:

SELECT DISTINCT

       s.sid

     , s.username

     , a.owner ||'.' || a.object object_name

     , p.username  locked_by

     , l.type

     , DECODE(l.request,

              0, 'None',

              1, 'Null (NULL)',

              2, 'Row-S (SS)',

              3, 'Row-X (SX)',

              4, 'Share (S)',

              5, 'S/Row-X (SSX)',

              6, 'Exclusive (X)',

                'Unknown(' || l.request || ')' ) AS mode_requested

     , DECODE  -- Alternative bullet-style (use only one style at a time!)

       ( l.lmode

       , 0, 'None'

       , 1, 'Null (NULL)'

       , 2, 'Row-S (SS)'

       , 3, 'Row-X (SX)'

       , 4, 'Share (S)'

       , 5, 'S/Row-X (SSX)'

       , 6, 'Exclusive (X)'

         , 'Unknown(' || l.lmode || ')' ) AS mode_held

     , CASE

       WHEN l.ctime > 86400 * 2 -- More than 2 days

            THEN FLOOR(l.ctime/86400) || ' days '

                 || TO_CHAR(TO_DATE(systdate),'HH24:MI:SS')

       WHEN l.ctime > 86400  -- More than 1 day

            THEN FLOOR(l.ctime/86400) || ' day '

                 || TO_CHAR(TO_DATE(sysdate),'HH24:MI:SS')

       ELSE

            TO_CHAR(TO_DATE(ROUND(l.ctime),'SSSSS'),'HH24:MI:SS')

       END AS TIME_HELD

FROM   v$lock l

     , v$process p

     , v$access a

     , v$session s

WHERE  a.sid (+)= l.sid

AND    s.sid (+)= l.sid

AND    p.addr (+)= s.paddr

AND    a.owner (+) NOT IN ('PUBLIC','SYS')

ORDER BY 1,2,3,4

PL/SQL编程标准

异常

命名异常

       使用RAISE exception_name时要注意,特别是用户定义异常,如RAISE invalid_account,但是没有方法提供解释的伴随信息。作为一条规则,通常应该避免抛出用户自定义异常,仅在WHEN中策是它们,比较如下:

       应该避免如下:

         RAISE errorpkg.fatal_error;

       而是使用如下:

RAISE_APPLICATION_ERROR

( errorpkg.k_fatal_error

, 'Credit check failed for account ' || r_acc.acc_id

, TRUE );

避免使用如下:

ERROR_PKG.LOG_ERROR('Invalid account ' || r_acc.acc_id);

RAISE errorpkg.fatal_error;

而是使用如下:

v_error_text := 'Invalid account ' || r_acc.acc_id;

ERRORPKG.LOG_ERROR(v_error_text);

RAISE_APPLICATION_ERROR(errorpkg.k_invalid_account, v_error_txt );

错误码

       需要注意的是,使用RAISE_APPLICATION_ERROR时,实际的错误码并不需要特别关心。通常应用程序中只需要几个特定的错误即可,可以在包级别声明几个常量,如:k_error_code CONSTANT PLS_INTEGER := -20042,然后在调用RAISE_APPLICATION_ERROR时使用k_error_code

越少越好

       通常使用过程简单的产生错误报告,如“Could not generate invoice for order 4231”。

 

尽可能使用%TYPE

当存储从表中提取的值时使用%TYPE

       如下:

PROCEDURE format_customer

    ( p_customer_id  customers.cst_id%TYPE )

IS

    v_first_name     customers.cst_first_name%TYPE;

    v_last_name      customers.cst_last_name%TYPE;

    v_address        customers.cst_address_l1%TYPE;

    v_city           customers.cst_city%TYPE;

    v_national_ins#  customers.cst_national_ins_number%TYPE;

BEGIN

    ...

END;

使用%TYPE标准化非数据库声明

       如下:

v_revenue NUMBER(20,2) DEFAULT 0;

v_total_revenue v_revenue%TYPE;     

此时,v_revenue就可以充当revenue数据的标准变量,随后当需要更改revenue时就可以仅更改v_revenue,需要注意,DEFAULT 0不会继承。

 

删除不使用的变量

       尽量在编写完成后立刻检查,否则会花费大量时间而且乏味。

 

避免变量循环使用

       一个变量只用于一个目的。

 

在自我描述的文档中使用子类型

       SUBTYPE room_number IS rooms.room_number%TYPE;

...

-- A declaration using the subtype:

v_open_room ROOM_NUMBER;

是一种特定类型的NUMBER

常量

       ·从代码中删除所有文本常量和数字,使用常量代替他们。可以确保系统的可维护性。

       ·确保该常量的值仅仅被设置一次。

       ·如果发现程序中某些变量值没有变化,需要首先检查逻辑是否正确;如果正确,则更改为常量;

       ·如果将一个变量改为常量,需要同时更改其名称。

       使用常量的原因是可以提高可读性和可维护性。

 

可以使用SQL解决的,不要使用PL/SQL

       应用尽量使用SQL代替PL/SQL循环,如下:

FOR i_year IN 1..20

LOOP

    INSERT INTO table1 -- Twenty INSERT statements passed to the SQL engine

    SELECT *

    FROM   table2

    WHERE  starting_year = i_year;

END LOOP;

      

INSERT INTO table1

SELECT *

FROM   v1table2

WHERE  starting_year BETWEEN 1 AND 20;

 

循环

       ·对于FORWHILE循环不要EXITRETURN

       ·仅在执行指定次数时使用FOR循环;

       ·仅在条件边界为FALSE时退出循环;

       ·Cursor FOR loop通常效率均比OPEN-FETCH-EXIT-CLOSE好,并且后者更复杂;作为一个规则,仅在以下情况下使用OPEN-FETCH-EXIT-CLOSE

         ·要与游标变量一起工作,编译器不能自动声明ref cursorscursor%ROWTYPE

         ·希望在循环后保留值。

       ·使用匿名游标循环,FOR r IN (SELECT cols FROM sometable) LOOP;但是不能重用,也不能使用cursor's %ROWTYPE

 

条件表达式

布局

       ·不要增加冗余的括号;

IF (x = 1)

IF ((x = 1) AND (y = 2))

使用以下形式:

IF x = 1 AND y = 2

       ·THEN关键字可以在IF的一行,也可以在下一行;

       ·恰当的布局IF中的条件:

IF TO_CHAR(SYSDATE,'D') > 1 AND MYPACKAGE.MYPROCEDURE(1,2,3,4) NOT BETWEEN 1 AND 99 THEN

IF TO_CHAR(SYSDATE,'D') > 1  

AND MYPACKAGE.MYPROCEDURE(1,2,3,4) NOT BETWEEN 1 AND 99

THEN

使用以下格式:

IF TO_CHAR(SYSDATE,'D') > 1

AND MYPACKAGE.MYPROCEDURE(1,2,3,4) NOT BETWEEN 1 AND 99

THEN

       ·如果混合orand,除非很简单,否则一般使用空格缩进:

         IF a = 1 AND (b = 2 OR c = 3 OR d = 4) THEN

         使用以下格式:

IF a = 1

AND (   b = 2

     OR c = 3

     OR d = 4 )

THEN

确保ELSIF中的条件是排斥的

IF sal BETWEEN 0 AND 10000

THEN

    ...

ELSIF sal BETWEEN 10000 AND 20000

THEN

    ...

ELSIF sal BETWEEN 20000 AND 30000

THEN

    ...

ELSE

    ...

END IF;

需要使用以下格式:

IF sal < 10000

THEN

    ...

ELSIF sal < 20000

THEN

    ...

ELSIF sal < 30000

THEN

    ...

ELSE

    ...

END IF;

使用逻辑元素提高系统可读性

IF total_sal BETWEEN 10000 AND 50000

AND emp_status(emp_rec.empno) = 'N'

AND MONTHS_BETWEEN(emp_rec.hiredate, SYSDATE) > 10

THEN

    GIVE_RAISE(emp_rec.empno);

END IF;

应该使用以下格式:

eligible_for_raise :=

    total_sal BETWEEN 10000 AND 50000

    AND emp_status(emp_rec.empno) = 'N'

    AND MONTHS_BETWEEN(emp_rec.hiredate, SYSDATE) > 10;

 

IF eligible_for_raise

THEN

    GIVE_RAISE(emp_rec.empno);

END IF;

 

避免在将值赋给逻辑变量时使用IF

IF hiredate < SYSDATE

THEN

    date_in_past := TRUE;

ELSE

    date_in_past := FALSE;

END IF;

使用如下格式:

date_in_past := hiredate < SYSDATE;

总结

       如下:

IF (shipdate < ADD_MONTHS (SYSDATE, +3) OR

    order_date >= ADD_MONTHS (SYSDATE, -2)) AND

    cust_priority_type ='HIGH' AND

    order_status = 'O'

THEN

    ship_order('EXPRESS');

 

ELSIF (order_date >= ADD_MONTHS (SYSDATE, -2) OR

    ADD_MONTHS (SYSDATE, 3) > shipdate) AND

    order_status = 'O'

THEN

    ship_order('STANDARD');

END IF;

应该使用如下格式:

IF (   v_shipdate < ADD_MONTHS(SYSDATE, +3)

    OR v_order_date >= ADD_MONTHS(SYSDATE, -2) )

AND v_cust_priority = 'HIGH'

AND v_order_status = 'O'

THEN

    SHIP_ORDER(k_ship_express);

 

ELSIF (   v_order_date >= ADD_MONTHS(SYSDATE, -2)

       OR ADD_MONTHS(SYSDATE, 3) > v_shipdate)

AND v_order_status = 'O'

THEN

    SHIP_ORDER(k_ship_standard);

END IF;

 

阅读(3405) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~