This slide is based on Jeffrey D. Ullman's work, which can be download from his website.
我们之前所看到的仅是 SQL 在
现实情况几乎总是不同的: 常规程序与 SQL 进行交互.
可以做那些单用
存储过程对于 MySQL 来说还是比较新的功能. 但应该要开始考虑把现有的程序转移到存储过程中来. 因为有下面几点原因:
It often happens that in carrying out a database operation, a large amount of data must be transported back and forth between the PHP program and the database server: The PHP program executes a SELECT command, processes the result, executes an UPDATE command based on the results, and returns LAST_INSERT_ID, etc. If all of these steps can be executed on the server in an SP, a great deal of overhead in data transmission can be saved.
触发器是
CREATE PROCEDURE <name> (
<parameter list> )
<optional local declarations>
<body>;
函数
CREATE FUNCTION <name> (
<parameter list> ) RETURNS <type>
<optional local declarations>
<body>;
函数的定义与过程相仿, 区别是使用保留字
一般的编程语言(如: C 语言), 过程或函数的参数使用的是
我们来写一个过程, 用到两个参数
CREATE PROCEDURE JoeMenu ( IN b CHAR(20), IN p REAL ) INSERT INTO Sells VALUES ('Joe''s Bar',b,p);
mysql> CREATE PROCEDURE JoeMenu ( -> IN b CHAR(20), -> IN p REAL -> ) -> INSERT INTO Sells -> VALUES ('Joe''s Bar',b,p); Query OK, 0 rows affected (0.37 sec)
使用
CALL JoeMenu('Moose Drool', 5.00);
注意: 这里不允许调用函数. 在 PSM 中调用函数与在 C 中一样: 使用函数名和匹配的参数作为表达式的一部分. 也就是说, 这里函数是用在
mysql> CALL JoeMenu('Stella Artois', 5.00); Query OK, 1 row affected (0.42 sec)
当使用
ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)
mysql> show variables like "log_bin_trust_function_creators"; +---------------------------------+-------+ | Variable_name | Value | +---------------------------------+-------+ | log_bin_trust_function_creators | OFF | +---------------------------------+-------+ 1 row in set, 1 warning (0.05 sec)
则我们应修改 MySQL 的配置文件, Windows 下是
然后重新启动 MySQL.
mysql> select count(*) from Frequents; +----------+ | count(*) | +----------+ | 18 | +----------+ 1 row in set (0.00 sec)
mysql> delimiter // mysql> create function count() -> returns int -> return (select count(*) from Frequents)// ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'count() returns int return (select count(*) from Frequents)' at line 1
这里
mysql> delimiter // mysql> create function countF() -> returns int -> return (select count(*) from Frequents)// Query OK, 0 rows affected (0.00 sec) mysql> delimiter ; mysql> select countF(); +----------+ | countF() | +----------+ | 18 | +----------+ 1 row in set (0.00 sec)
说明函数名称不能使用 MySQL 内部的函数名称
mysql> delimiter // mysql> create function avg() -> returns int -> return (select count(*) from Frequents)// Query OK, 0 rows affected (0.00 sec)
mysql> show create function avg\G; *************************** 1. row *************************** Function: avg sql_mode: Create Function: CREATE DEFINER=`haifeng`@`localhost` FUNCTION `avg`() RETURNS int(11) return (select count(*) from Frequents) character_set_client: utf8 collation_connection: utf8_general_ci Database Collation: utf8_general_ci 1 row in set (0.00 sec) ERROR: No query specified
mysql> delimiter ; mysql> select avg(); ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1 mysql> select test_bar.avg(); +----------------+ | test_bar.avg() | +----------------+ | 18 | +----------------+ 1 row in set (0.00 sec)
mysql> delimiter // mysql> create function countF2() -> returns int; -> return (select count(*) from Frequents)// ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '; return (select count(*) from Frequents)' at line 2
说明
使用
这里
mysql> show create function test_bar.countF\G; *************************** 1. row *************************** Function: countF sql_mode: Create Function: CREATE DEFINER=`haifeng`@`localhost` FUNCTION `countF`() RETURNS int(11) return (select count(*) from Frequents) character_set_client: utf8 collation_connection: utf8_general_ci Database Collation: utf8_general_ci 1 row in set (0.00 sec) ERROR: No query specified
或使用
查看当前数据库中定义的所有函数
mysql> show function status like '%'\G; *************************** 1. row *************************** Db: test_bar Name: Rate Type: FUNCTION Definer: haifeng@localhost Modified: 2013-10-30 09:35:35 Created: 2013-10-30 09:35:35 Security_type: DEFINER Comment: character_set_client: utf8 collation_connection: utf8_general_ci Database Collation: utf8_general_ci *************************** 2. row *************************** Db: test_bar Name: shorten Type: FUNCTION Definer: haifeng@localhost Modified: 2013-10-29 09:13:17 Created: 2013-10-29 09:13:17 Security_type: DEFINER Comment: character_set_client: utf8 collation_connection: utf8_general_ci Database Collation: utf8_general_ci 2 rows in set (0.00 sec) ERROR: No query specified
DROP {PROCEDURE | FUNCTION} [IF EXISTS] sp_name
mysql> drop function if exists avg; Query OK, 0 rows affected (0.00 sec)
mysql> delimiter // mysql> create function Rate(b char(20)) -> returns char(10) -> declare c int; -> begin -> set c=1; -> return c; -> end// ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'declare c int; begin set c=1; return c; end' at line 3
mysql> create function Rate(b char(20)) -> returns char(10) -> declare c int; -> set c=1; -> return c; // ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'declare c int; set c=1; return c' at line 1
mysql> create function Rate(b char(20)) -> returns char(10) -> begin -> declare c int; -> set c=1; -> return c; -> end // Query OK, 0 rows affected (0.00 sec)
因此用于变量声明的
MySQL 中存储过程和存储函数的信息都保存在数据库
可使用通常的
SELECT * FROM information_schema.Routines WHERE ROUTINE_NAME ='sp_name';
mysql> select * from information_schema.Routines\G; *************************** 1. row *************************** SPECIFIC_NAME: JoeMenu ROUTINE_CATALOG: def ROUTINE_SCHEMA: test_bar ROUTINE_NAME: JoeMenu ROUTINE_TYPE: PROCEDURE DATA_TYPE: CHARACTER_MAXIMUM_LENGTH: NULL CHARACTER_OCTET_LENGTH: NULL NUMERIC_PRECISION: NULL NUMERIC_SCALE: NULL CHARACTER_SET_NAME: NULL COLLATION_NAME: NULL DTD_IDENTIFIER: NULL ROUTINE_BODY: SQL ROUTINE_DEFINITION: INSERT INTO Sells VALUES ('Joe's Bar',b,p) EXTERNAL_NAME: NULL EXTERNAL_LANGUAGE: NULL PARAMETER_STYLE: SQL IS_DETERMINISTIC: NO SQL_DATA_ACCESS: CONTAINS SQL SQL_PATH: NULL SECURITY_TYPE: DEFINER CREATED: 2013-10-22 11:23:04 LAST_ALTERED: 2013-10-22 11:23:04 SQL_MODE: ROUTINE_COMMENT: DEFINER: haifeng@localhost CHARACTER_SET_CLIENT: utf8 COLLATION_CONNECTION: utf8_general_ci DATABASE_COLLATION: utf8_general_ci *************************** 2. row *************************** SPECIFIC_NAME: JoeMenu2 ROUTINE_CATALOG: def ROUTINE_SCHEMA: test_bar ROUTINE_NAME: JoeMenu2 ROUTINE_TYPE: PROCEDURE DATA_TYPE: CHARACTER_MAXIMUM_LENGTH: NULL CHARACTER_OCTET_LENGTH: NULL NUMERIC_PRECISION: NULL NUMERIC_SCALE: NULL CHARACTER_SET_NAME: NULL COLLATION_NAME: NULL DTD_IDENTIFIER: NULL ROUTINE_BODY: SQL ROUTINE_DEFINITION: insert into Sells values('Joe's Bar',b,p) EXTERNAL_NAME: NULL EXTERNAL_LANGUAGE: NULL PARAMETER_STYLE: SQL IS_DETERMINISTIC: NO SQL_DATA_ACCESS: CONTAINS SQL SQL_PATH: NULL SECURITY_TYPE: DEFINER CREATED: 2013-10-29 10:15:39 LAST_ALTERED: 2013-10-29 10:15:39 SQL_MODE: ROUTINE_COMMENT: DEFINER: haifeng@localhost CHARACTER_SET_CLIENT: utf8 COLLATION_CONNECTION: utf8_general_ci DATABASE_COLLATION: utf8_general_ci *************************** 3. row *************************** SPECIFIC_NAME: Rate ROUTINE_CATALOG: def ROUTINE_SCHEMA: test_bar ROUTINE_NAME: Rate ROUTINE_TYPE: FUNCTION DATA_TYPE: char CHARACTER_MAXIMUM_LENGTH: 10 CHARACTER_OCTET_LENGTH: 30 NUMERIC_PRECISION: NULL NUMERIC_SCALE: NULL CHARACTER_SET_NAME: utf8 COLLATION_NAME: utf8_general_ci DTD_IDENTIFIER: char(10) ROUTINE_BODY: SQL ROUTINE_DEFINITION: BEGIN DECLARE cust INT; SET cust = (SELECT COUNT(*) FROM Frequents WHERE bar=b); IF cust < 100 THEN RETURN 'unpopular'; ELSEIF cust < 200 THEN RETURN 'average'; ELSE RETURN 'popular'; END IF; END EXTERNAL_NAME: NULL EXTERNAL_LANGUAGE: NULL PARAMETER_STYLE: SQL IS_DETERMINISTIC: NO SQL_DATA_ACCESS: CONTAINS SQL SQL_PATH: NULL SECURITY_TYPE: DEFINER CREATED: 2013-10-30 09:35:35 LAST_ALTERED: 2013-10-30 09:35:35 SQL_MODE: ROUTINE_COMMENT: DEFINER: haifeng@localhost CHARACTER_SET_CLIENT: utf8 COLLATION_CONNECTION: utf8_general_ci DATABASE_COLLATION: utf8_general_ci *************************** 4. row *************************** SPECIFIC_NAME: shorten ROUTINE_CATALOG: def ROUTINE_SCHEMA: test_bar ROUTINE_NAME: shorten ROUTINE_TYPE: FUNCTION DATA_TYPE: varchar CHARACTER_MAXIMUM_LENGTH: 255 CHARACTER_OCTET_LENGTH: 765 NUMERIC_PRECISION: NULL NUMERIC_SCALE: NULL CHARACTER_SET_NAME: utf8 COLLATION_NAME: utf8_general_ci DTD_IDENTIFIER: varchar(255) ROUTINE_BODY: SQL ROUTINE_DEFINITION: BEGIN IF isNULL(s) THEN RETURN ''; ELSEIF n<15 THEN RETURN LEFT(s,n); ELSE IF CHAR_LENGTH(s)<=n THEN RETURN s; ELSE RETURN CONCAT(LEFT(s,n-10),'...',RIGHT(s,5)); END IF; END IF; END EXTERNAL_NAME: NULL EXTERNAL_LANGUAGE: NULL PARAMETER_STYLE: SQL IS_DETERMINISTIC: NO SQL_DATA_ACCESS: CONTAINS SQL SQL_PATH: NULL SECURITY_TYPE: DEFINER CREATED: 2013-10-29 09:13:17 LAST_ALTERED: 2013-10-29 09:13:17 SQL_MODE: ROUTINE_COMMENT: DEFINER: haifeng@localhost CHARACTER_SET_CLIENT: utf8 COLLATION_CONNECTION: utf8_general_ci DATABASE_COLLATION: utf8_general_ci 4 rows in set (0.00 sec) ERROR: No query specified
简单形式:
IF <condition> THEN <statement(s)> END IF
如果需要, 加入
IF <condition> THEN <statement(s)> ELSE <statement(s)> END IF
若要加入额外的情形, 则使用
IF <condition> THEN <statement(s)> ELSEIF <condition> THEN <statement(s)> ELSE <statement(s)> END IF
我们来给各个酒吧评一下等级, 根据它们所拥有的顾客数. 这是基于关系
函数
CREATE FUNCTION Rate(IN b CHAR(20)) RETURNS CHAR(10) DECLARE cust INTEGER; BEGIN SET cust = (SELECT COUNT(*) FROM Frequents WHERE bar=b); IF cust < 100 THEN RETURN 'unpopular' ELSEIF cust < 200 THEN RETURN 'average' ELSE RETURN 'popular' END IF END;
正确的语法是
delimiter $$ CREATE FUNCTION Rate(b CHAR(20)) RETURNS CHAR(10) BEGIN DECLARE cust INT; SET cust = (SELECT COUNT(*) FROM Frequents WHERE bar=b); IF cust < 100 THEN RETURN 'unpopular'; ELSEIF cust < 200 THEN RETURN 'average'; ELSE RETURN 'popular'; END IF; END$$
mysql> delimiter $$ mysql> CREATE FUNCTION Rate(b CHAR(20)) -> RETURNS CHAR(10) -> BEGIN -> DECLARE cust INT; -> SET cust = (SELECT COUNT(*) FROM Frequents -> WHERE bar=b); -> IF cust < 100 THEN RETURN 'unpopular'; -> ELSEIF cust < 200 THEN RETURN 'average'; -> ELSE RETURN 'popular'; -> END IF; -> END$$ Query OK, 0 rows affected (0.00 sec)
mysql> select Rate("HardRock"); +------------------+ | Rate("HardRock") | +------------------+ | unpopular | +------------------+ 1 row in set (0.00 sec)
mysql> show create function Rate\G; *************************** 1. row *************************** Function: Rate sql_mode: Create Function: CREATE DEFINER=`haifeng`@`localhost` FUNCTION `Rate`(b CHAR(20)) RETURNS char(10) CHARSET utf8 BEGIN DECLARE cust INT; SET cust = (SELECT COUNT(*) FROM Frequents WHERE bar=b); IF cust < 100 THEN RETURN 'unpopular'; ELSEIF cust < 200 THEN RETURN 'average'; ELSE RETURN 'popular'; END IF; END character_set_client: utf8 collation_connection: utf8_general_ci Database Collation: utf8_general_ci 1 row in set (0.00 sec) ERROR: No query specified
另一个例子
delimiter $$ CREATE FUNCTION shorten(s varchar(255), n INT) RETURNS varchar(255) BEGIN IF isNULL(s) THEN RETURN ''; ELSEIF n<15 THEN RETURN LEFT(s,n); ELSE IF CHAR_LENGTH(s)<=n THEN RETURN s; ELSE RETURN CONCAT(LEFT(s,n-10),'...',RIGHT(s,5)); END IF; END IF; END$$
mysql> delimiter ; mysql> select shorten("abcdefghijklmn", 5); +------------------------------+ | shorten("abcdefghijklmn", 5) | +------------------------------+ | abcde | +------------------------------+ 1 row in set (0.00 sec) mysql> select shorten("abcdefghijklmnopqrstuvwxyz", 15); +-------------------------------------------+ | shorten("abcdefghijklmnopqrstuvwxyz", 15) | +-------------------------------------------+ | abcde...vwxyz | +-------------------------------------------+ 1 row in set (0.00 sec)
基本形式:
<loop name>: LOOP <statements> END LOOP;
从循环中跳出:
LEAVE <loop name>
跳出循环
loop1: LOOP ... LEAVE loop1; ... END LOOP;
如果执行了上面的
mysql> delimiter // mysql> create function looptest() -> returns int -> begin -> declare n int default 0; -> L:LOOP -> if n>100 then leave L; -> end if; -> set n=n+5; -> END LOOP; -> return n; -> end//
注意函数名不能使用
mysql> delimiter ; mysql> select looptest(); +------------+ | looptest() | +------------+ | 105 | +------------+ 1 row in set (0.00 sec)
WHILE <condition> DO <statements> END WHILE;
REPEAT <statements> UNTIL <condition> END REPEAT;
注意: 目前 MySQL 中只有三种形式的循环:
下面这种
FOR <loop name> AS <cursor name> CURSOR FOR <query> DO <statement list> END FOR;
mysql> create procedure repeattest() -> begin -> declare id int default 0; -> R:repeat -> set id=id+1; -> until id>=10; -> end repeat; -> end// ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '; end repeat R; end' at line 1
mysql> create procedure repeat2() -> begin -> declare id int default 0; -> R:repeat -> set id=id+1; -> until id>=10 -> end repeat R; -> end// Query OK, 0 rows affected (0.00 sec)
过程名不能使用
mysql> delimiter // mysql> create function whiletest() -> returns int -> begin -> declare i int default 0; -> while i<10 do -> set i=i+1; -> end while; -> return i; -> end//
同样函数名不能使用
书本第9章 P.236 图 9-13
CREATE FUNCTION BandW(y INT, s CHAR(15)) RETURNS BOOLEAN IF NOT EXISTS ( SELECT * FROM Movies WHERE year = y AND studioName = s ) THEN RETURN TRUE; ELSEIF 1<= (SELECT COUNT(*) FROM Movies WHERE year=y AND studioName=s AND genre='comedy') THEN RETURN TRUE; ELSE RETURN FALSE; END IF;//
书本第9章 P.238 图 9-15. 已经改写为适合 MySQL 下运行.
CREATE PROCEDURE MeanVar( IN s CHAR(15), OUT mean REAL, OUT variance REAL ) BEGIN DECLARE Not_Found CONDITION FOR SQLSTATE '02000'; DECLARE MovieCursor CURSOR FOR SELECT length FROM Movies WHERE studioName = s; DECLARE newLength INTEGER; DECLARE movieCount INTEGER; SET mean = 0.0; SET variance = 0.0; SET movieCount =0; OPEN MovieCursor; movieLoop: LOOP FETCH FROM MovieCursor INTO newLength; IF Not_Found THEN LEAVE movieLoop END IF; SET movieCount = movieCount + 1; SET mean = mean + newLength; SET variance = variance + newLength * newLength; END LOOP; SET mean = mean / movieCount; SET variance = variance / movieCount - mean * mean; CLOSE MovieCursor; END;
通常的
不过有三种方式可以使用这样的查询.
使用局部变量 p 和关系
SET p = (SELECT price FROM Sells WHERE bar = 'Joe''s Bar' AND beer = 'Bud');
对于返回单个元组的查询, 获取其中各个属性的值, 有另一种方式. 即将
例如:
SELECT price INTO p FROM Sells WHERE bar = 'Joe''s Bar' AND beer = 'Bud';
使用下面的方式声明一个游标
DECLARE c CURSOR FOR <query>;
为使用游标
当游标
从游标
关于
使用游标的通常方式是建立一个含有
一个技巧性的问题是当游标没有元组要传送时, 我们如何跳出循环.
每个
在 PSM 中, 我们可以通过一个名为
我们可以定义一个 condition, 它是一个 boolean 变量. 其值为真当且仅当
例如: 我们可以声明一个名为
DECLARE NotFound CONDITION FOR SQLSTATE '02000';
游标循环的结构如下:
cursorLoop: LOOP ... FETCH c INTO ... ; IF NotFound THEN LEAVE cursorLoop; END IF; ... END LOOP;
我们来写一个过程, 检查关系
CREATE PROCEDURE JoeGouge() DECLARE theBeer CHAR(20); DECLARE thePrice REAL; DECLARE NotFound CONDITION FOR SQLSTATE '02000'; DELCARE c CURSOR FOR (SELECT beer, price FROM Sells WHERE bar='Joe''s Bar'); BEGIN OPEN c; menuLoop:LOOP FETCH c INTO theBeer, thePrice; IF NotFound THEN LEAVE menuLoop END IF; IF thePrice < 3.00 THEN UPDATE Sells SET price = thePrice + 1.00 WHERE bar = ’Joe’’s Bar’ AND beer = theBeer; END IF; END LOOP; CLOSE c; END;
mysql> create procedure JoeGouge() -> begin -> declare theBeer char(20); -> declare thePrice real; -> declare c cursor for -> select beer,price from Sells where bar='Joe''s bar'; -> open c; -> menuLoop:LOOP -> fetch c into theBeer,thePrice; -> if thePrice<20 then -> update Sells set price=thePrice+1 where -> beer=theBeer AND bar='Joe''s bar'; -> end if; -> end LOOP; -> close c; -> end// Query OK, 0 rows affected (0.01 sec)
mysql> select * from Sells where price < 20// +-----------+---------------+-------+ | bar | beer | price | +-----------+---------------+-------+ | Joe's Bar | Stella Artois | 5 | +-----------+---------------+-------+ 1 row in set (0.00 sec) mysql> call JoeGouge(); -> // ERROR 1329 (02000): No data - zero rows fetched, selected, or processed mysql> select * from Sells where price < 21; +-----------+---------------+-------+ | bar | beer | price | +-----------+---------------+-------+ | Joe's Bar | Stella Artois | 6 | +-----------+---------------+-------+ 1 row in set (0.00 sec)
尽管这个存储过程设计得并不好, LOOP 循环居然没有 LEAVE 语句, 但还是起作用了.
mysql> delimiter // mysql> create procedure JoeGouge2() -> begin -> declare theBeer char(20); -> declare thePrice real; -> declare NotFound condition for sqlstate '02000'; -> declare c cursor for -> select beer,price from Sells; -> open c; -> menuLoop: LOOP -> fetch c into theBeer, thePrice; -> if NotFound then leave menuLoop; -> end if; -> if thePrice < 20 then -> update Sells set price=thePrice+1 -> where beer=theBeer; -> end if; -> end LOOP; -> close c; -> end// Query OK, 0 rows affected (0.00 sec)
CREATE PROCEDURE JoeGouge3() BEGIN DECLARE theBeer CHAR(20); DECLARE thePrice REAL; DECLARE NotFound CONDITION FOR SQLSTATE '02000'; DELCARE c CURSOR FOR SELECT beer, price FROM Sells WHERE bar='Joe''s Bar'; OPEN c; menuLoop:LOOP FETCH c INTO theBeer, thePrice; IF NotFound THEN LEAVE menuLoop END IF; IF thePrice < 3.00 THEN UPDATE Sells SET price = thePrice + 1.00 WHERE bar = 'Joe''s Bar' AND beer = theBeer; END IF; END LOOP; CLOSE c; END// ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'c CURSOR FOR SELECT beer, price FROM Sells WHERE bar='Joe''s Bar'; O' at line 7
例子来源于 http://dev.mysql.com/doc/refman/5.1/zh/stored-procedures.html#cursors
mysql> CREATE PROCEDURE curdemo() -> BEGIN -> DECLARE done INT DEFAULT 0; -> DECLARE a CHAR(16); -> DECLARE b,c INT; -> DECLARE cur1 CURSOR FOR SELECT id,data FROM test.t1; -> DECLARE cur2 CURSOR FOR SELECT i FROM test.t2; -> DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1; -> OPEN cur1; -> OPEN cur2; -> REPEAT -> FETCH cur1 INTO a, b; -> FETCH cur2 INTO c; -> IF NOT done THEN -> IF b < c THEN -> INSERT INTO test.t3 VALUES (a,b); -> ELSE -> INSERT INTO test.t3 VALUES (a,c); -> END IF; -> END IF; -> UNTIL done END REPEAT; -> CLOSE cur1; -> CLOSE cur2; -> END// Query OK, 0 rows affected (0.00 sec)
FOR <loop name> AS <cursor name> CURSOR FOR <query> DO <statement list> END FOR;
由于不是直接获取自己的元组, 所以不能为那些被替代的元组分量指定存储变量. 查询结果的属性名可作为局部变量.
我们用
CREATE PROCEDURE JoeGouge()
BEGIN
FOR menuLoop AS c CURSOR FOR
(SELECT beer AS theBeer, price AS thePrice FROM Sells
WHERE bar='Joe''s Bar');
DO
IF price < 3.00 THEN
UPDATE Sells SET price=thePrice+1.00
WHERE bar='Joe''s Bar' AND beer=theBeer;
END IF;
END FOR;
END;
每个异常处理都对应一个
DECLARE <next step> HANDLER FOR <condition list> <statements>
这里的转移 <next step> 有如下几种选择方式:
从
CREATE FUNCTION GetUniquePrice (b VARCHAR(20)) RETURNS REAL DECLARE Not_Found CONDITION FOR SQLSTATE '02000'; DECLARE Too_Many CONDITION FOR SQLSTATE '21000'; BEGIN DECLARE EXIT HANDLER FOR Not_Found RETURN 0; DECLARE EXIT HANDLER FOR Too_Many RETURN -1; RETURN (SELECT DISTINCT price FROM Sells WHERE beer=b); --(SELECT price FROM Sells WHERE beer=b LIMIT 1); END;
将
CREATE PROCEDURE PrintUniquePrice ( IN b VARCHAR(20), OUT p REAL ) DECLARE Not_Found CONDITION FOR SQLSTATE '02000'; DECLARE Too_Many CONDITION FOR SQLSTATE '21000'; BEGIN DECLARE EXIT HANDLER FOR Not_Found; DECLARE EXIT HANDLER FOR Too_Many; SET p=(SELECT price FROM Sells WHERE beer=b LIMIT 1); INSERT INTO UniquePrice (beer, price) VALUES (b, p); END;
将之改进为对所有啤酒的循环
SQL语言的使用有两种方式:
所有嵌入 SQL 语句都以
嵌入式SQL须解决下列几个问题:
PHP 以及现在的很多scripting language, 比如 Perl, Python, Ruby, Javascript 等都不支持嵌入式SQL(Embedded SQL)。只有C和C++以及一些老的语言支持嵌入式SQL。
现在新的应用软件很少使用嵌入式SQL。这是1980s--1990s时的产品。不过使用嵌入式SQL有个好处,就是不用担心 SQL injection。因为其中的SQL语句在编译前被解析(预编译)而不是运行时解析。
现在使用的都是
目前支持嵌入式SQL的数据库有 PostgreSQL 和 Firebird。 请分别参考:
关于如何编译PostgreSQL的嵌入式SQL代码,参见
https://www.postgresql.org/docs/9.3/static/ecpg-process.html
因此,如果需要学习嵌入式SQL的话,建议先学习PostgreSQL或Firebird。
References: 嵌入式SQL介绍 http://www.cppblog.com/prayer/archive/2010/01/11/105415.html 本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/prayforever/archive/2008/08/06/2778495.aspx Bill Karwin, https://www.quora.com/What-is-embedded-SQL-Does-PHP-support-it
为了联系
共享变量的声明加入到如下两个嵌套
EXEC SQL BEGIN DECLARE SECTION; <host-language declarations> EXEC SQL END DECLARE SECTION;
这两个语句之间的部分称为
在
在宿主语言中, 共享变量的行为与其他变量的行为并无二致.
我们将使用 C 语言和嵌入式
对于关系
EXEC SQL BEGIN DECLARE SECTION; char theBar[21], theBeer[21], float thePrice; EXEC SQL END DECLARE SECTION; /* obtain values for theBar and theBeer */ EXEC SQL SELECT price INTO :thePrice FROM Sells WHERE bar = :theBar AND beer = :theBeer; /* do something with thePrice*/
这里的
嵌入式
声明一个游标:
EXEC SQL DECLARE c CURSOR FOR <query>
开启和关闭游标:
EXEC SQL OPEN CURSOR c; EXEC SQL CLOSE CURSOR c;
从游标:处获得数据.
EXEC SQL FETCH c INTO <variable(s)>;
宏NOT_FOUND 为真当且仅当FETCH 没有取得一个元组.
我们使用
游标将访问
EXEC SQL BEGIN DECLARE SECTION; char theBeer[21]; float thePrice; EXEC SQL END DECLARE SECTION; EXEC SQL DECLARE c CURSOR FOR SELECT beer, price FROM Sells WHERE bar = 'Joe''s Bar';
注意游标的声明在声明节(declare-section)之外.
EXEC SQL OPEN CURSOR c; while(1){ EXEC SQL FETCH c INTO :theBeer, :thePrice; if(NOT_FOUND) break; /* the C style of breaking loops */ /* format and print theBeer and thePrice */ } EXEC SQL CLOSE CURSOR c;
大多数应用使用特定的查询和修改语句与数据库进行交互.
另一种形式的嵌套
准备一个查询:
EXEC SQL PREPARE <query-name> FROM <text of the query>;
执行一个查询:
EXEC SQL EXECUTE <query-name>;
EXEC SQL BEGIN DECLARE SECTION; char query[MAX_LENGTH]; EXEC SQL END DECLARE SECTION; while(1){ /* issue SQL > prompt */ /* read user's query into array query */ EXEC SQL PREPARE q FROM :query; EXEC SQL EXECUTE q; }
如果语句被编译后只是执行一次, 则可以将上面的
EXEC SQL EXECUTE IMMEDIATE <text>;
EXEC SQL BEGIN DECLARE SECTION; char query[MAX_LENGTH]; EXEC SQL END DECLARE SECTION; while(1){ /* issue SQL> prompt */ /* read user's query into array query */ EXEC SQL EXECUTE IMMEDIATE :query; }