本章将讨论 PL/pgSQL 的控制结构,包括:IF、CASE、LOOP、WHILE LOOP、FOR LOOP、EXIT和CONTINUE等语句。
IF
语句
PL/pgSQL 提供了三种形式的 if
语句:
IF THEN
IF THEN ELSE
IF THEN ELSIF
IF THEN
语句
它的最简单形式是:
if 条件 then
语句;
end if;
示例:
do $$
declare
selected_film film%rowtype;
input_film_id film.film_id%type := 0;
begin
select * from film
into selected_film
where film_id = input_film_id;
if not found then
raise notice'The film % could not be found',
input_film_id;
end if;
end $$;
found
是 PL/pgSQL 中的全局变量,如果分配了行,则SELECT INTO
语句将found
设置为true
;否则,将found
设置为false
IF-THEN-ELSE
语句
语法如下:
if 条件 then
语句;
else
其它语句;
END if;
示例:
do $$
declare
selected_film film%rowtype;
input_film_id film.film_id%type := 100;
begin
select * from film
into selected_film
where film_id = input_film_id;
if not found then
raise notice 'The film % could not be found',
input_film_id;
else
raise notice 'The film title is %', selected_film.title;
end if;
end $$;
IF-THEN-ELSIF
语句
语法如下:
if 条件1 then
语句1;
elsif 条件2 then
语句2;
...
elsif 条件n then
语句n;
else
其它语句;
end if;
示例:
do $$
declare
v_film film%rowtype;
len_description varchar(100);
begin
select * from film
into v_film
where film_id = 100;
if not found then
raise notice 'Film not found';
else
if v_film.length >0 and v_film.length <= 50 then
len_description := 'Short';
elsif v_film.length > 50 and v_film.length < 120 then
len_description := 'Medium';
elsif v_film.length > 120 then
len_description := 'Long';
else
len_description := 'N/A';
end if;
raise notice 'The % film is %.',
v_film.title,
len_description;
end if;
end $$;
CASE
语句
CASE
语句根据条件从 WHEN
列表中选择要执行的部分,它有两种形式:
- 简单的
CASE
语句 - 搜索型
CASE
语句
不要混淆
CASE
语句和CASE
表达式。
case 计算表达式
when 表达式1 [, 表达式2, ...] then
语句
[ ... ]
[else
其它语句 ]
END case;
示例:
do $$
declare
rate film.rental_rate%type;
price_segment varchar(50);
begin
select rental_rate into rate
from film
where film_id = 100;
if found then
case rate
when 0.99 then
price_segment = 'Mass';
when 2.99 then
price_segment = 'Mainstream';
when 4.99 then
price_segment = 'High End';
else
price_segment = 'Unspecified';
end case;
raise notice '%', price_segment;
end if;
end $$;
搜索型CASE
语句
语法:
case
when 布尔表达式1 then
语句1
[ when 布尔表达式2 then
语句2
... ]
[ else
其它语句 ]
end case;
示例:
do $$
declare
total_payment numeric;
service_level varchar(25) ;
begin
select sum(amount) into total_payment
from Payment
where customer_id = 100;
if found then
case
when total_payment > 200 then
service_level = 'Platinum' ;
when total_payment > 100 then
service_level = 'Gold' ;
else
service_level = 'Silver' ;
end case;
raise notice 'Service Level: %', service_level;
else
raise notice 'Customer not found';
end if;
end $$;
LOOP
语句
LOOP
定义一个无限循环,直到被 exit
或 return
语句终止。语法如下:
<<标签>>
loop
语句;
end loop;
通常会在循环体中使用 IF
来根据条件终止循环:
<<标签>>
loop
语句;
if 条件 then
exit;
end if;
end loop;
示例:计算斐波那契数列:
do $$
declare
n integer:= 10;
fib integer := 0;
counter integer := 0 ;
i integer := 0 ;
j integer := 1 ;
begin
if (n < 1) then
fib := 0 ;
end if;
loop
exit when counter = n ;
counter := counter + 1 ;
select j, i + j into i, j ;
end loop;
fib := i;
raise notice '%', fib;
end $$;
WHILE LOOP
语句
WHILE LOOP
语句循环执行代码块,直到条件计算结果为 false
,语法如下:
[ <<标签>> ]
while 条件 loop
语句;
end loop;
示例:
do $$
declare
counter integer := 0;
begin
while counter < 5 loop
raise notice 'Counter %', counter;
counter := counter + 1;
end loop;
end$$;
FOR LOOP
语句
用于迭代一系列整数值或查询的结果集。
语法一:迭代一系列整数值:
[ <<label>> ]
for loop_counter in [ reverse ] from.. to [ by step ] loop
statements
end loop [ label ];
示例:
do $$
begin
for counter in 1..5 loop
raise notice 'counter: %', counter;
end loop;
end $$;
语法二:迭代查询结果集:
[ <<label>> ]
for target in query loop
statements
end loop [ label ];
示例:
do
$$
declare
f record;
begin
for f in select title, length
from film
order by length desc, title
limit 10
loop
raise notice '%(% mins)', f.title, f.length;
end loop;
end $$;
语法三:迭代动态查询的结果集:
[ <<label>> ]
for row in execute query_expression [ using query_param [, ... ] ]
loop
statements
end loop [ label ];
示例:
do $$
declare
-- 排序: 1: 标题, 2: 年份
sort_type smallint := 1;
-- 电影数量
rec_count int := 10;
-- 用于迭代每个记录
rec record;
-- 动态查询
query text;
begin
query := 'select title, release_year from film ';
if sort_type = 1 then
query := query || 'order by title';
elsif sort_type = 2 then
query := query || 'order by release_year';
else
raise 'invalid sort type %s', sort_type;
end if;
query := query || ' limit $1';
for rec in execute query using rec_count
loop
raise notice '% - %', rec.release_year, rec.title;
end loop;
end $$;
EXIT
语句
用于终止各种循环,语法:
exit [label] [when boolean_expression]
以下语句是等效的:
EXIT WHEN counter > 10;
-- 等效于
IF counter > 10 THEN
EXIT;
END IF;
<<block_label>>
BEGIN
-- some code
EXIT [block_label] [WHEN condition];
-- some more code
END block_label;
示例:
do $$
declare
i int = 0;
j int = 0;
begin
<<outer_loop>>
loop
i = i + 1;
exit when i > 3;
-- inner loop
j = 0;
<<inner_loop>>
loop
j = j + 1;
exit when j > 3;
raise notice '(i,j): (%,%)', i, j;
end loop inner_loop;
end loop outer_loop;
end $$;
示例二:
CONTINUE
语句
用于结束当前循环,跳转到下一个循环。语法:
continue [loop_label] [when condition]
示例:打印10以内的奇数
do
$$
declare
counter int = 0;
begin
loop
counter = counter + 1;
exit when counter > 10;
continue when mod(counter,2) = 0;
raise notice '%', counter;
end loop;
end;
$$