本文主要是介绍<POSTGRESQL修炼之道:从小工到专家>之逻辑结构管理(1-5节),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 一、数据库逻辑结构介绍
- 二、数据库基本操作
- 三、模式schema
- 1.模式的定义
- 2.模式的使用
- 3.公共模式
- 4.模式的搜索路径
- 5模式的权限
- 6.模式的移植性
- 四、表
- 1.创建表
- 2.表的存储属性
- 3.临时表
- 4.默认值
- 5.约束
- 6.修改表
- 7.表继承及分区表
- 8.分区表
- 五、触发器
- 1.创建触发器
- 2.语句级触发器与行级触发器
- 3.BEFORE触发器与AFTER触发器
- 4.删除触发器
- 5.触发器的行为
- 6.触发器函数中的特殊变量
- 六、事件触发器
- 七、表空间
- 1.表空间的定义
- 2.表空间的使用
- 八、视图
- 1.视图的定义
- 2.创建视图
- 3.可更新视图
一、数据库逻辑结构介绍
PG中, 数据的组织结构可以分为三层:
- 数据库
一个PG数据库服务下可以管理多个数据库,当应用连接到一个数据库时,只能访问这个数据库中的数据 - 表、索引
一个数据库中有很多表、索引。PG中称之为Relation,其他数据库叫Table - 数据行
每张表中有很多行数据,PG中称之为Tuple,其他数据库叫Row
PG中,一个数据库服务(实例)下可以有很多数据库
- 一个数据库不能属于多个实例
- Oracle数据库中,一个实例只能有一个数据库,一个数据库可以在多个实例中
二、数据库基本操作
创建数据库
CREATE DATABASE testdb1 ENOCODING 'LATIN1' TEMPLATE template0;
ENOCODING 表示创建数据库使用的字符编码,一般不指定字符集,默认是UTF8
注意:template0公认不包含任何会受字符集编码或者排序影响的数据或缩影,可以作为创建任意字符集数据库的模板,默认就是template0;
若指定template1,要保证编码和区域设置要与template1模板数据库匹配;修改数据库
改变数据库的最大连接数
alter database mybase CONNECTION LIMIT 10;
改变数据库的名称
alter database mybase rename to mybase1;
改变数据库的配置参数,关闭数据库mybase上的默认索引扫描
alter database mybase set enable_indexscan to off;删除数据库
若有人连接到数据库上,则不能删除该数据库;
不能在事务块中删除数据库;
能在事务块中修改数据库;
drop database mybase;drop database if exists mybase;
三、模式schema
1.模式的定义
模式schema可以理解为一个命名空间或目录
-
不同模式下可以拥有相同名称的表、函数等对象,且互相不冲突
-
PG中,不能同时访问不同数据库中的对象,当要访问另一个数据库中的表或者其他对象时,需要重新连接到新的数据库
(1)一个用户连接到一个数据库后,可以同时访问这个数据库中多个模式的对象。
PG中模式的概念与Mysql中Database的概念是等同的,在Mysql中可以同时访问多个Database中的对象,与在PostgreSQL中可以同时多个Schema中的UI小是一样的;
Oracle数据库中的一个用户就对应一个schema。
(2)PG中,一个数据库包含一个或者多个模式,模式中又包含表、函数、操作符等函数对象 -
不同模式下可以有相同名称的表、函数等对象且相互不冲突
-
PG使用模式的原因
(1)允许多个用户在使用同一个数据库时彼此不干扰
(2)数据库对象放在不同的模式下,然后组织成逻辑组,让他们更便于管理
(3)第三方的应用可以放在不同的模式中,这样就不会和其他对象的名字冲突了
2.模式的使用
CREATE SCHEMA mydba;查看有哪些模式
\dn
DROP SCHEMA mydba;创建一个模式的同时,创建表的视图
CREATE SCHEMA mysba
CREATE TABLE t1 (id int, title text)
CREATE TABLE t1 (id int, contend text)
CREATE VIEW v1 AS SELECT a,id,a.title,b.contend from t1 a, t2 b where a.id=b.id;在模式中修改名称和属主
改名称:
alter schema mydba rename to mydba2;改属主:
alter schema mydba owner to mydba3;
3.公共模式
默认创建的数据库访问的都是public模式。
- 模式访问方式:要创建或者访问模式中的对象,需要先写出模式的名字
schema_name.table_name
4.模式的搜索路径
类似LInux中的$PATH
- 显示当前搜索路径
SHOW search_path;
5模式的权限
默认情况下,用户无法访问模式中不属于他们的对象。
- 若要访问,模式的所有者必须在模式上赋予他们USAGE权限
其他用户不能在模式public下创建对象
REVOKE CREATE on SCHEMA public FROM PUBLIC;
第一个public是模式的名称(标识符),第二个PUBLIC是所有用户(关键字,关键字有大写或者小写)以wangji用户连接osba数据库
\c osba wangji;create table t1(id int);失败
6.模式的移植性
-
与Oracle数据库兼容:
(1)Oracle中,模式和用户的概念一样。
移植到PG时,PG为每个用户都创建一个与用户名同名的模式,那么就能与Oracle数据库兼容;
(2)删除public模式; -
与Mysql兼容:
若Mysql实例中有三个数据库,移植到PG时,应该创建三个模式。
四、表
1.创建表
主键只有一个
create table test01(id int primary ket, node varchar(20));复合主键
create table test01(id1 int, id2 int,node varchar(20), CONSTRAINT pk_test01 primary key(id1,id2));唯一键
create table test03(id1 int, id2 int,node varchar(20), CONSTRAINT pk_test03 primary key(id1,id2), CONSTRAINT uk_test03_id3 UNIQUE(id3));CHECK
CREATE TABLE chile(name varchar(20), age int, note text, CONSTRAINT ck_chile_age CHECK(age<19));用其他表为模板创建新表
CREATE TABLE baby (LIKE chile);用其他表为模板创建新表,且把所有属性都复制过来
CREATE TABLE baby2 (LIKE chile INCLUDING ALL);
2.表的存储属性
TOAST(“The Oversized-Attribute Storage Technique”)
- 用于存储一个大字段的值,由于PG页面大小固定为8KB且不允许跨越多个页面,所以大字段值通常被压缩或切片成多个物理行存到另一张系统TOAST表中
- 支持TOAST的数据类型必须是变长的
将表的content字符安的TOAST字段修改为EXTERNAL
CREATE TABLE blog(id int, title text, content text);
ALTER TABLE blog ALTER content content SET STORAGE EXTERNAL;
fillfactor填充因子
- eg:设置为60,则表示往一个数据库中插入的数据占用60%的空间后,就不再往这个数据库中插入数据了,剩余的40%的空间是为了更新数据使用的。
- toast.fillfactor是这个表中TOAST表的填充因子
3.临时表
不管是事务级的临时表还是会话级的临时表,当会话结束时,临时表就会消失。
会话级的临时表
- 数据可以一直保存在整个会话的生命周期内
事务级的临时表
- 数据只存在于这个事务的生命周期内
创建一个同名的临时表(默认是会话级)
create TEMPORARY table tmp_t1(id in primary key, note text);
等价于
create TEMP table tmp_t1(id in primary key, note text);
\d
insert into tmp_t1 values(1,'111');开启另一个会话
psql postgres
\d pg_temp_2.tmp_t1
select * from pg_temp_2.tmp_t1;不能访问创建一个事务级别的临时表
on commit delete rows表示数据只存在于事务周期中,事务一提交,数据就消失
create TEMPORARY table tmp_t2(id in primary key, note text) on commit delete rows;
begin;
insert into tmp_t2 values(1,'aaa');
select * from tmo_t2;
end;
select * from tmo_t2;
4.默认值
create table student (no int, name varchar(20), age int default 15);
insert into student (no,name) values(1,'张三');
update student set age=default where id=2;默认值在插入的时候计算
create table blog(id int ,title text, created_date_timestamp timestamp default now());
5.约束
SQL允许在字段和表上定义约束,约束允许你对数据进行任意控制
(1)检查约束
create table persons(name varchar(40);age int CHECK(age>=40 and age<=150),sex boolean
);
给约束取名字
create table persons(name varchar(40);age int CONSTRAINT check_age CHECK(age>=40 and age<=150),sex boolean
);CREATE TABLE books(book_no integer,name text,price numeric CHECK(price>0),字段约束disconted_price numberric CHECK(disconted_price>0),CHECK(price > disconted_price )表约束
);
等价于
CREATE TABLE books(book_no integer,name text,price numeric,disconted_price numberric,CHECK(price>0)CHECK(disconted_price>0),CHECK(price > disconted_price )
);(2)非空约束
一个非空约束总是被写成一个字段约束。
一个字段约束可以有多个约束。
CREATE TABLE books(book_no integer not null,name text,price numeric
);
多个约束写法
CREATE TABLE books(book_no integer not null,name text,price numeric NOT NULL CHECK(price > 0)
);(3)唯一约束
字段约束
CREATE TABLE books(book_no integer UNIQUE,name text,pricee numeric
);表约束
CREATE TABLE books(book_no integer,name text,pricee numeric,UNIQUE(book_no )
);(4)外键约束(表之间的参照完整性约束)
create table class(class_no int primary key,class_name varchar(40)
);外键约束REFERENCES class(class_no)表面在学生表中class_no的取值必须出现在表class中,且为class_no中的一个数值
create table student(student_no int primary key,student_name varchar(40),age int,class_no int REFERENCES class(class_no)
);
6.修改表
(1)增加字段
alter table class add column class_teacher varchar(40);
alter table class add column class_teacher varchar(40) CHECK(class_teacher <> '');(2)删除字段
alter table class drop COLUMN class_teacher;
删除外键依赖
alter table class drop column class_no CASCADE;(3)增加约束
alter table student ADD CHECK(age<16);
alter table class add constraint unique_class_teacher UNIQUE(class_teacher);增加非空约束
alter table student alter column student_name set not null;(4)删除约束
\d student;查出约束
删除有名字的约束
alter table student drop constraint student_age_check;删除非空约束
alter table student alter column student_name drp not null;(5)修改默认值
不会改变现有的任何数据将,仅改变未来insert的
alter table student alter column age set DEFAULT 15;(6)删除默认值
alter table student alter column age drop DEFAULT;(7)修改字段数据类型
只有在字段里现有的每个项都可以隐式地转换成新类型时,才可以
alter table student alter column student_name TYPE text;(8)重命名字段
alter table books rename column book_no to book_id;(9)重命名表
alter table class rename to classs;
7.表继承及分区表
PG特有
- (1)所有父表的检查约束和非空约束都会自动被所有子表继承
- (2)一个子表可以从多个父表继承,他将拥有所有父表字段的总和
- (3)select,update,delete,alter table等命令访问父表时,也会同时访问或操作相应的子表。除非使用reindex,vacuum
- (4)唯一约束、外键的作用域不会扩大到子表
create table persons(name text,sex boolean,age int
);
增加一个学生表students,比persons表多了一个班级号class_no
class table students(class_no int
)INHERITS(persons);只显示父表的结果,不显示子表的
select * from only persons;
8.分区表
表分区就是把逻辑上的一个大表分割成物理上的几个小块。
- 多大的数据使用分区表?表的大小超过了数据库服务器的物理内存大小则使用
- 使用继承实现的分区表时,一般会让父表为空,数据都存储在子表中
- 分区的优化技巧:打开约束排除constraint_exclusion是一种查询优化技巧,constraint_exclusion默认就是partition,若采用默认值,则在SQL查询中将WHERE语句的过滤条件与表上的CHECK条件进行对比,就可以得知不需要扫描分区,从而跳过相应的分区表,所以性能就得到了提升。
>set constraint_exclusion='on';
五、触发器
触发器trigger是一种由事件自动触发执行的特殊的存储过程
- 事件可以是对一个表进行INSERT、UPDATE、DELETE等操作
- 触发器经常用于加强数据的完整性约束和业务规则上的约束等
1.创建触发器
要求:如果想在删除学生表中的一条记录时,就把这个学生在成绩表score中的成绩记录也删除掉
学生表
CREATE TABLE student(student_no int primary key,student_name varchar(40),age int
);考试成绩表
CREATE TABLE score(student_no int,chinese_score int,math_score int,test_date date
);先创建触发器的执行函数,返回来为触发器类型
CREATE OR REPLACE FUNCTION student_delete_trigger()
RETURNS TRIGGER AS $$
BEGINDELETE FROM score WHERE student_no =OLD.student_no RETURN OLD;
END;
$$
LANGUAGE plpgsql;再创建触发器
CREATE TRIGGER student_delete_triggerAFTER DELETE ON studentFOR EACH ROW EXECUTE PROCEDURE student_delete_trigger();INSERT into student values();
INSERT into score values();
DELETE FROM student where student_no=3;
select * from score;
2.语句级触发器与行级触发器
语句级触发器
- 执行每个SQL时,只执行一次
行级触发器
- 每行触发一次
为student创建一张log表
CREATE TABLE log_student(update_time timestamp,db_user varchar(40),操作的数据库用户名opr_type varchar(6),操作类型:insert、delete、update
)创建记录log的触发器函数
CREATE FUNCTION log_student_trigger ()
RETURNS trigger AS
$$
BEGINTG_OP是触发器的特殊变量:DML操作类型INSERT INTO log_student values(now(), user, TG_OP)RETURN NULL;
END;
$$
LANGUAGE "plpgsql";(1)语句级触发器
CREATE TRIGGER log_student_triggerAFTER INSERT OR DELETE OR UPDATE on student FORSTATEMENT EXECUTE PROCEDURE log_student_trigger();INSERT INTO student values();
select * from log_student;
删除log_student中的记录
delete from log_student;(2)行级触发器
删除触发器前需要清除log_student中的记录
drop trigger log_student_trigger on student;
delete from log_student;
建一个行级触发器
CREATE TRIGGER log_student_trigger2AFTER INSERT OR DELETE OR UPDATE on student FOR RAW EXECUTE PROCEDURE log_student_trigger ()
3.BEFORE触发器与AFTER触发器
语句级别的before触发器是在语句开始做任何事情之前就被触发了的,而语句级别的after触发器是在语句结束时才触发的;
行级别的before触发器在对特定航进行操作之前触发,行级别的after触发器是在语句结束时才触发的,但是它会在任何语句级别的after触发器被触发之前触发
- 方法中NEW和OLD指的是触发器监听的某表某行的新数据和未更新前的数据
BEFORE触发器可以直接修改NEW值,可以改变实际的更新值触发器函数:在插入表或者更新表时,在student_name后加上student_no学号
CREATE FUNCTION student_use_new_name_trigger()
RETURNS trigger AS '
BEGINNEW.student_name=NEW.student_name||student_no;RETURN NEW;
END;'
LANGUAGE "plpgsql"before触发器是在更新数据之前就触发的
CREATE TRIGGER user_new_student_triggerBEFORE INSERT OR UPDATE on studentFOR EACH RAW EXECUTE PROCEDURE student_use_new_name_trigger ();
4.删除触发器
删除触发器时,触发器函数不会被删除;
当删除表时,表上的触发器也会被删除;
DROP TRIGGER user_new_student_trigger on student;
5.触发器的行为
触发器函数的返回值RETURN总是NULL,必须显式写上
- BEFORE和INSTEAD OF行级触发器,返回的是NULL,则表示忽略对当前行的操作;
返回非NULL,返回的行将成为新插入的行或者将要更新的行; - AFTER的行级触发器,返回值会被忽略
- 如果是同一事件上的的多个触发器,就按照触发器的名字顺序来触发。
(1)BEFORE和INSTEAD OF行级触发器,每个触发器返回的行将成为下一个触发器的输入
(2)BEFORE和INSTEAD OF行级触发器返回的内容为空, 则修改行上的其他行级触发器也不会触发
6.触发器函数中的特殊变量
对于pl/pgSQL触发器函数来说,系统在调用它的时候,会开声明段里自动创建几个特殊的变量:
NEW:数据类型是record。在insert、update操作触发时存储新的数据行。
OLD:数据类型是record。在update、delete操作触发时存储旧的数据行。
TG_NAME:数据类型是name。触发器名称。
TG_WHEN:内容为"BEFORE"或“AFTER”,可以用来判断是BEFORE触发器还是AFTER触发器。
TG_LEVEL:内容为“ROW”或“STATEMENT”,可以用来判断是语句级触发器还是行级触发器。
TG_OP:内容为“INSERT”、“UPDATE”、“DELETE”、“TRUNCATE”,用于指定DML语句类型。
TG_RELID:触发器所在表的 oid。
TG_TABLE_NAME:触发器所在表的表名称。
TG_SCHEMA_NAME:触发器所在表的模式。
TG_NARGS:在创建触发器语句中赋予触发器过程的参数个数。
TG_ARGV[]:text类型的一个数组。创建触发器语句中指定的参数。
六、事件触发器
目的是解决版本不支持DDL触发器的功能,事件触发器支持以下三种DDL事件:
- ddl_command_start:一个DDL开始执行前被触发
- ddl_command_end:一个DDL执行完成后被触发
- sql_drop:删除一个数据库对象前被触发
事件触发器函数仅支持TG_EVENT和TG_TAG 变量
- TG_EVENT:ddl_command_start,ddl_command_end,sql_drop
- TG_TAG :具体的哪种DDL操作,eg:CREATE TABLE,DROP TABLE。但是TRUNCATE table test01;还是可以用的
禁止所有DDL语句
CREATE OR REPLACE FUNCTION abort_any_command()
RETURN event_trigger
LANGUAGE plpgsql AS $$
BEGINRAISE EXCEPTION 'command % is disables', tg_tag;
END;
$$CREATE EVENT TRIGGER abort_DDL ON DDL_command_startEXECUTE PROCEDURE abort_any_command();禁止事件触发器
ALTER EVENT TRIGGER abort_DDL DISBLE;
事件触发器sql_drop
- 可以调用一个函数pg_event_trigger_dropped_objects()获取删除数据库对象的信息
查询系统视图pg_event_trigger看到已有的事件触发器
select * from pg_event_trigger;创建一个事件触发器,记录数据库中对象删除的审计日志
--创建审计记录表
create table log_drop_objects(
op_time timestamp, --操作执行时间
ddl_tag text, --执行的ddl操作
classid Oid,
objid Oid,
objsubid OID,
object_type text,
schema_name text,
object_name text,
object_identify text
);--创建触发器函数
create function event_trigger_log_drops()
returns event_trigger language plpgsql as $$
declare
obj record;
begin
insert into log_drop_objects select now(),tg_tag,classid,objid,objsubid,
object_type,schema_name,object_name,
object_identity from pg_event_trigger_dropped_objects();
end
$$;--创建触发器
create event trigger event_trigger_log_drops
on sql_drop
execute procedure event_trigger_log_drops();create table test01(id int primary key, note varchar(20));
alter table test01 drop column note;
drop table test01;
删除一张表,数据库实际还删除了表的主键约束,主键索引,以及和以表名相同的类型等内容
select ddl_tag,object_type,object_name,object_identity from log_drop_objects;
修改事件触发器
ALTER EVENT TRIGGER name DISABLE
ALTER EVENT TRIGGER name ENABLE [ REPLICA | ALWAYS ]
ALTER EVENT TRIGGER name OWNER TO new_owner
ALTER EVENT TRIGGER name RENAME TO new_name
七、表空间
1.表空间的定义
PG中,表空间实际上是为表指定一个存储的目录
2.表空间的使用
创建表空间:create tablespace tbs_data location '/home/pgdata'创建数据库时指定默认的表空间:create database db01 tablespace tbs_data;改变一个数据库的默认表空间时的语法:alter database db01 set tablespace tbs_data;创建表的时候也可以指定表空间:create table test01(id int, note text) tablespace tbs_data;创建索引的时候同样可以指定表空间:create index idx_test01_id on test01(id) tablespace tbs_dasta;建唯一约束时,也可以指定约束索引的表空间:alter table test01 add constraint unique_test01_id unique(id) using index tablespace tbs_data;增加主键时,也可指定主键索引的表空间:alter table test01 add constraint pk_test01_id primary key(id) using index tablespace tbs_data;把表从一个表空间移动到另一个表空间的命令:alter table test01 set tablespace pg_default;
注意:在移动表的时候会锁表,此时对该表的所有操作都将被阻塞,
包括select操作,所以请考虑在合适的实际做这个操作。
八、视图
1.视图的定义
视图是查询语句定义额虚拟表
- 从视图重看大ode数据可能来自数据库中的一张或者多张表
- 一般数据库提供的视图大多数是只读的
2.创建视图
创建一个可以隐藏password的视图:
postgres=# create table users(
id int,
user_name varchar(40),
password varchar(256),
user_email text,
user_mark text
);create view vw_users as select id,
user_name,user_email,user_mark from users;使用temp 或者 temporary 关键字来创建视图,当session结束时,视图消失
postgres=# create temp view vw_users as select id,user_name,user_email,user_mark from users;若session未结束,\d可以看到临时临时视图视图也能为查询的各列定义另一个名称。列如将原来表中的id、user_name、user_email、user_mark等
列名重新定义为no、name、email、mark
postgres=# create view vm_users(no,name,email,mark) as select id,user_name,
postgres-# user_email,user_mark from users;
3.可更新视图
若直接update视图中的字段失败,则可以使用规则来update视图
postgres=# create table users(
id int,
user_name varchar(40),
password varchar(256),
user_email text,
user_mark text
);create view vw_users as select id,
user_name,user_email,user_mark from users;create rule vw_users_upd as on update to vw_users do instead update users set user_email=NEW.user_email;update vw_users set user_email='zhangsan@163.com' where id=1;
增加一个删除规则的方式来实现删除视图中的数据
CREATE RULE vm_users_del AS ON DELETE TO vw_users DO INSTAED DELETE FROM users WHERE od=OLD,id;delete from vw_users where id=1;
增加一个插入规则来实现可插入数据的视图
CREATE RULE vm_users_del AS ON INSERT TO vw_users DO INSTEAD INSERT INTO users(id, user_name,password, user_email,user_mark) values(NEW.id, NEW.user_name, '111', NEW.user_email, NEW.user_mark);insert into vw_users values(2,'王继','jiwangreal@163.com','hello world')
通过INSTAED OF触发器更新视图
CREATE OR REPLACE FUNCTION vw_users_insert_trigger()
RETURNS TRIGGER AS $$
BEGININSERT INTO users values(NEW.id, NEW.user_name, '111', NEW.user_email, NEW.user_mark);RETURN NULL;
END;
$$
LANGUAGE plpgsql;CREATE OR REPLACE FUNCTION vw_users_update_trigger()
RETURNS TRIGGER AS $$
BEGINUPDATE FROM users SET user_email=NEW.user_email() WHERE id=NEW.id;RETURN NULL;
END;
$$
LANGUAGE plpgsql;CREATE OR REPLACE FUNCTION vw_users_delete_trigger()
RETURNS TRIGGER AS $$
BEGINDELETE FROM users WHERE id=NEW.id;RETURN NULL;
END;
$$
LANGUAGE plpgsql;CREATE TRIGGER insert_vw_users_triggerINSERT OF INSERT ON vw_usersFOR EACH ROW EXECUTE PROCEDURE vw_users_insert_trigger();CREATE TRIGGER update_vw_users_triggerINSERT OF INSERT ON vw_usersFOR EACH ROW EXECUTE PROCEDURE vw_users_update_trigger();CREATE TRIGGER delete_vw_users_triggerINSERT OF INSERT ON vw_usersFOR EACH ROW EXECUTE PROCEDURE vw_users_delete_trigger();
这篇关于<POSTGRESQL修炼之道:从小工到专家>之逻辑结构管理(1-5节)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!