PostgreSQL的存储过程简单入门

本文详细介绍了 PostgreSQL 存储过程的结构、变量类型、连接字符、控制结构及异常捕获,通过示例代码展示了如何批量插入数据,并强调了在操作过程中避免时间指定为同一时间的重要性。

一、存储过程结构:

         Create or replace function 过程名(参数名 参数类型,…..) returns 返回值类型 as

                   $body$

 

                            //声明变量

                            Declare

                            变量名变量类型;

                            如:

                            flag Boolean;

 

                            变量赋值方式(变量名类型 :=值;)

                            如:

                            str  text :=值; / str  text;  str :=值;

 

                            Begin

                                     函数体;

 

                             return 变量名; //存储过程中的返回语句

 

                            End;

                   $body$

         Language plpgsql;

 

二、变量类型 :

除了postgresql内置的变量类型外,常用的还有 RECORD ,表示一条记录。

 

整数数据类型:

子类型

标准名

描述

Smalll integer

Smallint

一个2字节的符号型整数,可以存储-32768到32767的数字

Integer

Int

一个4字节的符号型整数,可以存储-2147483648到2147473647的数字

Serial

和integer一样,除了它的值通常是由PostgreSQL自动输入的。

 

浮点数据类型:(浮点数据也可以再细分,分为提供通用功能的浮点值和固定精度的数字)

子类型

标准名

描述

float

float(n)

支持最少精度为n,存储为最多8字节的浮点数。

float8

real

双精度(8字节)浮点数字

numeric

numeric(p,s)

拥有p个数字的实数,其中小数点后有s位。不像float,这始终是一个确切的数字,但工作效率比普通浮点数字低。

money

numeric(9,2)

PostgreSQL特有的类型,但在其他数据库里也普遍存在。Money类型从PostgreSQL 8.0开始不赞成使用,且可能在以后版本中取消。你应该使用number类型代替。

注:

存储floatreal类型的数据的行为非常相似,但是numeric列的行为有点不同。Numeric类型不是存储接近的数,而是在小数后面进行后超出固定长度的部分进行四舍五入。如果我们存储太大的数据到其中,INSERT将失败。还要注意floatreal也会对数字四舍五入;例如123.456789被四舍五入为123.457

 

时间数据类型:

定义

意义

date

存储日期信息

time

存储时间信息

timestamp

存储日期和时间

interval

存储timestamp之间差别的信息

timestamptz

PostgreSQL扩展的类型,存储包含时区信息的timestamp

 

特殊数据类型:

定义

意义

box

矩形盒子

line

一组点

point

一对几何学的数字

lseg

一条线段

polygon

一条封闭的几何线

cidr或inet

一个IPv4的地址,录入192.168.0.1

macaddr

以MAC地址(以太网卡物理地址)

注:PostgreSQL也允许你使用SQL命令CREATE TYPE在数据库中建立你自己的类型。这通常不需要,而且在一定程度上,它是PostgreSQL独有的。


数组

通常,一个数组需要通过使用一个附加表实现。但是,数组的能力有时候很有用。建立数组的方法有两种:传统的PostgreSQL的方法和SQL99标准的方法。


PostgreSQL样式的数组

要将一个表的列定义为数组,你可以简单地在类型后面添加[];不需要定义元素的个数。即使定义了个数,也不会强制要求存储的个数。

Eg

test=> CREATE TABLE empworkday (

test(>     refcode char(5),

test(>     workdays int[]

test(> );

往数组列中插入值:

test=> INSERT INTO empworkday VALUES(‘val01′,‘{0,1,0,1,1,1,1}’);

test=> INSERT INTO empworkday VALUES(‘val02′,‘{0,1,1,1,1,0,1}’);

 

SQL99样式的数字

SQL99标准中,必须指出元素的个数。

Eg:

test=> CREATE TABLE empworkday (

test(>     refcode char(5),

test(>     workdays int array[7]

test(> );

test=> INSERT INTO empworkday VALUES(‘val01′,‘{0,1,0,1,1,1,1}’);

test=> INSERT INTO empworkday VALUES(‘val02′,‘{0,1,1,1,1,0,1}’);

 

三、连接字符:

         Postgresql存储过程中的连接字符不再是“+”,而是使用“||”。

四、  控制结构:

1、if 条件(五种形式)

IF ... THEN

IF ... THEN ... ELSE

IF ... THEN ... ELSE IF

IF ... THEN ... ELSIF ... THEN ... ELSE

IF ... THEN ... ELSEIF ... THEN ... ELSE(注:ELSEIF 是ELSIF 的别名)

2、循环

使用LOOP,EXIT,CONTINUE,WHILE, 和FOR 语句,可以控制PL/pgSQL 函数重复一系列命令。

1)、LOOP

[ <<label>> ]
LOOP
    statements
END LOOP [ label ];

LOOP 定义一个无条件的循环,无限循环, 直到由EXIT或者RETURN语句终止。可选的label 可以由EXIT 和CONTINUE 语句使用, 用于在嵌套循环中声明应该应用于哪一层循环。

2)、EXIT

	EXIT [ label ] [ WHEN expression ];
	如果没有给出label, 那么退出最内层的循环,然后执行跟在 END LOOP 后面的语句。 如果给出 label, 那么它必须是当前或者更高层的嵌套循环块或者语句块的标签。然后该命名块或者循环就会终止,而控制落到对应循环/块的 END 语句后面的语句上。

如果声明了WHEN,循环退出只有在expression 为真的时候才发生, 否则控制会落到EXIT 后面的语句上。

EXIT 可以用于在所有的循环类型中,它并不仅仅限制于在无条件循环中使用。在和 BEGIN 块一起使用的时候,EXIT 把控制交给块结束后的下一个语句。

例如:

Loop  循环

If … then  条件判断

Exit ; 条件成立,则退出循环。

End if;

End loop;

3)、CONTINUE

CONTINUE [label ] [ WHENexpression ];

如果没有给出 label,那么就开始最内层的循环的下一次执行。也就是说,控制传递回给循环控制表达式(如果有),然后重新计算循环体。 如果出现了label,它声明即将继续执行的循环的标签。

如果声明了 WHEN,那么循环的下一次执行只有在expression 为真的情况下才进行。否则,控制传递给CONTINUE 后面的语句。

CONTINUE 可以用于所有类型的循环; 它并不仅仅限于无条件循环。

例如:

LOOP
    一些计算
    EXIT WHEN count > 100;
    CONTINUE WHEN count < 50;
    一些在count 数值在 [50 .. 100] 里面时候的计算
END LOOP;
 
4)、WHILE
	[ <<label>> ]
	WHILE expression LOOP
    	statements
	END LOOP [ label ];
只要条件表达式为真,WHILE语句就会不停在一系列语句上进行循环. 条件是在每次进入循环体的时候检查的.
例如:
WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP
    -- 可以在这里做些计算
END LOOP;
WHILE NOT BOOLEAN_expression LOOP
    -- 可以在这里做些计算
END LOOP;
 
5)、FOR(整数变种)
	[ <<label>> ]
	FOR name IN [ REVERSE ] expression .. expression LOOP
    	statements
	END LOOP [ labal ];

这种形式的FOR对一定范围的整数数值进行迭代的循环。变量name 会自动定义为integer类型并且只在循环里存在。给出范围上下界的两个表达式在进入循环的时候计算一次。 迭代步进值总是为 1,但如果声明了REVERSE就是 -1。

一些整数FOR循环的例子∶

FOR i IN 1..10 LOOP 表示1循环到10
  这里可以放一些表达式
    RAISE NOTICE 'i IS %', i;
END LOOP;
 
FOR i IN REVERSE 10..1 LOOP
    这里可以放一些表达式
END LOOP;

如果下界大于上界(或者是在 REVERSE 情况下是小于),那么循环体将完全不被执行。而且不会抛出任何错误。

 

 

3、异常捕获

        EXCEPTION

WHEN 错误码(如:STRING_DATA_RIGHT_TRUNCATION:字串数据右边被截断) THEN

        /**后台打印错误信息*/

        RAISE NOTICE '错吴信息';

 

五、示例代码:

/**

批量插入一批数据,经纬度字段值要满足中国地理位置上的经纬度范围;

注:时间不能指定为同一时间,否则会扫描全表,导致性能低下。下列脚本未考虑时间的分段,采用的一个时间点。

*/

create orreplace function intobatch() returns integer as

$body$

declare

    skyid integer;

    lot float;

         lat float;

         sex varchar;

         level integer;

         ctime int :=1325404914;

         num integer :=0;

         total integer :=0;

    begin

                   lot='73.6666666';

                   lat='3.8666666';

                   FOR skyid IN 404499817 ..404953416 loop

                             if(lot > 135.0416666) then

             lot=73.6666666;

                             end if;

                        if(lat > 53.5500000) then

             lat=3.8666666;

                             end if;

                             if(skyid%2 <> 0) then

                                                        sex='1';

                                                        level=0;

                             else

                                                        sex='2';

                                                        level=1;

                             end if;

 

                            INSERT INTO user_last_location(user_id,app_id,lonlat,sex,accurate_level,lonlat_point,create_time)

 

VALUES(skyid,2934,ST_GeomFromText('POINT('||lot||' '||lat||')',4326),sex,level,POINT(lot,lat),to_timestamp(ctime));

                           

                            lot=lot+0.1;

                            lat=lat+0.1;

                            skyid=skyid+1;

                           

        end loop;        

                   return skyid;  

    end

$body$

languageplpgsql;

 

SELECT *from intobatch();

 

<think> 我们正在讨论PostgreSQL存储过程如何输出数据集(表形式)。根据引用内容,用户提到想要以表形式输出,而引用[1]中展示了存储过程输出参数,但用户希望输出表形式。引用[2]则给出了存储过程的基本语法模板。 在PostgreSQL中,存储过程(procedure)通常用于执行操作而不返回结果,但用户想要返回数据集(表形式),这实际上应该使用函数(function),因为函数可以返回一个表。然而,从PostgreSQL 11开始,存储过程(PROCEDURE)被引入,但它主要用于执行事务控制(如COMMIT),并且不返回值。返回结果集(表)的传统方式是使用函数(FUNCTION)。 因此,我们可以这样回答: 1. 说明存储过程(PROCEDURE)和函数(FUNCTION)在返回结果集上的区别。 2. 展示如何使用函数(FUNCTION)返回一个表(数据集)。 3. 如果用户坚持使用存储过程(PROCEDURE),则说明存储过程本身不支持直接返回结果集,但可以通过输出参数(如refcursor)来实现,但这通常需要在事务中操作,并且客户端需要处理游标。 根据引用[1]中的例子,用户尝试了输出参数,但得到的是在一个表格里(可能是单行数据),而用户想要表形式(多行数据)。因此,我们可以提供两种方法: 方法一:使用函数(FUNCTION)返回表 ```sql CREATE OR REPLACE FUNCTION get_books() RETURNS TABLE (bookname VARCHAR, price NUMERIC) AS $$ BEGIN RETURN QUERY SELECT bookname, price FROM repertory; END; $$ LANGUAGE plpgsql; ``` 然后调用:SELECT * FROM get_books(); 方法二:使用存储过程(PROCEDURE)和游标(refcursor) 但是注意,存储过程不能直接返回表,但可以返回一个游标。在事务中打开游标,然后客户端可以获取游标中的数据。 例如: ```sql CREATE OR REPLACE PROCEDURE get_books_proc(INOUT myref refcursor) LANGUAGE plpgsql AS $$ BEGIN OPEN myref FOR SELECT bookname, price FROM repertory; END; $$; ``` 在事务中调用: ```sql BEGIN; CALL get_books_proc('mycursor'); FETCH ALL IN mycursor; COMMIT; ``` 或者,在客户端(如psql)中,使用游标获取数据。 由于用户要求存储过程输出数据集的方法,我们重点介绍这两种方法,并说明推荐使用函数。 另外,注意引用[2]中存储过程的语法模板,我们可以参考其结构。 因此,回答如下: 在PostgreSQL中,存储过程(PROCEDURE)通常用于执行操作而不返回结果集。如果您需要返回数据集(表形式),有两种主要方法: ### 方法一:使用函数(FUNCTION)返回表 这是最直接的方法。您可以创建一个返回表的函数,然后在查询中调用它。 示例:创建一个函数,返回`repertory`表中的`bookname`和`price`列。 ```sql CREATE OR REPLACE FUNCTION get_books() RETURNS TABLE ( book_name VARCHAR, book_price NUMERIC ) LANGUAGE plpgsql AS $$ BEGIN RETURN QUERY SELECT bookname, price FROM repertory; END; $$; ``` 调用函数: ```sql SELECT * FROM get_books(); ``` ### 方法二:使用存储过程(PROCEDURE)和游标(REF CURSOR) 如果您必须使用存储过程,可以通过游标输出。存储过程可以打开一个游标,然后客户端通过该游标获取数据。但注意,这种方法需要在事务中操作。 示例: ```sql CREATE OR REPLACE PROCEDURE get_books_proc(INOUT ref refcursor) LANGUAGE plpgsql AS $$ BEGIN OPEN ref FOR SELECT bookname, price FROM repertory; END; $$; ``` 在事务中调用存储过程并获取游标数据(在psql中): ```sql BEGIN; CALL get_books_proc('mycursor'); -- 打开游标 FETCH ALL IN mycursor; -- 获取游标中的所有数据 COMMIT; ``` ### 说明 - **函数(FUNCTION)** 更适合返回结果集,因为它可以直接在SELECT语句中使用。 - **存储过程(PROCEDURE)** 主要用于执行事务性操作(支持COMMIT/ROLLBACK),返回结果集不是其设计初衷。如果需要返回结果集,通常建议使用函数。 如果您有特定原因必须使用存储过程(例如需要执行事务控制),则可以使用游标方式。否则,函数是更简单直接的选择[^1][^2]。 §§相关问题§§ 1. PostgreSQL中函数和存储过程的主要区别是什么? 2. 如何通过PostgreSQL函数返回多个结果集? 3. 在PostgreSQL中如何处理存储过程中的事务? </think> ### PostgreSQL存储过程输出数据集的方法 在PostgreSQL中,存储过程(PROCEDURE)本身不直接返回结果集,但可以通过以下两种主要方法实现数据集输出: #### 方法一:使用函数(FUNCTION)返回表 ```sql CREATE OR REPLACE FUNCTION get_books_data() RETURNS TABLE ( book_name VARCHAR, book_price NUMERIC ) LANGUAGE plpgsql AS $$ BEGIN RETURN QUERY SELECT bookname, price FROM repertory; -- 直接返回查询结果集 END; $$; ``` 调用方式: ```sql SELECT * FROM get_books_data(); -- 以表形式输出结果 ``` #### 方法二:使用存储过程(PROCEDURE)+ 输出参数 ```sql CREATE OR REPLACE PROCEDURE export_books_data( INOUT result_ref REFCURSOR -- 声明游标类型输出参数 ) LANGUAGE plpgsql AS $$ BEGIN OPEN result_ref FOR -- 打开游标绑定结果集 SELECT bookname, price FROM repertory; END; $$; ``` 调用方式: ```sql BEGIN; CALL export_books_data('mycursor'); -- 调用存储过程 FETCH ALL FROM mycursor; -- 提取游标数据 COMMIT; ``` #### 关键区别: | **特性** | 函数(FUNCTION) | 存储过程(PROCEDURE) | |----------------|--------------------------------|-----------------------------| | **返回类型** | 直接返回表/结果集 | 需通过REF CURSOR输出参数 | | **调用方式** | `SELECT * FROM func_name()` | `CALL proc_name()` + `FETCH` | | **事务控制** | 不能包含事务控制语句 | 支持COMMIT/ROLLBACK | | **结果获取** | 直接作为结果集输出 | 需额外处理游标 | > **实际建议**: > 如果只需输出结果集,优先使用**函数(FUNCTION)**(方法一),语法更简洁; > 若需要事务控制(如更新后返回结果),使用**存储过程+游标**(方法二)[^1][^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值