[转]数据库主键设计之思考

主键的必要性:

有些朋友可能不提倡数据库表必须要主键,但在我的思考中,觉得每个表都应该具有主键,不管是单主键还是双主键,主键的存在就代表着表结构的完整性,表的记录必须得有唯一区分的字段,主键主要是用于其他表的外键关联,本记录的修改与删除,当我们没有主键时,这些操作会变的非常麻烦。

主键的无意义性:

我强调主键不应该具有实际的意义,这可能对于一些朋友来说不太认同,比如订单表吧,会有”订单编号”字段,而这个字段呢在业务实际中本身就是应该具有唯一性,具有唯一标识记录的功能,但我是不推荐采用订单编号字段作为主键的,因为具有实际意义的字段,具有”意义更改”的可能性,比如订单编号在刚开始的时候我们一切顺利,后来客户说”订单可以作废,并重新生成订单,而且订单号要保持原订单号一致”,这样原来的主键就面临危险了。因此,具有唯一性的实际字段也代表可以作为主键。因此,我推荐是新设一个字段专门用为主键,此主键本身在业务逻辑上不体现,不具有实际意义。而这种主键在一定程序增加了复杂度,所以要视实际系统的规模大小而定,对于小项目,以后扩展不会很大的话,也查允许用实际唯一的字段作主键的。

主键的选择

我们现在在思考一下,应该采用什么来作表的主键比较合理,申明一下,主键的设计没有一个定论,各人有各人的方法,哪怕同一个,在不同的项目中,也会采用不同的主键设计原则。

第一:编号作主键

此方法就是采用实际业务中的唯一字段的”编号”作为主键设计,这在小型的项目中是推荐这样做的,因为这可以使项目比较简单化,但在使用中却可能带来一些麻烦,比如要进行”编号修改”时,可能要涉及到很多相关联的其他表,就象黎叔说的”后果很严重”;还有就是上面提到的”业务要求允许编号重复时 “,我们再那么先知,都无法知道业务将会修改成什么?

第二:自动编号主键

这种方法也是很多朋友在使用的,就是新建一个ID字段,自动增长,非常方便也满足主键的原则,优点是:数据库自动编号,速度快,而且是增量增长,聚集型主键按顺序存放,对于检索非常有利;数字型的,占用空间小,易排序,在程序中传递也方便;如果通过非系统增加记录(比如手动录入,或是用其他工具直接在表里插入新记录,或老系统数据导入)时,非常方便,不用担心主键重复问题。

缺点:其实缺点也就是来自其优点,就是因为自动增长,在手动要插入指定ID的记录时会显得麻烦,尤其是当系统与其他系统集成时,需要数据导入时,很难保证原系统的ID不发生主键冲突(前提是老系统也是数字型的);如果其他系统主键不是数字型那就麻烦更大了,会导致修改主键数据类型了,这也会导致其他相关表的修改,后果同样很严重;就算其他系统也是数字型的,在导入时,为了区分新老数据,可能想在老数据主键前统一加一个”o”(old)来表示这是老数据,那么自动增长的数字型又面临一个挑战。

第三:Max加一

由于自动编号存在那些问题,所以有些朋友就采用自己生成,同样是数字型的,只是把自动增长去掉了,采用在Insert时,读取Max值后加一,这种方法可以避免自动编号的问题,但也存在一个效率问题,如果记录非常大的话,那么Max()也会影响效率的;更严重的是并发性问题,如果同时有两人读到相同的Max后,加一后插入的 ID值会重复,这已经是有经验教训的了。

第四:自制加一

考虑Max加一的效率后,有人采用自制加一,也就是建一个特别的表,字段为:表名,当前序列值。这样在往表中插入值时,先从此表中找到相应表的最大值后加一,进行插入,有人可能发现,也可能会存在并发处理,这个并发处理,我们可以采用lock线程的方式来避免,在生成此值的时,先Lock,取到值以后,再unLock出来,这样不会有两人同时生成了。这比Max加一的速度要快多了。但同样存在一个问题:在与其他系统集成时,脱离了系统中的生成方法后,很麻烦保证自制表中的最大值与导入后的保持一致,而且数字型都存在上面讲到的”o”老数据的导入问题。因此在”自制加一”中可以把主键设为字符型的。字符型的自制加一我倒是蛮推荐的,应该字符型主键可以应付很多我们意想不到的情况。

第五:GUID主键

目前一个比较好的主键是采用GUID,当然我是推荐主键还是字符型的,但值由GUID生成,GUID是可以自动生成,也可以程序生成,而且键值不可能重复,可以解决系统集成问题,几个系统的GUID值导到一起时,也不会发生重复,就算有”o”老数据也可以区分,而且效率很高,在。NET里可以直接使用 System.Guid.NewGuid()进行生成,在SQL里也可以使用 NewID()生成。

优点是:

同 IDENTITY 列相比,uniqueidentifier 列可以通过 NewID() 函数提前得知新增加的行 ID,为应用程序的后续处理提供了很大方便。

便于数据库移植,其它数据库中并不一定具有 IDENTITY 列,而 Guid 列可以作为字符型列转换到其它数据库中,同时将应用程序中产生的 GUID 值存入数据库,它不会对原有数据带来影响。

便于数据库初始化,如果应用程序要加载一些初始数据, IDENTITY 列的处理方式就比较麻烦,而 uniqueidentifier 列则无需任何处理,直接用 T-SQL 加载即可。

便于对某些对象或常量进行永久标识,如类的 ClassID,对象的实例标识,UDDI 中的联系人、服务接口、tModel标识定义等。

缺点是:

GUID 值较长,不容易记忆和输入,而且这个值是随机、无顺序的。

GUID 的值有 16 个字节,与其它那些诸如 4 字节的整数相比要相对大一些。这意味着如果在数据库中使用 uniqueidentifier 键,可能会带来两方面的消极影响:存储空间增大;索引时间较慢。

我也不是推荐GUID最好,其实在不同的情况,我们都可以采用上面的某一种方式,思考了一些利与弊,也方便大家在进行设计时参考。这些也只是我的一点思考而已,而且可能我知识面限制,会有一些误论在里面,希望大

MTI,MMTT,MMT

Table Forms Name
MTI MTL_TRANSACTIONS_INTERFACE Transaciton Open Interface
MMTT MTL_MATERIAL_TRANSACTIONS_TEMP Pending Transactions Form
MMT MTL_MATERIAL_TRANSACTIONS Material Transactions Form

TP:INV Transaction processing mode


Profile Name Description
TP:INV Transaction processing mode On-line processing:界面停止响应,直到做完事务到MMT
Immediate concurrent processing:插入数据到接口表,并立即提
交一个处理请求,界面可以继续操作
Background processing:插入数据到接口表,界面可以继续操作,
需要用户手工提交处理请求
Form level processing:有各种事务处理对应的Profile分别控制,
比如”
TP:WIP Material Transaction Form“等

Standard Sales Order

应用:Order Management
职责:Order Management Super User

标准订单:标准销售订单:从系统角度看,指直接从本OU的库存挑库发货、不直接与采购和制造发生关联的销售订单。
SO的工作流: 任何时候,我们都可以通过SO的菜单Tools/Workflow Status来看订单头和行的工作流状态;一般通过工作流就能看出目前订单履行到哪个步骤,下一步该做什么。如果发现工作流停在某个Defer的节点,那么需要跑一下Workflow BackgroundProcess;这个请求一般在设置的时候是Schedule好了的

核心功能流程
Prerequisite      Item/Customer/Terms/Transaction Type/OM Parameter

Enter 输入,哪个客户买什么东西多少个,每个多少钱

Book 预定,检查订单完整性

Schedule 计划,计划订单发货时间

Reserve 保留,预留库存,免得现在看有到发货时没有了

Pick Release 挑库,决定从哪个(些)库存挑选物品

Pick Confirm 挑库确认,把物品从现在的库存搬到发货区Staging Area

Create Delivery 制作发运单,从哪里经过哪里送到哪里

Ship Confirm 发货确认,确认货物已经送达预定地点

Interface Trip Stop  行程停靠接口,产生库存交易

Close Order 关闭订单

推荐大家读一下《深入浅出oracle ebs之核心功能(distrbute)》这个文档,里边有比较相近的业务流程

Inventory Transactions

Transaction
• A transaction is an item movement into, within, or out of inventory.
• A transaction changes the quantity, location, or cost of an item.
• Oracle Inventory supports a number of predefined and user-defined transaction
types.
• Every material movement has a corresponding set of accounting transactions that
Oracle Inventory automatically generates.
• All transactions validate the various controls (revision, locator, lot number, and
serial number) you enable for your items.
• You can choose your own transaction processing methods to use available
computing resources most effectively.


Transaction Source Types
You can add user-defined transaction types to the list of predefined transaction types.


You Can Perform the Following Oracle Inventory Transactions
• Receive items into your organization from a general ledger account number
• Issue items from your organization to a general ledger account number
• Transfer items from a subinventory in your organization to another subinventory
in the same organization
• Transfer items directly between organizations
• Transfer items between organizations by way of intransit
• Reserve items for a specific account or temporarily prevent the release of items
onto the shop floor


Moving Inventory
Inventory organization transfers may be accomplished without using an internal
requisition/internal sales order.
• You can use miscellaneous receipts to receive material without a purchase order
number.
• Use miscellaneous issues to receive material without approvals or a document
number.
• Use subinventory transfers to transfer the location of material without a
document number.

You can use internal requisitions and internal sales orders as an approval mechanism
and to provide a document to track when you transport material between warehouses or
transfer material across inventory organizations.
Use miscellaneous receipts to receive material without a purchase order number.
• Use move orders with an Account Transfer transaction type to approve a
transaction and to provide a document to track the issue of the material.
Previously, sales orders with approvals in the order cycle were used to issue the
material and track the movement.
• Use move orders with a Subinventory Transfer transaction type to approve a
transfer and to provide a document to track the transfer of the material.

PL/SQL Versions

PL/SQL Versions

Version/Release

Characteristics

Version 1.0

First available in SQL*Plus as a batch-processing script. Oracle Version 6.0 was released at approximately the same time. PL/SQL was then implemented within SQL*Forms Version 3, the predecessor of Oracle Forms.

Release 1.1

Available only in the Oracle Developer/2000 Release 1 tools. This upgrade supports client-side packages and allows client-side programs to execute stored code transparently.

Version 2.0

Available with Release 7.0 (Oracle Server). Major upgrade to Version 1. Adds support for stored procedures, functions, packages, programmer-defined records, PL/SQL tables, and many package extensions, including DBMS_OUTPUT and DBMS_PIPE.

Release 2.1

Available with Release 7.1 of the Oracle Server Version. Supports programmer-defined subtypes, enables the use of stored functions inside SQL statements, and offers dynamic SQL with the DBMS_SQL package. With Version 2.1, you can now execute SQL DDL statements from within PL/SQL programs.

Release 2.2

Available with Release 7.2 of the Oracle Server Version. Implements a binary “wrapper” for PL/SQL programs to protect source code, supports cursor variables for embedded PL/SQL environments such as Pro*C, and makes available database-driven job scheduling with the DBMS_JOB package.

Release 2.3

Available with Release 7.3 of the Oracle Server Version. Enhances functionality of PL/SQL tables, offers improved remote dependency management, adds file I/O capabilities to PL/SQL, and completes the implementation of cursor variables.

Version 8.0

Available with Oracle8 Release 8.0. The drastic change in version number reflects Oracle’s effort to synchronize version numbers across related products. PL/SQL8 is the version of PL/SQL which supports the many enhancements of Oracle8, including large objects (LOBs), object-oriented design and development, collections (VARRAYs and nested tables), and Oracle/AQ (the Oracle/Advanced Queueing facility).

PL/SQL的两种应用模式

服务器端的P L / S Q L
从O r a c l e 6 . 0版开始,P L / S Q L就驻留在数据库服务器端,同时,该服务器也是P L / S Q L引擎的默认位置。由于数据库服务器可以处理S Q L语句,所以S Q L语句和P L / S Q L块都可以送到该服务器进行处理。一个客户应用,不管是用O r a c l e开发工具实现的或使用其他开发工具编制的,都可以向数据库服务器提交S Q L语句和P L / S Q L块。SQL *Plus就是一个这种客户应用的案例,该程序可以在S Q L提示符下接收交互输入的S Q L语句和P L / S Q L命令并将其送往服务器执行。例如,我们可以假设在SQL *Plus与服务器建立了连接的情况下输入下列的SQL,P L / S Q L命令:
SQL_PLSQL.sql
SQL> CREATE OR REPLACE PROCEDURE ServerProcedure AS
2 BEGIN
3 NULL;
4 END ServerProcedure;
5 /
Procedure created.
SQL> DECLARE
2 v_StudentRecord students%ROWTYPE;
3 v_Counter BINARY_INTEGER;
4 BEGIN
5 v_Counter := 7;
6
7 SELECT *
8 INTO v_StudentRecord
9 FROM students
10 WHERE id = 10001;
11
12 ServerProcedure;
13
14 END;
15 /
PL/SQL procedure successfully completed.
SQL> UPDATE classes
2 SET max_students = 70
3 WHERE department = ‘HIS’
4 AND course = 101;
1 row updated.

图1演示了在服务器端的P L / S Q L引擎对P L / S Q L块的处理过程。客户应用可以向服务器提
交P L / S Q L块(该块可以带有过程和包括调用服务器端存储过程的S Q L语句),以及单独的S Q L语句。如图所示, P L / S Q L块和S Q L语句通过网络送往服务器。一旦服务器收到了这些内容, S Q L语句将直接进入服务器内含的S Q L语句执行器,而P L / S Q L块则送往P L / S Q L引擎进行语法分析。在该块的运行期间, P L / S Q L引擎负责执行过程语句(如赋值语句和存储过程调用)。对于该块中出现的S Q L语句(如S E L E C T语句等),P L / S Q L引擎将它们送往S Q L语句执行器执行。

客户端的P L / S Q L
除了在服务器端的P L / S Q L引擎外,几种O r a c l e开发工具也带有P L / S Q L引擎。由于这些开发工具是运行在客户端的,所以它们的P L / S Q L引擎也可以运行在客户端。这样一来,借助于客户端的P L / S Q L支持,P L / S Q L块中的过程语句就可以在本地运行,而没有必要送到服务器端。例如开发工具Oracle Forms(该工具是Oracle Developer的一部分)自身带有P L / S Q L引擎;在O r a c l e D e v e l o p e r工具包中,如Oracle Reports也带有P L / S Q L引擎。需要指出的是这种引擎与P L / S Q L服务器端的引擎有所不同。P L / S Q L块只能出现在客户端应用中,并且该块必须用开发工具来编制。
假设一个Oracle Forms应用包括了触发器和过程,这些语句都在客户端执行。只有该程序中的S Q L语句和调用服务器端存储子程序的语句被送往服务器进行处理。如图2 所示,客户端的P L / S Q L引擎可以处理过程语句。

与服务器端的P L / S Q L一样,应用程序提交的单独的S Q L语句(如U P D AT E语句)直接通过网络送往服务器端的S Q L语句执行器。不同的是, P L / S Q L块是在客户端直接处理。任何过程语句(如赋值语句)的处理都不会引起网络传输。P L / S Q L块中的S Q L语句要提交给S Q L语句执行器,对服务器端的存储子程序的调用则是送到服务器端的P L / S Q L引擎执行。

再补一图:

Because PL/SQL is used both in the database (for stored procedures and database triggers) and in the application code (to implement logic within a form, for example), you can leverage the same programming language for both client-side and server-side development. You can even move programs from one component of the configuration to another.For example, you might decide that a function which was originally coded for a single screen could be shared by all of the modules in an application. To make this happen, you simply move that function from the client-side screen component to the server-side database environment. You have to change neither the PL/SQL code nor any of the programs that call that function.

Commit processing

Commit processing是指Oracle Forms把数据提交到数据库的流程。一般的流程是这样的:
1.从数据库中读取数据
2.允许终端用户增删改查,修改的东西这个时候仅仅还停留在Form层,数据库并没有发生改变
3.Post这些改变到DB,并且把数据传递给DB。在Post数据之后,Oracle Forms只能roll back这些改变(通过Clear Form)或者commit这些改变
4.Commit,数据库数据发生永久性改变
一般情况下,3,4步是一起执行的。 [Commit] key 或者调用COMMIT_FORM built-in,oracle Forms会先post,再Commit
Post是在commit之前单独的一步,但是终端用户是不可以触发post命令的,但是可以调用POST trigger
补充:替换标准的Commit流程,ON-INSERT, ON-UPDATE, and ON-DELETE triggers可以替换标准的Commit流程

Windows 和Canvas的关系

对于你创建的每一个window,你也必须相应的创建至少一个canvas(可以指定多个canvas);同样,每一个创建的canvas必须指向一个window;
那么item,canvas和window是的关系呢,每一个item(text item, check box, etc.)都有一个canvas属性,用于指定item在那个canvas上显示。(注意,当你创建一个item,oracle Forms会给这个属性设置一个默认值,这个值为你当前所在的canvas);同样每一个canvas都有一个window属性,用于展示canvas。
你明白了么?
[item] *—1 [canvas] *—1 [window]

Program Unit或者PLD中的PL/SQL是不能直接在数据库中运行的

Form 中Program Unit或者PLD中的PL/SQL拷贝到数据库上,是不能直接中运行的,实际上应该理解为类似SQL的高级语言(如Java,C ++等),这些Procedure是local的Procedure,他们可能会调用其他Form,PLD上变量,所以直接拷贝DBMS,是无法找到这些变量的

只有数据库级别Procedure(PLS文件)才能直接在DB上运行