PostgreSQL-高级查询、代码和规则

聚合函数
聚合函数按照一些共同的标准将多行的结果分组。例如统计一张表的行数,或者计算某些数值列的平均数。
如果要在PostgreSQL里面输入Valentine's Day,可以用双撇号转义,如Valentine''s Day

最简单的聚合函数是count()。min()返回最小值,max()返回最大值。
%是LIKE搜索中的通配符。
SELECT count(title) FROM events WHERE title like '%Day%';

查找最早开始时间和最晚结束时间。
select min(starts),max(ends) from events e INNER JOIN venues v on e.venue_id = v.venue_id where v.name = 'Crystal Ballroom';

分组
使用GROUP BY对行进行归类,然后执行一些聚合函数(如count())。
SELECT venue_id,count(*) FROM events GROUP BY venue_id;

GROUP BY有自己的过滤关键字: HAVING。HAVING和WHERE子句类似,只不过HAVING可以用聚合函数作为过滤条件,而WHERE不能。
SELECT venue_id,count(*) FROM events GROUP BY venue_id HAVING count(*)>=2 AND venue_id is not null;

窗口函数
窗口函数类似于GROUP BY查询,允许对多行执行聚合函数。所不同的是,窗口函数允许使用内置的聚合函数,而不要求将每个字段分组成单行。

select venue_id,count(*) over(PARTITION BY venue_id) from events order by venue_id;
select venue_id,count(*) from events group by venue_id order by venue_id;

PARTITION BY和GROUP BY类似,但是PARTITION BY不会在select属性列表之后,再对结果分组(从而将结果合并成较少的行),而是像其他所有字段一样返回分组的值(根据分组变量进行计算,但其他方面就是另一个属性)。或者按SQL的表达方式,它超越(OVER)结果集的分区(PARTITION),返回聚合函数的结果。


事务
       事务保障了关系数据库的一致性。事务的准则是要么全部成功,要么全部失败。事务确保一组命令中的每一条命令都执行。如果过程中间发生了任何失败,所有的命令都将回滚。

PostgreSQL的事务遵循ACID,代表原子性(atomic,所有的操作都成功或者都不做),一致性(consistent,数据始终处于完整的状态),隔离性(isolated,事务相互之间不感染),以及持久性(durable,即使在服务器崩溃之后,提交的事务都是安全的)。

可以将任何事务的命令至于BEGIN TRANSACTION块内。

BEGIN TRANSACTION;
  DELETE FROM events;
ROLLBACK;         //验证原子性
SELECT * FROM events;
如果要修改两个表,并希望它们保持同步,事务就很有用。

最典型的例子是一个银行借记/贷记系统,其中钱从一个账户转移到另一个账户:

BEGIN TRANSACTION;
  UPDATE account SET total=total+5000.5 where account_id=1337;
  UPDATE account SET total=total-5000.5 where account_id=45887;
END;

在psql中执行的每条命令都隐式地包裹在事务中。如果你执行一条命令,如DELETE FROM account where total< 20;数据组在删除的中途崩溃,不会被迫接受半张表。当重新启动数据库服务器的时候,该命令将回滚。



存储过程
使用存储过程可以避免将数千行数据发送到客户端应用程序,但也让应用程序代码与该数据库绑定,因此不应该轻易决定使用存储过程。


创建一个过程(FUNCTION),简化了向event表插入记录的工作,无需venue_id,就可以插入在某个场地举行的活动。如果场地不存在,会先创建,并在新的事件中引用它。最后函数会返回一个布尔值,表明添加新场地是否成功。

add_event.sql

CREATE OR REPLACE FUNCTION add_event(i_title text,i_starts timestamp,i_ends timestamp,venue text,postal varchar(9),country char(2))
RETURNS boolean AS $$
DECLARE
  did_insert boolean := false;
  found_count integer;
  the_venue_id integer;
BEGIN 
  SELECT venue_id INTO the_venue_id
  FROM venues v
  WHERE v.postal_code =postal AND v.country_code=country AND v.name ILIKE venue LIMIT 1;


  IF the_venue_id IS NULL THEN
  INSERT INTO venues(name,postal_code,country_code)
  VALUES(venus,postal,country)
  RETURNING venue_id INTO the_venue_id;


  did_insert :=true;
	END IF;


--Note:not an"error",as in some programming languages RAISE NITICE 'Venus found %',the_venue_id;


  INSERT INTO events(i_title,i_starts,i_ends,i_venue_id)
  VALUES(title,starts,ends,venue_id);


  RETURN did_insert;
END;
$$LANGUAGE plpgsql;

SELECT add_event('House Party','2012-05-03 23:00','2012-05-04 02:00','Run''s House','97205','us');

我们所写的存储过程中使用的语言是plpgsql(Procedural Language/PostgreSQL)。
可以在服务器端执行 createlang testdb --list,查看数据库中安装的语言,createlang命令也可以添加新的语言。

除了PL/pgSQL,PostgreSQL还支持三种更核心的语言Tcl、Perl和Python。还有Ruby、Jave、PHP、Scheme等拓展模块。


触发器
当插入或更新的操作发生时,触发器会自动调用存储过程。它允许数据库在数据变化的时候,强制执行一些必要的操作。

创建一个新函数,当活动信息event更新的时候,都会记录相信的日志。
首先创建一个logs表记录活动信息的变化,这里没有必要使用主键,因为只是日志。

create table logs(
  event_id integer,
  old_title varchar(255),
  old_starts timestamp,
  old_ends timestamp,
  logged_at timestamp default current_timestamp
);

接下来,创建一个函数,将更新前的数据写入日志。

testdb/log_event.sql

CREATE OR REPLACE FUNCTION log_event() RETURNS trigger as $$
DECLARE
BEGIN
	INSERT INTO logs(event_id,old_title,old_starts,old_ends)
	values(OLD.event_id,OLD.title,OLD.starts,OLD.ends);


	RAISE NOTICE 'someone just changed event #%',OLD.event_id;
	RETURN new;
end;
$$LANGUAGE plpgsql;

 
  
最后,创建触发器,可以在任意行更新后记录相应变更。
create trigger log_events
	after update on events
	for each row execute procedure log_event();


现在更新一个事件做测试
update events
set ends='2012-05-04 01:00:00'
where title='LARP CLUB';

NOTICE:  someone just changed event #1
而且原来的结束时间计入了日志。
SELECT event_id,old_title,old_ends,logged_at FROM logs;

触发器还可以在更新之前以及插入之前或者之后创建。


视图
VIEW可以使复杂查询的结果用起来像其他任何表一样。
与存储过程不同,VIEW不是执行的函数,而是查询的别名。

postgres/holiday_view_1.sql

CREATE VIEW holidays AS 
	SELECT event_id AS holiday_id,title AS name,starts AS date
	FROM events
	WHERE title LIKE '%Day%' AND venue_id IS not NULL;

可以像查询其他任何表一样查询holidays,其后面是普通的events表。
下面是查询节日视图holidays。
SELECT name,to_char(date, 'Month DD,YYYY') AS date
FROM holidays
WHERE date >= '2012-02-14';


视图是强大的工具,可以以一种简单的方式访问复杂查询的数据。
如果想在视图中添加一个新列,那么只能修改底层的表。
修改events表,使它有一组相关的颜色。
ALTER TABLE events ADD colors text ARRAY;

因为holidays有与events相关联的颜色组,所以需要修改视图的查询,以包含colors数组。
CREATE OR REPLACE VIEW holidays AS 
	SELECT event_id AS holiday_id,title AS name,starts AS date,colors
	FROM events
	WHERE title LIKE '%Day%' AND venue_id IS not NULL;

现在为选定的日子设置一个颜色字符串数组。
UPDATE holidays set colors = '{"red","green"}' where name = 'Christmas Day';


规则
规则是对如何修改解析过的查询树的描述。
postgres每次运行一条SQL语句,它将语句解析成查询树(抽象语法树)。
树的枝和叶是运算符和值,在执行前,树会被遍历、删减,并以其他方式修改。这棵树可以被postgres规则重新,然后发送到查询规划器(它也是以某种方式重写这棵树,以达到优化性能运行效果),最后会把最终的 命令发送到执行器。


诸如holidays这样的视图其实就是一条规则。

PostgreSQL-高级查询、代码和规则_第1张图片


用EXPLAIN命令查看holidays视图的执行计划
(Filter是where的子句,Output为列的列表)


mydb=# EXPLAIN VERBOSE SELECT * FROM holidays;
                                  QUERY PLAN                                   
-------------------------------------------------------------------------------
 Seq Scan on public.events  (cost=0.00..17.75 rows=25 width=76)
   Output: events.event_id, events.title, events.starts, events.colors
   Filter: ((events.venue_id IS NOT NULL) AND (events.title ~~ '%Day%'::text))

如果对定义holidays视图的查询语句执行EXPLAIN VERBOSE,并和上面的视图比较,功能上是等价的。
EXPLAIN VERBOSE
	SELECT event_id AS holiday_id,title AS name,starts AS date,colors
	FROM events
	WHERE title LIKE '%Day%' AND venue_id IS not NULL;


                                  QUERY PLAN                                   
-------------------------------------------------------------------------------
 Seq Scan on public.events  (cost=0.00..17.75 rows=25 width=76)
   Output: event_id, title, starts, colors
   Filter: ((events.venue_id IS NOT NULL) AND (events.title ~~ '%Day%'::text))

所以为了允许更新holidays视图,需要定义一条规则,告诉PostgreSQL在update的时候做什么操作。
规则将捕捉对holidays视图的更新,从OLD与NEW的伪关系取值,并在events上执行更新。
NEW看做是包含即将更新的值的表,而OLD则是包含查询的值。

postgres/create_rule.sql
CREATE RULE update_holidays AS ON UPDATE TO holidays DO INSTEAD
	UPDATE events 
	SET title = NEW.name,
			starts = NEW.date,
			colors = NEW.colors
	WHERE title = OLD.name;

有了这条规则,就可以直接更新holidays。
UPDATE holidays set colors = '{"red","green"}' where name = 'Christmas Day';


你可能感兴趣的:(PostgreSQL,Notes,for,DB,postgresql)