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;
}