Professional Documents
Culture Documents
David Wheeler
Kineticode
1
2
But First…
2
3
SQL on Rails!
http://www.sqlonrails.org/
3
Learning PL/pgSQL
David Wheeler
Kineticode
4
Application Tiers
5
Application Tiers
Application developers used to tiers
5
Application Tiers
Application developers used to tiers
UI layer (Browser)
5
Application Tiers
Application developers used to tiers
UI layer (Browser)
5
Application Tiers
Application developers used to tiers
UI layer (Browser)
5
What Have We Learned?
6
What Have We Learned?
Application layer
6
What Have We Learned?
Application layer
P* languages
6
What Have We Learned?
Application layer
P* languages
SQL
6
What Have We Learned?
Application layer
P* languages
SQL
Templating
6
What Have We Learned?
7
What Have We Learned?
UI layer
7
What Have We Learned?
UI layer
X?HTML
7
What Have We Learned?
UI layer
X?HTML
CSS
7
What Have We Learned?
UI layer
X?HTML
CSS
JavaScript
7
What Have We Learned?
UI layer
X?HTML
CSS
JavaScript
AJAX
7
What Have We Learned?
8
What Have We Learned?
Database layer
8
What Have We Learned?
Database layer
CREATE TABLE
8
What Have We Learned?
Database layer
CREATE TABLE
CREATE INDEX
8
What Have We Learned?
Database layer
CREATE TABLE
CREATE INDEX
8
What Have We Learned?
Database layer
CREATE TABLE
CREATE INDEX
Erm…
8
What Have We Learned?
Database layer
CREATE TABLE
CREATE INDEX
Erm…
Is that it?
8
What about the
Database?
9
What about the
Database?
What we haven’t learned
9
What about the
Database?
What we haven’t learned
Views
9
What about the
Database?
What we haven’t learned
Views
Rules
9
What about the
Database?
What we haven’t learned
Views
Rules
Triggers
9
What about the
Database?
What we haven’t learned
Views
Rules
Triggers
Domains
9
What about the
Database?
What we haven’t learned
Views
Rules
Triggers
Domains
Aggregates
9
What about the
Database?
What we haven’t learned
Views
Rules
Triggers
Domains
Aggregates
Functions/Stored Procedures
9
10
Isn’t it Time We
Changed That?
10
PostgreSQL Functions
11
PostgreSQL Functions
SQL
PL/Perl
PL/Python
PL/TCL
PL/Ruby
PL/Java
PL/PHP
PL/pgSQL
11
What is PL/pgSQL?
12
What is PL/pgSQL?
Procedural programming language
12
What is PL/pgSQL?
Procedural programming language
12
What is PL/pgSQL?
Procedural programming language
Variables
12
What is PL/pgSQL?
Procedural programming language
Variables
Conditionals
12
What is PL/pgSQL?
Procedural programming language
Variables
Conditionals
Looping constructs
12
What is PL/pgSQL?
Procedural programming language
Variables
Conditionals
Looping constructs
Exceptions
12
What is PL/pgSQL?
Procedural programming language
Variables
Conditionals
Looping constructs
Exceptions
13
Adding PL/pgSQL
13
CREATE FUNCTION
14
CREATE FUNCTION
CREATE OR REPLACE FUNCTION name (
14
CREATE FUNCTION
CREATE OR REPLACE FUNCTION name (
type,
type
14
CREATE FUNCTION
CREATE OR REPLACE FUNCTION name (
type,
type
) RETURNS type
14
CREATE FUNCTION
CREATE OR REPLACE FUNCTION name (
type,
type
) RETURNS type AS $$
14
CREATE FUNCTION
CREATE OR REPLACE FUNCTION name (
type,
type
) RETURNS type AS $$
// Function Body
14
CREATE FUNCTION
CREATE OR REPLACE FUNCTION name (
type,
type
) RETURNS type AS $$
// Function Body
$$ LANGUAGE langname attributes;
14
A Note on Examples
15
A Note on Examples
Early examples calculate Fibonacci numbers
15
A Note on Examples
Early examples calculate Fibonacci numbers
15
A Note on Examples
Early examples calculate Fibonacci numbers
0, 1, 1, 2, 3, 5, 8, 13, 21…
15
A Note on Examples
Early examples calculate Fibonacci numbers
0, 1, 1, 2, 3, 5, 8, 13, 21…
http://en.wikipedia.org/wiki/Fibonacci_number
15
A Note on Examples
Early examples calculate Fibonacci numbers
0, 1, 1, 2, 3, 5, 8, 13, 21…
http://en.wikipedia.org/wiki/Fibonacci_number
15
A Note on Examples
Early examples calculate Fibonacci numbers
0, 1, 1, 2, 3, 5, 8, 13, 21…
http://en.wikipedia.org/wiki/Fibonacci_number
15
A Note on Examples
Early examples calculate Fibonacci numbers
0, 1, 1, 2, 3, 5, 8, 13, 21…
http://en.wikipedia.org/wiki/Fibonacci_number
16
Recursive Fibonacci
Function
CREATE OR REPLACE FUNCTION fib (
fib_for integer
) RETURNS integer AS $$
BEGIN
IF fib_for < 2 THEN
RETURN fib_for;
END IF;
RETURN fib(fib_for - 2) + fib(fib_for - 1);
END;
$$ LANGUAGE plpgsql strict;
16
Recursive Fibonacci
Function
CREATE OR REPLACE FUNCTION fib (
fib_for integer
) RETURNS integer AS $$
BEGIN
IF fib_for < 2 THEN
RETURN fib_for;
END IF;
RETURN fib(fib_for - 2) + fib(fib_for - 1);
END;
$$ LANGUAGE plpgsql strict;
16
Recursive Fibonacci
Function
CREATE OR REPLACE FUNCTION fib (
fib_for integer
) RETURNS integer AS $$
BEGIN
IF fib_for < 2 THEN
RETURN fib_for;
END IF;
RETURN fib(fib_for - 2) + fib(fib_for - 1);
END;
$$ LANGUAGE plpgsql strict;
16
Recursive Fibonacci
Function
CREATE OR REPLACE FUNCTION fib (
fib_for integer
) RETURNS integer AS $$
BEGIN
IF fib_for < 2 THEN
RETURN fib_for;
END IF;
RETURN fib(fib_for - 2) + fib(fib_for - 1);
END;
$$ LANGUAGE plpgsql strict;
16
Recursive Fibonacci
Function
CREATE OR REPLACE FUNCTION fib (
fib_for integer
) RETURNS integer AS $$
BEGIN
IF fib_for < 2 THEN
RETURN fib_for;
END IF;
RETURN fib(fib_for - 2) + fib(fib_for - 1);
END;
$$ LANGUAGE plpgsql strict;
16
Recursive Fibonacci
Function
CREATE OR REPLACE FUNCTION fib (
fib_for integer
) RETURNS integer AS $$
BEGIN
IF fib_for < 2 THEN
RETURN fib_for;
END IF;
RETURN fib(fib_for - 2) + fib(fib_for - 1);
END;
$$ LANGUAGE plpgsql strict;
16
Recursive Fibonacci
Function
CREATE OR REPLACE FUNCTION fib (
fib_for integer
) RETURNS integer AS $$
BEGIN
IF fib_for < 2 THEN
RETURN fib_for;
END IF;
RETURN fib(fib_for - 2) + fib(fib_for - 1);
END;
$$ LANGUAGE plpgsql strict;
16
Try It
17
Accessing the Database
18
Accessing the Database
Use SQL to access relations
18
Accessing the Database
Use SQL to access relations
18
Accessing the Database
Use SQL to access relations
18
Accessing the Database
Use SQL to access relations
18
Memoized Fibonacci
19
Memoized Fibonacci
CREATE OR REPLACE FUNCTION fib_cached(
fib_for int
) RETURNS integer AS $$
DECLARE
ret integer;
BEGIN
if fib_for < 2 THEN
RETURN fib_for;
END IF;
19
Memoized Fibonacci
CREATE OR REPLACE FUNCTION fib_cached(
fib_for int
) RETURNS integer AS $$
DECLARE
ret integer;
BEGIN
if fib_for < 2 THEN
RETURN fib_for;
END IF;
19
Memoized Fibonacci
CREATE OR REPLACE FUNCTION fib_cached(
fib_for int
) RETURNS integer AS $$
DECLARE
ret integer;
BEGIN
if fib_for < 2 THEN
RETURN fib_for;
END IF;
19
Memoized Fibonacci
CREATE OR REPLACE FUNCTION fib_cached(
fib_for int
) RETURNS integer AS $$
DECLARE
ret integer;
BEGIN
if fib_for < 2 THEN
RETURN fib_for;
END IF;
19
Memoized Fibonacci
CREATE OR REPLACE FUNCTION fib_cached(
fib_for int
) RETURNS integer AS $$
DECLARE
ret integer;
BEGIN
if fib_for < 2 THEN
RETURN fib_for;
END IF;
19
Memoized Fibonacci
CREATE OR REPLACE FUNCTION fib_cached(
fib_for int
) RETURNS integer AS $$
DECLARE
ret integer;
BEGIN
if fib_for < 2 THEN
RETURN fib_for;
END IF;
19
Memoized Fibonacci
CREATE OR REPLACE FUNCTION fib_cached(
fib_for int
) RETURNS integer AS $$
DECLARE
ret integer;
BEGIN
if fib_for < 2 THEN
RETURN fib_for;
END IF;
19
Memoized Fibonacci
CREATE OR REPLACE FUNCTION fib_cached(
fib_for int
) RETURNS integer AS $$
DECLARE
ret integer;
BEGIN
if fib_for < 2 THEN
RETURN fib_for;
END IF;
19
Memoized Fibonacci
CREATE OR REPLACE FUNCTION fib_cached(
fib_for int
) RETURNS integer AS $$
DECLARE
ret integer;
BEGIN
if fib_for < 2 THEN
RETURN fib_for;
END IF;
19
Try It
20
Using SQL in PL/pgSQL
21
Using SQL in PL/pgSQL
Any SQL can be used
21
Using SQL in PL/pgSQL
Any SQL can be used
21
Using SQL in PL/pgSQL
Any SQL can be used
21
Using SQL in PL/pgSQL
Any SQL can be used
21
Using SQL in PL/pgSQL
Any SQL can be used
21
Using SQL in PL/pgSQL
Any SQL can be used
21
Looping Fibonacci
Function
22
Looping Fibonacci
Function
CREATE OR REPLACE FUNCTION fib_fast(
fib_for int
) RETURNS integer AS $$
DECLARE
ret integer := 0;
nxt integer := 1;
tmp integer;
BEGIN
FOR num IN 1..fib_for LOOP
tmp := ret;
ret := nxt;
nxt := tmp + nxt;
END LOOP;
RETURN ret;
END;
$$ LANGUAGE plpgsql strict;
22
Looping Fibonacci
Function
CREATE OR REPLACE FUNCTION fib_fast(
fib_for int
) RETURNS integer AS $$
DECLARE
ret integer := 0;
nxt integer := 1;
tmp integer;
BEGIN
FOR num IN 1..fib_for LOOP
tmp := ret;
ret := nxt;
nxt := tmp + nxt;
END LOOP;
RETURN ret;
END;
$$ LANGUAGE plpgsql strict;
22
Looping Fibonacci
Function
CREATE OR REPLACE FUNCTION fib_fast(
fib_for int
) RETURNS integer AS $$
DECLARE
ret integer := 0;
nxt integer := 1;
tmp integer;
BEGIN
FOR num IN 1..fib_for LOOP
tmp := ret;
ret := nxt;
nxt := tmp + nxt;
END LOOP;
RETURN ret;
END;
$$ LANGUAGE plpgsql strict;
22
Looping Fibonacci
Function
CREATE OR REPLACE FUNCTION fib_fast(
fib_for int
) RETURNS integer AS $$
DECLARE
ret integer := 0;
nxt integer := 1;
tmp integer;
BEGIN
FOR num IN 1..fib_for LOOP
tmp := ret;
ret := nxt;
nxt := tmp + nxt;
END LOOP;
RETURN ret;
END;
$$ LANGUAGE plpgsql strict;
22
Looping Fibonacci
Function
CREATE OR REPLACE FUNCTION fib_fast(
fib_for int
) RETURNS integer AS $$
DECLARE
ret integer := 0;
nxt integer := 1;
tmp integer;
BEGIN
FOR num IN 1..fib_for LOOP
tmp := ret;
ret := nxt;
nxt := tmp + nxt;
END LOOP;
RETURN ret;
END;
$$ LANGUAGE plpgsql strict;
22
Try It
23
Set Returning Functions
24
Set Returning Functions
Functions can return sets
24
Set Returning Functions
Functions can return sets
Similar to continuations
24
Set Returning Functions
Functions can return sets
Similar to continuations
24
Set Returning Functions
Functions can return sets
Similar to continuations
24
Set Returning Functions
Functions can return sets
Similar to continuations
24
Fibonacci Set
25
Fibonacci Set
CREATE OR REPLACE FUNCTION fib_fast(
fib_for int
) RETURNS integer AS $$
DECLARE
ret integer := 0;
nxt integer := 1;
tmp integer;
BEGIN
FOR num IN 1..fib_for LOOP
tmp := ret;
ret := nxt;
nxt := tmp + nxt;
END LOOP;
RETURN ret;
END;
$$ LANGUAGE plpgsql strict;
25
Fibonacci Set
CREATE OR REPLACE FUNCTION fibs_to(
fib_for int
) RETURNS SETOF integer AS $$
DECLARE
ret integer := 0;
nxt integer := 1;
tmp integer;
BEGIN
FOR num IN 1..fib_for LOOP
tmp := ret;
ret := nxt;
nxt := tmp + nxt;
END LOOP;
RETURN ret;
END;
$$ LANGUAGE plpgsql strict;
25
Fibonacci Set
CREATE OR REPLACE FUNCTION fibs_to(
fib_for int
) RETURNS SETOF integer AS $$
DECLARE
ret integer := 0;
nxt integer := 1;
tmp integer;
BEGIN
FOR num IN 1..fib_for LOOP
RETURN NEXT ret;
tmp := ret;
ret := nxt;
nxt := tmp + nxt;
END LOOP;
RETURN ret;
END;
$$ LANGUAGE plpgsql strict;
25
Fibonacci Set
CREATE OR REPLACE FUNCTION fibs_to(
fib_for int
) RETURNS SETOF integer AS $$
DECLARE
ret integer := 0;
nxt integer := 1;
tmp integer;
BEGIN
FOR num IN 1..fib_for LOOP
RETURN NEXT ret;
tmp := ret;
ret := nxt;
nxt := tmp + nxt;
END LOOP;
25
Try It
26
Yea, but What Good
are They?
27
Yea, but What Good
are They?
I’m glad you asked
27
Yea, but What Good
are They?
I’m glad you asked
27
Yea, but What Good
are They?
I’m glad you asked
27
A Blogging App’s Schema
28
A Blogging App’s Schema
CREATE TABLE entry (
id SERIAL PRIMARY KEY,
title TEXT,
content TEXT
);
28
A Blogging App’s Schema
CREATE TABLE entry (
id SERIAL PRIMARY KEY,
title TEXT,
content TEXT
);
28
A Blogging App’s Schema
CREATE TABLE entry (
id SERIAL PRIMARY KEY,
title TEXT,
content TEXT
);
28
A Blogging App’s Schema
CREATE TABLE entry (
id SERIAL PRIMARY KEY,
title TEXT,
content TEXT
);
28
SELECT Tags for an Entry
29
SELECT Tags for an Entry
29
Try It
SELECT tag.id, tag.name
FROM tag, entry_coll_tag
WHERE tag.id = entry_coll_tag.tag_id
AND entry_coll_tag.entry_id = 1
ORDER BY entry_coll_tag.tag_order;
30
Setting Tags
31
Setting Tags
# Use prepared statements.
insert = dbh.prepare('INSERT INTO entry (title, content) VALUES (?, ?)');
sel_id = dbh.prepare("SELECT CURRVAL('entry_id_seq')");
ins_coll = dbh.prepare('
INSERT INTO entry_coll_tag (entry_id, tag_id, tag_order)
VALUES (?, ?, ?)
');
# Make it so!
dbh.commit;
31
What’s Wrong with
That?
32
What’s Wrong with
That?
It’s Slow
32
What’s Wrong with
That?
It’s Slow
32
What’s Wrong with
That?
It’s Slow
32
What’s Wrong with
That?
It’s Slow
32
What’s Wrong with
That?
It’s Slow
32
What’s Wrong with
That?
It’s Slow
32
How can PL/pgSQL Help?
33
How can PL/pgSQL Help?
A function can associate tags with entries
33
How can PL/pgSQL Help?
A function can associate tags with entries
33
How can PL/pgSQL Help?
A function can associate tags with entries
33
Let’s Do It!
34
Let’s Do It!
CREATE OR REPLACE FUNCTION entry_coll_tag_set (
obj_id integer,
coll_ids integer[]
) RETURNS VOID AS $$
BEGIN
PERFORM true FROM entry WHERE id = obj_id FOR UPDATE;
UPDATE entry_coll_tag
SET tag_order = -tag_order
WHERE entry_id = obj_id
UPDATE entry_coll_tag
SET tag_order = iloop
WHERE entry_id = obj_id
AND tag_id = coll_ids[iloop];
34
Let’s Do It!
CREATE OR REPLACE FUNCTION entry_coll_tag_set (
obj_id integer,
coll_ids integer[]
) RETURNS VOID AS $$
BEGIN
PERFORM true FROM entry WHERE id = obj_id FOR UPDATE;
UPDATE entry_coll_tag
SET tag_order = -tag_order
WHERE entry_id = obj_id
UPDATE entry_coll_tag
SET tag_order = iloop
WHERE entry_id = obj_id
AND tag_id = coll_ids[iloop];
34
Let’s Do It!
CREATE OR REPLACE FUNCTION entry_coll_tag_set (
obj_id integer,
coll_ids integer[]
) RETURNS VOID AS $$
BEGIN
PERFORM true FROM entry WHERE id = obj_id FOR UPDATE;
UPDATE entry_coll_tag
SET tag_order = -tag_order
WHERE entry_id = obj_id
UPDATE entry_coll_tag
SET tag_order = iloop
WHERE entry_id = obj_id
AND tag_id = coll_ids[iloop];
34
Let’s Do It!
CREATE OR REPLACE FUNCTION entry_coll_tag_set (
obj_id integer,
coll_ids integer[]
) RETURNS VOID AS $$
BEGIN
PERFORM true FROM entry WHERE id = obj_id FOR UPDATE;
UPDATE entry_coll_tag
SET tag_order = -tag_order
WHERE entry_id = obj_id
UPDATE entry_coll_tag
SET tag_order = iloop
WHERE entry_id = obj_id
AND tag_id = coll_ids[iloop];
34
Let’s Do It!
CREATE OR REPLACE FUNCTION entry_coll_tag_set (
obj_id integer,
coll_ids integer[]
) RETURNS VOID AS $$
BEGIN
PERFORM true FROM entry WHERE id = obj_id FOR UPDATE;
UPDATE entry_coll_tag
SET tag_order = -tag_order
WHERE entry_id = obj_id
UPDATE entry_coll_tag
SET tag_order = iloop
WHERE entry_id = obj_id
AND tag_id = coll_ids[iloop];
34
Let’s Do It!
CREATE OR REPLACE FUNCTION entry_coll_tag_set (
obj_id integer,
coll_ids integer[]
) RETURNS VOID AS $$
BEGIN
PERFORM true FROM entry WHERE id = obj_id FOR UPDATE;
UPDATE entry_coll_tag
SET tag_order = -tag_order
WHERE entry_id = obj_id
UPDATE entry_coll_tag
SET tag_order = iloop
WHERE entry_id = obj_id
AND tag_id = coll_ids[iloop];
34
Let’s Do It!
CREATE OR REPLACE FUNCTION entry_coll_tag_set (
obj_id integer,
coll_ids integer[]
) RETURNS VOID AS $$
BEGIN
PERFORM true FROM entry WHERE id = obj_id FOR UPDATE;
UPDATE entry_coll_tag
SET tag_order = -tag_order
WHERE entry_id = obj_id
UPDATE entry_coll_tag
SET tag_order = iloop
WHERE entry_id = obj_id
AND tag_id = coll_ids[iloop];
34
Let’s Do It!
CREATE OR REPLACE FUNCTION entry_coll_tag_set (
obj_id integer,
coll_ids integer[]
) RETURNS VOID AS $$
BEGIN
PERFORM true FROM entry WHERE id = obj_id FOR UPDATE;
UPDATE entry_coll_tag
SET tag_order = -tag_order
WHERE entry_id = obj_id
UPDATE entry_coll_tag
SET tag_order = iloop
WHERE entry_id = obj_id
AND tag_id = coll_ids[iloop];
34
Let’s Do It!
CREATE OR REPLACE FUNCTION entry_coll_tag_set (
obj_id integer,
coll_ids integer[]
) RETURNS VOID AS $$
BEGIN
PERFORM true FROM entry WHERE id = obj_id FOR UPDATE;
UPDATE entry_coll_tag
SET tag_order = -tag_order
WHERE entry_id = obj_id
UPDATE entry_coll_tag
SET tag_order = iloop
WHERE entry_id = obj_id
AND tag_id = coll_ids[iloop];
34
Try It
35
Try It
SELECT entry_coll_tag_set(1, '{1,4,6,3}');
35
Learn More
My O’Reilly Articles
http://www.oreillynet.com/pub/au/1059
36
Thank You!
Learning PL/pgSQL
David Wheeler
Kineticode
37