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';
SELECT venue_id,count(*) FROM events GROUP BY venue_id;
SELECT venue_id,count(*) FROM events GROUP BY venue_id HAVING count(*)>=2 AND venue_id is not null;
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;
除了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';
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;
SELECT name,to_char(date, 'Month DD,YYYY') AS date
FROM holidays
WHERE date >= '2012-02-14';
ALTER TABLE events ADD colors text ARRAY;
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';
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))
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))
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;
UPDATE holidays set colors = '{"red","green"}' where name = 'Christmas Day';