什么是外键?
外键就是用来关联表的节点,是为了保持表的参照完整性。一个表中的外键必须能够在另外一个关系表中能够匹配到.
以中的例子为例:
假如现在有一个订单表,需要确保订单order里面的商品在product里面存在,这里我们就需要在order增加一个外键这个外键就是product表里面的主键
create table product( product_no serial constraint product_pkey primary key , name text, others character varying(100));
上面这个例子创建了一个product表,主键是product_no 主键的名字是product_pkey
下面创建一个orders表,外键是product_no,参照product表中的product_no
create table orders( order_no serial primary key, product_no integer references product (product_no) , name text, others character varying(100));
当创建之后,如果你在order表里面插入一条数据,而所插入的product_no的在product表里面没没有就会出现报错
ERROR: insert or update on table "orders" violates foreign key constraint "orders_product_no_fkey"DETAIL: Key (product_no)=(1) is not present in table "product".
回到这里
create table orders( order_no serial primary key, product_no integer , name integer references product, others character varying(100));
当我们执行上面一句的时候也是可以通过的,即使外键约束的字段和被引用表的字段不同,只要与参照表主键的数据类型一样就可以了。但是这里并不是很推荐,不方便以后的维护。
在上面这个例子里面提到了一种不同的写法:
create table orders( order_no serial primary key, product_no integer references product, name text , others character varying(100));
上面这种写法,当缺少字段列表的时候,系统会自动引用被引用表的主键作为order表的外键
但是它不能像创建主键一样写成下面这种方式:
create table orders( order_no serial primary key, product_no integer foreign key, name integer, others character varying(100));
这样会报错
外键也能够像检查约束一样,把一个字段组创建为一个外键,例如:
create table t1( a integer primary key, b integer, c integer, FOREIGN KEY (b, c) REFERENCES t2(b,c));
不过执行上面这条SQL之前你需要先执行下面这条,不然没有引用表,说t2不存在:
create table t2( a integer primary key, b integer, c integer, unique (b,c));
执行完上面这条语句之后就会创建一个组外键。说到这儿可以发现外键不一定是被引用表中的主键,只要外键的字段在被引用表的字段拥有唯一约束就可以。
一个表里面可以创建多个外键,这种实现的原因是为了实现多对多的一种关系,假如你的订单表order有多个商品:
-----------product tablecreate table product( product_no serial primary key, name text);-----------orders tablecreate table orders( order_no serial primary key, shipping text);------------relationship tablecreate table order_item( order_no integer references orders, product_no integer references product, detail text, primary key (product_no,order_no));
最后的关系表order_item:外键和主键是重叠的。在标准里面是允许的。
在生产环境中经常有这样的情况:当创建了某个上面的订单,而在未来的某一刻,商品表中的某个商品被删除了呢?
上面那种情况是不能删除的,你只有把order_item表里面在product有关系的数据删除才能够删除product表中的数据,但是想删除:
-----------product tablecreate table product( product_no serial primary key, name text);-----------orders tablecreate table orders( order_no serial primary key, shipping text);------------relationship tablecreate table order_item( order_no integer references orders on delete restrict, product_no integer references product on delete cascade, detail text, primary key (product_no,order_no));
上面的这段语句,restrict与cascade分别是限制与级联:
限制的意思是当在关联表里面存在被引用表的值,则不允许删除;
级联的意思是当关联表里面存在被引用表的值,假如删除被引用表的值,那么关联表中的数据也会随之删除
系统默认是restrict
除了上面两个常用的选项之外,为了业务逻辑的考虑有的为了保留数据。还有set null与set default。set default必须保证默认值能够拥有关联性,能够起到外键的效果。不然就会抛出错误,上面的例子中就不可以设置为default,但是你可以设置为null,但是设置为null也会出现问题,就是被引用表的字段数据也是NULL,如果这样就会造成数据混乱的问题。在这里建议在设置关联字段的时候,把它设置为not null。
与ON delete与之对应的还有ON UPDATE。数据更新也是一样的。
对于删除与更新操作,都会扫描关联表的整个表。例如上面的那个例子,当删除product表中的某一条数据的时候,它会扫描整个关联表order_item。所以在这里你需要创建一个索引。以便能够快速扫描。