COMP9311 Database Systems WEEK6

1. Additional video from last week

--1.Functions returning single atomic value without any database access (immutable)
--SQL简单函数
CREATE OR REPLACE FUNCTION
    add2a(a integer, b integer) RETURNS integer
AS $$
    SELECT a + b;
$$ LANGUAGE 'sql';
--语言这里的sql是区分大小写的
--使用如下
SELECT add2a(3, 5);
 add2a
-------
     8
(1 row)

--PLpgsql简单函数
CREATE OR REPLACE FUNCTION
    add2b(a integer, b integer) RETURNS integer
AS $$
BEGIN
    RETURN a + b;
END;
$$ LANGUAGE 'plpgsql';
--语言这里的plpgsql同样是区分大小写的
--plpgsql写法是以BEGIN开始,END;结束
--使用如下
SELECT add2b(3, 5);
 add2b
-------
     8
(1 row)


--2.Functions returning a single tuple with no database access
CREATE TYPE Pair AS (x integer, y integer);

CREATE OR REPLACE FUNCTION
    mkpair(a integer, b integer) RETURNS Pair
AS $$
DECLARE 
    p Pair;
BEGIN
    p.x := a;
    p.y := b;
    return p;
END;
$$ LANGUAGE 'plpgsql';
--使用如下
SELECT mkpair(3, 4);
 mkpair
--------
 (3,4)
(1 row)
SELECT * FROM mkpair(3, 4);
 x | y
---+---
 3 | 4
(1 row)


--3.Fuction returning a set of atomic values (SETOF)
CREATE OR REPLACE FUNCTION
    seq(hi integer) RETURNS setof integer
AS $$
DECLARE
    i integer;
BEGIN
    i := 1;
    while(i <= hi) loop
        return next i;
        i := i + 1;
    end loop;
    return;
END;
$$ LANGUAGE 'plpgsql';
--plpgsql使用古老的语言风格,assign是:=,循环不使用括号,而是用loop + end loop;
--使用如下
SELECT * FROM seq(5);
 seq
-----
   1
   2
   3
   4
   5
(5 rows)


--4.Function returning a set of tuples (i.e. table)
CREATE OR REPLACE FUNCTION
    squares(hi integer) RETURNS SETOF Pair
AS $$
DECLARE
    i integer;
    p Pair;
BEGIN
    FOR i in 1..hi loop
        p.x := i;
        p.y := i * i;
        return next p;
    end loop;
    return;
END;
$$ LANGUAGE 'plpgsql';
--使用如下
SELECT * FROM squares(3);
 x | y
---+---
 1 | 1
 2 | 4
 3 | 9
(3 rows)
SELECT x AS "n", y as "n^2" FROM squares(5);
 n | n^2
---+-----
 1 |   1
 2 |   4
 3 |   9
 4 |  16
 5 |  25
(5 rows)


--5.Function that reads from the database and returns a set of atomic values
CREATE OR REPLACE FUNCTION
    pizzaWith(_topping text) RETURNS setof text
AS $$
DECLARE
    _p Pizzas;
BEGIN
    for _p in 
        SELECT p.*
        FROM Pizzas p
            JOIN Has h ON h.pizza = p.id
            JOIN Toppings t ON h.topping = t.id
        WHERE t.name = _topping
    loop
        return next _p.name;
    end loop;
END;
$$ LANGUAGE 'plpgsql';
--local variable命名以_开始,以区分database和local variable
--使用如下
SELECT * FROM pizzaWith('cheddar');

--6.Function that reads from the database and returns a set of tuples (i.e. a table)
CREATE OR REPLACE FUNCTION
    pizzaWith1(_topping text) RETURNS setof Pizzas
AS $$
DECLARE
    _p Pizzas;
BEGIN
    for _p in 
        SELECT p.*
        FROM Pizzas p
            JOIN Has h ON h.pizza = p.id
            JOIN Toppings t ON h.topping = t.id
        WHERE t.name = _topping
    loop
        return next _p;
    end loop;
END;
$$ LANGUAGE 'plpgsql';
--使用如下
SELECT * FROM pizzaWith1('cheddar');

2. Extending SQL

通常可以创建以下一些类型,一些可以用sql实现,但是很多用extending方式更加容易:

2.1 new data types

可以用sql实现,例如:

create domain Positive as integer check (value > 0);
create type Rating as enum ('poor','ok','excellent');
create type Pair as (x integer, y integer);

2.2 more operations/aggregates for use in queires

2.2.1 循环不可以用sql实现,例如:

-- Factorial functions

-- iterative (while)
create or replace function
    fac(n integer) returns integer
as $$
declare
    i integer;
    f integer := 1;
begin
    if (n < 1) then
        raise exception 'Invalid fac(%)',n; 
        --n 表示string中%的位置的value
    end if;
    i := 2;
    while (i <= n) loop
        f := f * i;
        i := i + 1;
    end loop;
    return f;
end;
$$ language plpgsql;


-- iterative (for)
create or replace function
    facc(n integer) returns integer
as $$
declare
    i integer;
    f integer := 1;
begin
    if (n < 1) then
        raise exception 'Invalid fac(%)',n;
    end if;
    for i in 2 .. n loop 
    -- ..表示2 to n
        f := f * i;
    end loop;
    return f;
end;
$$ language plpgsql;


-- recursive
create or replace function
    facr(n bigint) returns bigint
as $$
begin
    if (n < 1) then
        raise exception 'Invalid fac(%)',n;
    elsif (n = 1) then
        return 1;
    else
        return n * facr(n-1);
    end if;
end;
$$ language plpgsql;

2.2.2 现代query types

除了之前文章提到的select/project/join, aggregation, grouping的query方式,现代很多query分为两种类型:

2.2.2.1 recursive,用来manage hierarchies, graphs

定义如下:

with recursive T(a1, a2, ...) as
(
    non-recursive select
  union
    recursive select involving T
)
select ... from T where ...

要求:
The subqueries generating T cannot be arbirtrary
(1)non-recursive select:
does not refer to T
generates an initial set of tuples for T (initialisation)
(2)recursive select:
must be a query involving T
must include a where condition
must eventually return an empty result (termination)
例如:

with recursive nums(n) as (
    select 1
  union
    select n+1 from nums where n < 100
)
select sum(n) from nums;
-- which produces ...
 sum  
------
 5050

2.2.2.2 window,用来spread group-by summaries

将下例的分组用一个tuple显示出来

select student, avg(mark)
from   CourseEnrolments
group  by student;

 student  |  avg         
----------+-------
 46000936 | 64.75
 46001128 | 73.50

下例 attach student's average mark to each enrolment,table中不存在avg这一列,但是用window的方式可以实现在每列数据后都加入该学生的avg。

select *, avg(mark)
over   (partition by student)
--over相当于在哪里加入window信息,这里是在每一个student后面加入avg(mark)的信息
from   CourseEnrolments;

 student  | course | mark | grade | stueval |  avg         
----------+--------+------+-------+---------+-------
 46000936 |  11971 |   68 | CR    |       3 | 64.75
 46000936 |  12937 |   63 | PS    |       3 | 64.75
 46000936 |  12045 |   71 | CR    |       4 | 64.75
 46000936 |  11507 |   57 | PS    |       2 | 64.75
 46001128 |  12932 |   73 | CR    |       3 | 73.50
 46001128 |  13498 |   74 | CR    |       5 | 73.50
 46001128 |  11909 |   79 | DN    |       4 | 73.50
 46001128 |  12118 |   68 | CR    |       4 | 73.50

练习题:Using window functions, write an SQL function to find students whose mark is < 60% of average mark for course.

create or replace function
    under(integer) returns setof CourseEnrolments
as $$
select student,course,mark,grade,stueval
from   CourseAverages
where  course = $1 and mark < 0.6*avg
$$
language sql stable;
--stable含义: access the database without change it

-- Generate the CourseAverages table using window function

create view CourseAverages as
select student,course,mark,grade,stueval,avg(mark)
over   (partition by course)
from   CourseEnrolments;
--注意:CourseEnrolments的avg(mark)是一个学生所有学科的平均分,21行的partition by course则是根据课程的平均分

2.2.3 With queries

为了实现一个复杂的查询,往往需要中间有很多小查询,但往往并不需要这些中间查询永久存在,所以可以用with queiries来临时创建中间的view或其他步骤。例如:

with V as (select a,b,c from ... where ...),
     W as (select d,e from ... where ...)
select V.a as x, V.b as y, W.e as z
from   V join W on (v.c = W.d);
--或者
select V.a as x, V.b as y, W.e as z
from   (select a,b,c from ... where ...) as V,
       (select d,e from ... where ...) as W
where  V.c = W.d;

2.2.4 Aggregates

Aggregates reduce a collection of values into a single result.例如count(tuples), sum(numbers)

AggState = initial state
for each item V {
    # update AggState to include V
    AggState = newState(AggState, V)
}
return final(AggState)

例如,table R是一系列a,b,c tuple组成的table,使用aggragate可以显示精简后的信息,如sum和count:

--table R
a | b | c      
---+---+---
 1 | 2 | x       a | sum | count
 1 | 3 | y      ---+-----+-------
 2 | 2 | z       1 |   5 |     2
 2 | 1 | a       2 |   6 |     3
 2 | 3 | b

select a,sum(b),count(*) from R group by a

 a | sum | count
 ---+-----+-------
 1 |   5 |     2
 2 |   6 |     3

user-defined aggregates:
BaseType ... type of input values
StateType ... type of intermediate states
state mapping function: sfunc(state,value) → newState
[optionally] an initial state value (defaults to null)
[optionally] final function: ffunc(state) → result

CREATE AGGREGATE AggName(BaseType) (
    sfunc     = NewStateFunction,
    stype     = StateType,
    initcond  = InitialValue,  --optional
    finalfunc = FinalResFunction, --optional
    sortop    = OrderingOperator  --optional
);

例如:

create aggregate myCount(anyelement) (
    stype    = int,    -- the accumulator type
    initcond = 0,      -- initial accumulator value
    sfunc    = oneMore -- increment function
);

create function
    oneMore(sum int, x anyelement) returns int
as $$
begin return sum + 1; end;
$$ language plpgsql;

例如:

create type IntPair as (x int, y int);

create function
    AddPair(sum int, p IntPair) returns int
as $$
begin return p.x + p.y + sum; end;
$$ language plpgsql;

create aggregate sum2(IntPair) (
    stype     = int,
    initcond  = 0,
    sfunc     = AddPair
);

练习题:Define a concat aggregate that takes a column of string values and returns a comma-separated string of values

select count(*), concat(name) from Employee;
-- returns e.g.
  count |         concat
 -------+----------------------
      4 | John,Jane,David,Phil

2.3 more powerful constraint checking

包括attribute constraint,例如

age     integer check (age > 15),

relation constraint和referential integrity,例如

create table Employee (
   id      integer primary key,
   name    varchar(40),
   salary  real,
   age     integer check (age > 15),
   worksIn integer
              references Department(id),
   constraint PayOk check (salary > age*1000)
);

除此之外,还可以用assertion,但是assertion非常耗时耗能,每次改变table都一定要执行assertion,谨慎使用。

CREATE ASSERTION name CHECK (condition)

例如:

create assertion ClassSizeConstraint check (
   not exists (
      select c.id from Courses c, CourseEnrolments e
      where  c.id = e.course
      group  by c.id having count(e.student) > 9999
   )
);

create assertion AssetsCheck check (
   not exists (
      select branchName from Branches b
      where  b.assets <>
             (select sum(a.balance) from Accounts a
              where a.branch = b.location)
   )
);

2.4 event-based triggered actions

Triggers are procedures stored in the database and activated in response to database events (e.g. updates).

Triggers provide event-condition-action (ECA) programming:
--an event activates the trigger
--on activation, the trigger checks a condition
--if the condition holds, a procedure is executed (the action)

CREATE TRIGGER TriggerName
{AFTER|BEFORE}  Event1 [ OR Event2 ... ]
[ FOR EACH ROW ]
ON TableName
[ WHEN ( Condition ) ]
Block of Procedural/SQL Code ;

Triggers can be activated BEFORE or AFTER the event.
If activated BEFORE, can affect the change that occurs:
--NEW contains "proposed" value of changed tuple
--modifying NEW causes a different value to be placed in DB
If activated AFTER, the effects of the event are visible:
--NEW contains the current value of the changed tuple
--OLD contains the previous value of the changed tuple
--constraint-checking has been done for NEW

Note: OLD does not exist for insertion; NEW does not exist for deletion.

例如insert:

create trigger X before insert on T Code1;
create trigger Y after insert on T Code2;
insert into T values (a,b,c,...);

sequence of events:
1.execute Code1 for trigger X
2.code has access to (a,b,c,...) via NEW
3.code typically checks the values of a,b,c,..
4.code can modify values of a,b,c,.. in NEW
5.DBMS does constraint checking as if NEW is inserted
6.if fails any checking, abort insertion and rollback
7.execute Code2 for trigger Y
8.code has access to final version of tuple via NEW
9.code typically does final checking, or modifies other tables in database to ensure constraints are satisfied

Reminder: there is no OLD tuple for an INSERT trigger.

例如update:

create trigger X before update on T Code1;
create trigger Y after update on T Code2;
update T set b=j,c=k where a=m;

sequence of events:
1.execute Code1 for trigger X
2.code has access to current version of tuple via OLD
3.code has access to updated version of tuple via NEW
4.code typically checks new values of b,c,..
5.code can modify values of a,b,c,.. in NEW
6.do constraint checking as if NEW has replaced OLD
7.if fails any checking, abort update and rollback
8.execute Code2 for trigger Y
9.code has access to final version of tuple via NEW
10.code typically does final checking, or modifies other tables in database to ensure constraints are satisfied

Reminder: both OLD and NEW exist in UPDATE triggers.

例如delete:

create trigger X before delete on T Code1;
create trigger Y after delete on T Code2;
delete from T where a=m;

sequence of events:
1.execute Code1 for trigger X
2.code has access to (a,b,c,...) via OLD
3.code typically checks the values of a,b,c,..
4.DBMS does constraint checking as if OLD is removed
5.if fails any checking, abort deletion (restore OLD)
6.execute Code2 for trigger Y
7.code has access to about-to-be-deleted tuple via OLD
8.code typically does final checking, or modifies other tables in database to ensure constraints are satisfied

Reminder: tuple NEW does not exist in DELETE triggers.

Triggers in PostgreSQL:

CREATE TRIGGER TriggerName
{AFTER|BEFORE}  Event1 [OR Event2 ...]
ON TableName
[ WHEN ( Condition ) ]
FOR EACH {ROW|STATEMENT}
EXECUTE PROCEDURE FunctionName(args...);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • 答应同学兼好友去杭州看她,承诺了三年终于践行了,我们选的日子正是杭州G20峰会期间,进杭州的安检简直了,让我想起那...
    晩夏阅读 193评论 0 0
  • 崽崽已经一岁半了,但是说实话,我还真的没有在缺乏长辈的帮助下独立照顾他,心里还是有些慌的。同时慌的,还有孩子和他的...
    李九_Lijiu阅读 125评论 0 0
  • 我过 怎样的生活 关你屁事 屁打架 现实中总会有这样的朋友,过着比我们好的生活,却又对着别人的生活说三道四。你是过...
    屁打架阅读 911评论 0 0
  • 只可惜因为最后的离别 没能记得问问他 我心心念念的爆米花 他有没有留一点给我 而我们照的照片 也不晓得他是什么表情...
    程瑶瑶瑶瑶瑶阅读 134评论 0 0