ZenCart系列文章导航

函数学习

  • 原创 ZenCart函数-常用的函数
  • 原创 ZenCart函数-functions_categories.php
  • 原创 ZenCart函数-functions_general.php
  • 原创 ZenCart函数-functions_lookups.php

类库学习

  • 原创 ZenCart类库-order_total.php解读原理
  • 原创 ZenCart类库-payment.php解读实现原理
  • 原创 ZenCart类库-shipping.php解读实现原理

数据库学习

原创 ZenCart-数据库处理思路

ZenCart函数-functions_lookups.php

函数:zen_get_info_page

function zen_get_info_page($zf_product_id)
函数说明:根据产品ID 获得产品类型 在计算出产品的页面地址
备注说明:可缓存优化

由于产品具有很多的产品类型 根据产品ID  type_handler
先从产品表查询出产品的类型 然后在根据产品的类型 获取产品配置的type_handler

look up the product type from product_id and return an info page name (for template/page handling)
为模板和页面处理

先从products表查询出products_type
然后从TABLE_PRODUCT_TYPES 查询出 type_handler
最后返回$zp_handler->fields[‘type_handler’] . ‘_info’;

可能返回值
product_info
product_music_info
document_general_info
document_product_info
product_free_shipping_info

分类相关函数

zen_get_categories_name

根据分类ID—-获得分类名字

zen_get_category_name

根据分类ID和语言ID—-获取分类的名字信息

zen_get_category_description

根据分类ID和语言ID—-获取分类的描述信息

【注意区分上面两个函数一个是“categories”,一个是“category”】

zen_get_categories_image

根据分类ID—-获得分类的图片名字–数据库categories.categories_image 字段

zen_get_categories_name_from_product

根据产品ID—-获得分类名字【主要是获取主分类的名字】

实现过程:先从TABLE_PRODUCTS表查找出master_categories_id,然后在从TABLE_CATEGORIES_DESCRIPTION表查找categories_name分类的名字

zen_categories_lookup

【终极函数】根据分类ID,字段名称,语言ID

从TABLE_CATEGORIES和TABLE_CATEGORIES_DESCRIPTION查询分类的任意一个字段

比如 zen_categories_lookup(’10’, ‘parent_id’);

以上学习的是分类相关的6个函数,都是非常基础的函数

产品相关的基础函数

zen_products_id_valid($valid_id)

根据产品ID—-检查产品是否存在,返回bool类型

zen_get_products_virtual($lookup)

检查产品是否是一个虚拟产品,TABLE_PRODUCTS表的products_virtual字段,如果products_virtual等于1表示产品是一个虚拟产品,返回bool类型

【此处我设置成引用格式】表示是附属关系

zen_get_products_allow_add_to_cart($lookup) 

检查产品是否被允许添加到购物车,是否被允许添加到购物车是通过产品类型控制的,也就是表的TABLE_PRODUCT_TYPES的allow_add_to_cart字段,如果allow_add_to_cart==‘Y’表示产品类型可以被添加到购物车

查找过程:通过产品ID查找products_type,然后通过products_type 查找allow_add_to_cart

zen_get_product_is_always_free_shipping($lookup)

检查一个产品是否总是免邮费TABLE_PRODUCTS表的product_is_always_free_shipping,如果product_is_always_free_shipping==1 表示免邮费,返回bool类型

zen_get_products_category_id($products_id)

根据产品ID—-获得产品的主master_categories_id,在TABLE_PRODUCTS表,返回一个int类型master_categories_id

此函数和zen_get_categories_name_from_product 类似一个返回 master_categories_id 一个返回categories_name

zen_get_products_description($product_id, $language = ”) 

根据产品ID和语言ID,从TABLE_PRODUCTS_DESCRIPTION表中获取products_description描述信息

zen_get_products_name($product_id, $language = ”)

根据产品ID和语言ID,从TABLE_PRODUCTS_DESCRIPTION表中获取products_name名字信息

【上面两个函数是姐妹函数】

【再来看一个兄弟函数】

zen_get_products_image($product_id, $width = SMALL_IMAGE_WIDTH, $height = SMALL_IMAGE_HEIGHT)

根据产品ID,并且指定宽度高度,获取产品的图片信息,从TABLE_PRODUCTS表检索products_image,最终返回一个img的标签。此函数调用了zen_get_products_name获得产品名字,作为图片的alt说明

zen_get_products_manufacturers_id($product_id)

根据产品ID—-获得产品的制造商ID

在产品表TABLE_PRODUCTS中有一个manufacturers_id

zen_get_products_manufacturers_image($product_id)

根据产品ID—-获得产品的制造商图片,在TABLE_PRODUCTS表链接TABLE_MANUFACTURERS表,返回manufacturers_image字段

zen_get_products_manufacturers_name($product_id)

根据产品ID—-获得产品的制造商名字,在TABLE_PRODUCTS表链接TABLE_MANUFACTURERS表,返回manufacturers_name字段

【以上三个函数是一家子】

zen_get_manufacturers($manufacturers_array = ”, $have_products = false)

此函数是用来列举制造商,返回字段manufacturers_id和manufacturers_name,这个函数有两个点说明

1–参数manufacturers_array,如果是一个数组,并且数组中已经存在一些值,那么调用之后会将数据库中查询出来的值,和已经存在的值合并返回.

2.$have_products表示是否和TABLE_PRODUCTS联合起来查询,这样筛选出的制造商保证都是有产品的。

现在一共学习产品的8个函数了

继续开始

zen_products_lookup($product_id, $what_field = ‘products_name’, $language = ”)

联合TABLE_PRODUCTS和TABLE_PRODUCTS_DESCRIPTION两张表检索两张表的任意字段,此函数和zen_categories_lookup函数是姐妹函数

我们的产品总是有库存的,让我们看看库存相关的函数

zen_get_products_stock($products_id)

此函数会触发一个通知,暂时是什么我还不清楚。函数内部查找TABLE_PRODUCTS表的products_quantity字段,最终返回一个int类型的数值。函数内部调用了一个zen_get_prid,作用是从购物车id中解析产品ID,比如下面

下面格式的链接

http://localhost/ZenCart156c/index.php?main_page=product_info&products_id=8:c9f0f895fb98ab9159f51fd0297e236d

产品ID 是products_id=8:c9f0f895fb98ab9159f51fd0297e236d,zen_get_prid函数就是做了一个内部解析。

zen_check_stock($products_id, $products_quantity)

检查我们的库存,是否到达了一个值,根据产品ID查询我们的数据库,获得实际数量,然后减去$products_quantity得到$stock_left,判断$stock_left是否小于0,如果小于0,表示库存剩余量,返回span标签 附加说明消息。

关于新进产品和即将上架产品定义的时间范围

zen_get_new_date_range($time_limit = false)

zen_get_products_new_timelimit($time_limit = false)

获取最新产品的定义 得到时间条件

zen_get_upcoming_date_range()

关于区域zone函数

关于属性选项函数

ZenCart函数-functions_categories.php

共计有21个函数 已经列出了12个

zen_has_category_subcategories($category_id)

根据分类ID—-检查该分类是否有子分类,实现过程是统计 parent_id=$category_id的数量,如果数量大于1,表示有子分类,返回bool类型

zen_get_categories_parent_name($categories_id)

根据分类ID—-获取父分类的名字 categories_name

zen_get_parent_categories($categories, 37)

根据分类的ID—-向上查找父分类的ID,比如子分类:37 cPath=33_35_37,那么$categories最终是一个数组,[35,37],如果没有父分类就返回true,表示该分类是一个顶级分类。自身迭代函数,一直向上查找,直到parent_id为0为止,里面有个非常重要的判断 如果查找出来的当前分类的ID 不等于当前分类的ID 继续调用自身 直到查询到顶级分类。

zen_get_subcategories(&$subcategories_array, $parent_id = 0)

同理获取子分类的ID,并不是仅仅获取一级子分类,包括子分类的子分类,都一次性获取

比如:zen_get_subcategories($categories_sub,33);得到的结果是下面 有11个子分类

zen_get_product_path($products_id)

根据产品ID—-获得产品的cPath,比如产品ID:90,调用函数得到,获取48_45

实现过程:先根据产品的ID,查找出master_categories_id—》继续查找master_categories_id所有的父分类,得到数组后反转数组,最后拼接上当前分类的ID 返回48_45。

zen_get_path($current_category_id = ”)【还要继续研究】

zen_get_path函数中有个非常关键的 $cPath_array 暂时还不知道它的用法

zen_count_products_in_category($category_id, $include_inactive = false)

统计分类下的产品,产品ID,$include_inactive表示是否包含隐藏产品,如果产品隐藏了,我们统计的时候是否选择呢?

此函数默认会统计该分类下的所有子分类的产品,会累加在一起

zen_get_generated_category_path_rev(39)

用来生成当前分类的路径,包含当前分类“33_35_39”,函数实现过程是:根据参数查找到所有的父分类,然后反转函数,用连字符连接起来,最后拼接上当前分类ID,最后返回

zen_parse_category_path(’33_35_39′)

解析cPath变量并去掉重复的分类ID 比如 33_35_39,返回一个int类型数组。

以上学习了9个函数了

zen_get_product_types_to_category($lookup)

根据分类的ID,查找分类类型的ID,在TABLE_PRODUCT_TYPES_TO_CATEGORY表的product_type_id字段,一个产品分类可以有多个产品类别。

zen_get_categories_products_list(15)

zen_get_categories_products_list($categories_id, $include_deactivated = false, $include_child = true, $parent_category = ‘0’, $display_limit = ”)

获得一个分类下面所有的产品的ID【包括它的子分类】 得到一个数组 数组结构是 产品–分类ID

$include_deactivated:表示是否包含隐藏的产品

$include_child:是否包含子分类

$parent_category

$display_limit:表示限制条件

zen_product_in_category(1, 4)

检查分类ID和产品ID是否有对应的关系,在表TABLE_PRODUCTS_TO_CATEGORIES中,根据$product_id–检索categories_id,如果检索出来的categories_id和我们传入的分类ID相同,表示存在对应关系。如果不存在就继续检查父分类ID。

zen_product_in_parent_category($product_id, $cat_id, $parent_cat_id)
此函数就是继续检查父分类ID函数

ZenCart函数-functions_general.php

关于获取参数的两个函数

zen_get_all_get_params($exclude_array = array())

返回所有的http get变量 除了哪些 什么之外
$exclude_array:定义需要排除的参数

zen_post_all_get_params($exclude_array = array(), $hidden = true)

关于域名

解析一个URL
实现如下
先判断URL ://是否包含,然后解析URL中的host 值
可能是如下值
localhost
www.abc.com
abc.com
zen.abc.com
以小数点分割这个函数
abc.com

系统常量 定义
SESSION_USE_FQDN

关于产品ID解析

zen_get_prid($uprid)

从购物车的ID中解析出商品ID。

var_dump(zen_get_prid('11:abcdef12345'));echo '<hr />';//int(11) 

zen_get_uprid($prid, $params)

关于基础函数

zen_string_to_int($string)

字符串转int类型

zen_not_null($value)

检查值不为空,null,“NULL”,”” ,空数组,查询结果空。主要用来检查这五个值

1.如果是数组,要求数组元素大于0
2.如果是is_a($value, ‘queryFactoryResult’) 要求查询结果大于0【$value->result】
3.最后不为 空 不为NULL  并且字符长度大于0
true 表示 值是一个有效值

【如果值不为null,则返回true】

zen_rand($min = null, $max = null)

var_dump(zen_rand());echo '<hr />';
        var_dump(zen_rand(1,9999));echo '<hr />';

返回一个随机值,可以指定范围,也可以不指定

zen_get_ip_address()

获得IP地址

zen_round($value, $precision)

round()的包装函数。四舍五入返回一个指定位数的小于,但是不包括“.00”这样的格式

var_dump(zen_round(3.14159364,3));echo '<hr />';//double(3.142)
        var_dump(zen_round(3.14159364,2));echo '<hr />';//double(3.14)
        var_dump(zen_round(100.00,2));echo '<hr />';//double(100)

zen_exit()

Stop from parsing any further PHP code 停止解析任何进一步的PHP代码

zen_is_leap_year($year)

检查年份是否为闰年,如果是 返回true

zen_clean_html($clean_it, $extraTags = ”)

remove common HTML from text for display as paragraph

从文本中删除公共HTML以显示为段落

关于zen_db
zen_db和数据库查询,参数组装相关的函数

zen_db_input($string)

别名到$db->prepareInput(),用于清理数据库插入

zen_db_prepare_input($string)

zen_db_perform($table, $data, $action = ‘insert’, $parameters = ”)

zen_db_output($string)

关于基本业务

zen_get_country_zones($country_id)

根据国家ID获得国家区域zones,返回一个数组$zones_array[] = array(‘id’ =>$zone[‘zone_id’], ‘text’ => $zone[‘zone_name’]); 

ZenCart函数-常用的函数

zen_get_module_directory

函数说明:获得模块的目录。实现了includes/modules/*.php模块的重写。

此函数只是返回模块的名字

        var_dump(zen_get_module_directory('downloads'));echo '<hr />';//"CUSTOM/downloads.php"
        var_dump(zen_get_module_directory('downloads',true));echo '<hr />';//"CUSTOM/"

调用示例

<?php require(DIR_WS_MODULES . zen_get_module_directory('column_left.php')); ?>

此函数的一个兄弟函数是

zen_get_file_directory($check_directory, $check_file, $dir_only = ‘false’)

这个函数的功能应该更加的强大

$lang_file = zen_get_file_directory(DIR_FS_CATALOG . DIR_WS_LANGUAGES . $_SESSION['language'] . '/modules/order_total/', $value, 'false');

$lang_file = zen_get_file_directory(DIR_WS_LANGUAGES . $_SESSION['language'] . '/modules/order_total/', $value, 'false');

var_dump(zen_get_file_directory(DIR_WS_MODULES, 'attributes.php', $dir_only = 'false'));echo '<hr />';//"includes/modules/attributes.php"

从调用上看 函数可以检查相对目录,绝对目录

zen_href_link

函数说明:构造一个链接。function zen_href_link($page = ”, $parameters = ”, $connection = ‘NONSSL’, $add_session_id = true, $search_engine_safe = true, $static = false, $use_dir_ws_catalog = true)

25个sideboxes中特殊的search_header.php

所在目录 E:\wamp64\www\ZenCart156cDev\includes\modules\sideboxes

search_header.php 并不是一个真正的 sideboxes,它提供了一种思路,如何解决全局调用sideboxes的问题?

search_header.php调用示例如下

<?php require(DIR_WS_MODULES . 'sideboxes/search_header.php'); ?>

其余的sideboxes都是被模块column_left.php调用的

这样做的好处和坏处是?没有column_left.php提供的重写机制了

常用链接

<?php echo zen_href_link(FILENAME_LOGOFF, '', 'SSL'); ?>"><?php echo zen_href_link(FILENAME_ACCOUNT, '', 'SSL'); ?>">
<?php echo zen_href_link(FILENAME_LOGIN, '', 'SSL'); ?>

<?php echo zen_href_link(FILENAME_SHOPPING_CART, '', 'NONSSL'); ?>">

<?php echo zen_href_link(FILENAME_CHECKOUT_SHIPPING, '', 'SSL'); ?>">

表单函数
zen_draw_form
函数说明:构造一个表单。function zen_draw_form($name, $action, $method = ‘post’, $parameters = ”)

PHP新函数学习
basename
函数说明:返回路径中的文件名部分

需求和疑问
语言文件的载入
主语言文件的载入
让远程图片和本机图片同时兼容

ZenCart类库-payment.php解读实现原理

本篇解读 includes/classes/payment.php

关于具体实现模块的翻译

__construct

Constructor–构造器

函数–update_status()


Calculate zone matches and flag settings to determine whether this module should display to customers or not

计算区域匹配并标记设置以确定此模块是否应显示给客户

函数–javascript_validation()


JS validation which does error-checking of data-entry if this module is selected for use (Number, Owner Lengths)

JS验证,如果选择使用此模块,则将对数据输入进行错误检查(数字,所有者长度)

函数–selection()


Display Credit Card Information Submission Fields on the Checkout Payment Page

在结帐付款页面Checkout Payment Page 上显示信用卡信息提交字段

函数–pre_confirmation_check()


Evaluates the Credit Card Type for acceptance and the validity of the Credit Card Number & Expiration Date

评估可接受的信用卡类型 以及信用卡号 和有效期的有效性 

函数–confirmation()


Display Credit Card Information on the Checkout Confirmation Page

在结帐确认页面上显示信用卡信息

此函数和提交逻辑 好像没有关系

函数–process_button()


Build the data and actions to process when the “Submit” button is pressed on the order-confirmation screen.
This sends the data to the payment gateway for processing.
(These are hidden fields on the checkout confirmation page)

建立在订单确认屏幕上按下“提交”按钮时要处理的数据和操作。
这会将数据发送到支付网关进行处理。
(这些是结帐确认页面上的隐藏字段)

函数before_process()


Store the CC info to the order and process any results that come back from the payment gateway

将抄送信息存储到订单中,并处理从付款网关返回的所有结果

after_process()

Post-processing activities

Post-process activities. Updates the order-status history data with the auth code from the transaction.

Add receipt and transaction id to order-status-history (order comments)

后处理活动

后处理活动。 使用交易中的验证码更新订单状态历史记录数据。

check()

Check to see whether module is installed

检查是否安装了模块

install()

Install the payment module and its configuration settings

安装支付模块及其配置设置

remove() 

删除模块及其所有设置

keys()

Internal list of configuration keys used for configuration of the module

用于配置模块的配置密钥的内部列表

ZenCart类库-order_total.php解读原理

解读order_total.php文件

File contains the order-totals-processing class (“order-total”)
文件包含订单总数处理类(“订单总数”)

order-total class
Handles all order-total processing functions
处理所有订单总计处理功能

output($return_html=false)
函数处理输出最终的结果

credit_selection()

ZenCart类库-shipping.php解读实现原理

本篇文章解读 includes/classes/shipping.php shipping类的实现
关于 MODULE_SHIPPING_INSTALLED
表示系统中已经安装的模块,在后台配置中描述如下

Installed Modules
List of shipping module filenames separated by a semi-colon. This is automatically updated. No need to edit. (Example: ups.php;flat.php;item.php)

请进行必要的更改

已安装的模块
装运模块文件名列表,以分号分隔。
这会自动更新。 无需编辑。 (例如:ups.php; flat.php; item.php)
————————————————

关于构造函数的作用

1.加载你安装的模块

加载原理就是上面 MODULE_SHIPPING_INSTALLED,这个数据库配置,此配置在隐藏配置组id=6里面,后台其实没有显示,但是可以通过http://localhost/ZenCart156cDev/sprAy-Dsk-birtH/configuration.php?gID=6 访问这个配置组

另外此字段的配置是在 includes/init_includes/init_db_config_read.php 初始化时加载到系统中的。

分割此字段的值,得到数组

循环数组--得到类名和文件的名字

然后在DIR_WS_MODULES . 'shipping/'目录下加载

先加载模块对应的语言文件,然后加载模块本身

最后注册到 $GLOBALS变量中去,类名字作为索引,类实例作为值
————————————————

2.加载模块对应的语言

3.根据模块属性,确定模块是否开启or关闭

检查的原理是使用 check_enabled 函数,该函数的参数类型--使用类实例作为 输入参数。其内部使用method_exists方法检查类实例对象是否有指定的方法,然后调用类实例的方法 check_enabled_for_zone 或者check_enabled。

至此shipping其实已经实现了,类似的.net反射的特性,也是实现插件机制的原理。

通过shipping可以调用某一个具体实现类,比如flat类中的函数,实现了易于扩展的特性。

按照以下的优先次序返回插件是否启用

第一:默认$enabled = $class->enabled;默认的,其实就是flat类构造函数执行时初始化的属性,其内部调用了zen_get_shipping_enabled函数,并对区域做了检查

第二:$enabled = $class->check_enabled_for_zone();

第三:$enabled = $class->check_enabled();
————————————————

4.实例化模块,构造实例对象,并注册到全局变量【这是最终的目的】

$modules

构造函数中定义了此属性

这个属性只是单纯的安装模块“MODULE_SHIPPING_INSTALLED”分割之后的结果,除此之外再无其他

MODULE_SHIPPING_INSTALLED

在初始化过程中,只是读取了配置组2和3的配置,MODULE_SHIPPING_INSTALLED在配置组6里面,那么它是如何读取的呢?

此问题已经解答了,从上面看

关于flat类–模块的属性

code--模块的标识代码,通常就是类名字,和类文件同名,同时在$GLOBALS变量中引用它

title--模块标题,在语言文件中定义

description--模块本身描述,在语言文件中定义

sort_order--排序,排序字段是否定义决定了模块是否启用,也是非常重要的

icon---图标

tax_class--使用的税种

tax_basis--税种基于什么计算 一下是说明

On what basis is Shipping Tax calculated. Options are
Shipping - Based on customers Shipping Address
Billing Based on customers Billing address
Store - Based on Store address if Billing/Shipping Zone equals Store zone

enabled--该模块是否被启用

_check--属性用来读取插件的MODULE_SHIPPING_FLAT_STATUS 状态值
————————————————

关于zen_get_shipping_enabled的作用

disable only when entire cart is free shipping
仅当整个购物车免费送货时禁用

此函数内部注释先列出来

for admin always true if installed
对于管理员,如果已安装,则始终为true


Free Shipping when 0 weight - enable freeshipper - ORDER_WEIGHT_ZERO_STATUS must be on
0重量时免费送货-启用freeshipper-订单重量必须为0状态


Free Shipping when 0 weight - disable everyone - ORDER_WEIGHT_ZERO_STATUS must be on
0重量时免费送货-禁用所有人-订单重量必须为0


Always free shipping only true - enable freeshipper
始终免费送货-启用freeshipper


Always free shipping only true - disable everyone
始终免费送货-禁用所有人


Always free shipping only is false - disable freeshipper
总是免费送货是错误的-禁用免费送货
————————————————

同时牵连出一下函数

$_SESSION[‘cart’]->in_cart_check(‘product_is_always_free_shipping’,’1′);

检查指定字段的产品数

$_SESSION[‘cart’]->count_contents();

统计购物item总数

$_SESSION[‘cart’]->show_weight();

统计购物车总重量
————————————————

关于quote方法

返回对方法的引用,此方法可以返回所有模块的引用,也可以单独返回某一个模块的引用

内部实现机制是:

首先引用$shipping_weight, $uninsurable_value;$uninsurable_value表示的是运费险,看某一具体的类是否有get_uninsurable_value方法

定义$quotes_array,这是我们最终要返回的数组对象,这个数组对象是只包含已经启用的模块

通过两个循环,从$this->modules属性中分析你要的模块

第一个foreach循环实现了从$GLOBALS检查,判断模块的启用状态,只将满足启用状态的模块存入到$quotes_array

第二个循环,更新状态;调用某一具体shipping的quote方法,重新赋值计算重量的全局变量
————————————————

接下来一个问题是:flat的运费是如何被计算出来的呢?
$shipping_weight是在何时定义的?
会话$_SESSION[‘shipping’]是在什么地方被写入的?
shipping模块在实例化的时候,就已经确定并检查好哪些模块是可以使用的

关于设想的需求环境
所有订单免邮费

所有订单固定一个邮费

所有订单固定一个邮费,但是对某一个区域单独启用

订单满多少金额,就免邮费

订单金额设置区间范围,在不用的范围内,运费不一样

订单安装数量免邮费

关于flat类
这是一个提供统一费率的运输模块,所有的订单运费都是一样的,但是还可以设置在某个地区开启

关于flat类quote方法

quote中文意思是引用,此方法就是返回对此类的相关信息的引用

存储在quotes数组内,结构如下

id--模块的唯一标识符,全局内不要重复

module--模块的标题,被定义在语言包内

methods--这是一个数组,其中包含了关于运费的计算

tax--税率

icon--运费图标

结合分析 flat.php模块和freeshipper.php模块,两者编写方式高度相同

flat.php—-表示所有的订单采用统一的运费,税率

freeshipper.php—-表示免邮费

flat配置模块的区域是在什么地方设置的?暂时未知

zen_cfg_pull_down_zone_classes

ZenCart-数据库处理思路

处理数据库的思路是这样
1.原始数据库是我不做任何的更改
2.我对数据库的修改,使用单独的脚本,即使修改也是在单独的脚本中运行
3.我对原始脚本的操作大概有一下几种

  • 做新站的时候需要清空数据库的数据
  • 增加新表,比如products_images
  • 修改原始表的某些字段,比如字段长度等等
  • 插入某些配置,只是插入
  • 更新某些配置,比如隐藏模块,主要更加具体的生成环境来说
  • 数据新增,比如:货币数据,语言数据,国家数据等等,包括但是不限于

以上是我总结的6种操作 

SQL Server-数据库三范式和数据建模

数据库三范式

第一范式:
任何给定行的列必须是只包含一个值;
表中的每一行必须有相同数量的列;
表中的每一行必须是唯一的即是不相同的;
第二范式:
必须满足第一范式;
表中的所有非主键必须依赖整个主键;
第三范式:
必须满足第二范式;
表中的所有非主键必须相互独立;

数据建模

1 工具:Sysbase PowerDesigner ,  Borland Together , Rose, MS Visio
2 概念模型:概念数据模型也称信息模型,它以实体-联系(Entity-RelationShip,简称E-R)理论为基础,并对这一理论进行了扩充。它从用户的观点出发对信息进行建模
3 物理模型:数据库物理结构模型
4 正向工程:从物理模型产生一个数据库, 或产生数据库脚本,这是正向工程
5 逆向工程:从数据库映射为物理模型