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...);
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

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