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閱讀 230,182評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,489評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,290評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,776評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,510評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,866評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,860評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,036評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,585評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,331評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,536評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,058評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,754評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,154評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,469評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,273評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,505評論 2 379

推薦閱讀更多精彩內容

  • 答應同學兼好友去杭州看她,承諾了三年終于踐行了,我們選的日子正是杭州G20峰會期間,進杭州的安檢簡直了,讓我想起那...
    晩夏閱讀 235評論 0 0
  • 崽崽已經一歲半了,但是說實話,我還真的沒有在缺乏長輩的幫助下獨立照顧他,心里還是有些慌的。同時慌的,還有孩子和他的...
    李九_Lijiu閱讀 138評論 0 0
  • 我過 怎樣的生活 關你屁事 屁打架 現實中總會有這樣的朋友,過著比我們好的生活,卻又對著別人的生活說三道四。你是過...
    屁打架閱讀 1,008評論 0 0
  • 只可惜因為最后的離別 沒能記得問問他 我心心念念的爆米花 他有沒有留一點給我 而我們照的照片 也不曉得他是什么表情...
    程瑤瑤瑤瑤瑤閱讀 146評論 0 0