SQL简明笔记(持续更新)

子查询

子查询是嵌套在其他查询里的查询,比如下面的例子

SELECT product_name, cnt_product
FROM (SELECT product_name, COUNT(*) AS cnt_product
     FROM product
     GROUP BY product_name) AS ProductSum;

子查询可以应用在任何需要其结果的地方,比如在其之上进一步筛选,或者作为筛选条件(WHERE 或 IN)都可以。

  • 子查询作为内层查询会首先执行;
  • 使用子查询需要设定好名称,使不使用 AS 关键字都是可以的(Oracle 不允许 AS)。

标量子查询

标量子查询就是只返回一行一列的结果的子查询。主要是应用在需要使用常数或列名的地方(比如 WHERE 中不允许使用聚合函数,就可以使用标量子查询规避这个问题),特别是=或<>这种需要单一值的比较运算符之中。

关联子查询

关联子查询是在需要细分的组内进行比较时,所需要的操作。

比如,某表中商品分若干类,要筛选出价格高于对应类平均价格的商品信息,可以使用以下操作

SELECT product_type, product_name, sale_price
FROM Product AS P1
WHERE sale_price > (SELECT AVG(sale_price)
                    FROM Product AS P2
                    WHERE P1.product_type = P2.Product_type
                    GROUP BY product_type);

关键点在于在子查询中添加的 WHERE 子句的条件。

连结

在数据检索时,可以使用连结功能从一张以上的表中检索数据。

关键词:主键;外键;关系;可伸缩性

简单连结

SELECT product_name, vend_name
FROM products, vendors
WHERE products.products_id = vendors.vend_id;
  • 如果不使用 WHERE 指定配对的主键和外键,连结的输出结果是两个表的笛卡尔积

也可以连结多张表,如

SELECT product_name, vend_name, quantity
FROM products, vendors, orderitems
WHERE vendors.vend_id = products.products_id
AND orderitems.products_id = products.products_id;

使用 JOIN 和 ON 进行连结

更规范的连结方法为下面这种

SELECT product_name, vend_name
FROM products JOIN vendors
ON products.products_id = vendors.vend_id;

SQL支持的连结方法有内连结(INNER JOIN,默认)、外连结(OUTER JOIN)、左连结(LEFT JOIN)、右连结(RIGHT JOIN)四种。

  • 不使用 ON 指定连结键的话,结果依然是输出笛卡尔积。

组合查询

多数SQL允许执行多个查询,并将结果作为单个查询结果查询。多数情况下,组合查询达到的效果也能用WHERE子句达到,但是这两者在不同情况下的性能不同,所以应该分别尝试,确定选择哪一种。

使用UNION创建组合查询

SELECT vend_id, prod_id, prod_price
FROM products
where prod_price <= 5
UNION
SELECT vend_id, prod_id, prod_price
FROM products
where vend_id IN (1001, 1002);
  • 组合查询要求每个查询必须包含相同的列,但各个列的次序可不必相同。
  • UNION返回的是多个查询语句的并集,其中如果有重复的,将会去重。如果不想要去重,可以使用 UNION ALL
  • 可以对最终查询结果进行排序,但只能有一条ORDER BY语句,放在最后。

谓词

之前用过的各种比较运算符,比如 =、<、> 等,其正式名称就是谓词。而谓词的本质是一种返回值为布尔值的函数。

下面介绍几种常用的谓词。

LIKE:字符串的部分一致查询

在下文文本相关操作中叙述。

BETWEEN:范围查询

SELECT product_name, sale_price
FROM Product
WHERE sale_price BETWEEN 100 AND 1000;

注意 BETWEEN 返回结果包含100和1000这两个边界值。如不想包括边界值,则必须使用 < 和 >。

IS NULL、IS NOT NULL:判断空值

SELECT product_name, sale_price
FROM Product
WHERE product_name IS NOT NULL;

IN:匹配多个值

如果在筛选时要筛选出满足若干个特定值的结果,可以用 OR 书写多行的条件判断,更简单的方法是使用 IN 谓词来匹配一个列表。

SELECT product_name, sale_price
FROM Product
WHERE sale_price IN (100, 200, 300);

  • 与之相对的,还有 NOT IN 来进行反向匹配;
  • IN 和 NOT IN 都无法匹配空值。

文本相关操作

使用LIKE进行通配搜索

SELECT product_id, product_name
FROM products
WHERE product_name LIKE 'jet%';

进行通配搜索可以使用通配符进行模糊匹配,以下列出常见的几种通配符:

  • % :匹配任意个数的任意字符;
  • _ :匹配单个任意字符。

使用REGEXP应用正则表达式

SELECT product_id, product_name
FROM products
WHERE product_name REGEXP '^[0-9\\.]';

使用CONCAT拼接字符串

SELECT CONCAT(product_name, '(', produt_country, ')')
FROM products;

使用SUBSTR进行字符串截取

SELECT SUBSTR(product_name, 3, 5)
FROM products;

截取字符串从第3位开始长度为5的字串。

其他常见的文本处理函数

函数 说明
Left() 返回串最左边的字符
Right() 返回串最右边的字符
Length() 返回串的长度
Locate() 返回字串的出现位置
SubString() 返回子串的字符
Lower() 将串转换为大写
Upper() 将串转换为小写
Soundex() 返回串的SOUNDEX值
RTrim() 去除串右边所有空格
LTrim() 去除串左边所有空格
Trim() 去除串两边所有空格

关于 Locate() 和 SubString() 的使用,可见下:

  • Locate(substr, str, pos):返回字串在字符串中第一次出现的位置,如果串中没有该字串,返回0。默认从串开头开始搜索,如果设置了pos参数,则返回其在pos位置后第一次出现的位置(依然记字符串开头为1号位置)。
  • SubString(str, pos, len):截取字符串从pos位置之后的子串,默认截取到字符串最后,设置len参数可设定要截取字串的长度。
  • 另一个可以返回字串位置的函数是 Position(),其使用方法为 Position(substr IN str)

全文本搜索

MySQL只有部分数据库引擎支持全文本搜索。无论是正则还是通配符,他们都要求MySQL尝试匹配表中所有行,当随着系统的使用行数不断增加时,这些操作会非常耗时。而且,这两者是精确匹配,不够智能化。

在创建表时使用FULLTEXT启用全文本搜索

一般在创建表时启用全文本搜索支持。

CREATE TABLE productnotes
(
node_id  int  NOT NULL AUTO INCREMENT,
prod_id  char(10)  NOT NULL,
note_date  datetime NOT NULL,
note_text  text  NULL,
PRIMARY KEY(note_id),
FULLTEXT(note_text)
)ENGINE=MyISAM;

  • ENGINE子句可以指定使用哪个数据库引擎;
  • FULLTEXT可以在创建表时指定,也可以在稍后指定,但不要在导入数据时使用,这样会降低索引效率。

使用 Match() 和 Against() 进行全文本搜索

其中 Match() 指定被搜索的列,Against() 指定使用的搜索表达式

SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('rabbit');

  • 传递给 Match() 的值必须与 FULLTEXT 中定义的相同;
  • 搜索不区分大小写,除非使用 BINARY 方式;
  • 全文本搜索返回以文本匹配的良好程度排序的数据,比如上面返回的就是以rabbit一词在句中的次序排列的结果。

下面为排序的演示

SELECT note_text,
       Match(note_text) Against('rabbit') AS rank
FROM productnotes;

结果生成的rank列里,显示的就是该行的等级值,它根据行中的词数、唯一词数、整个索引中词的总数及包含该词的行数等计算出来。

而由于上面的语句没有使用WHERE,所以查询结果并没有过滤,会包含很多rank为0的行(即没有包含“rabbit”的行)。

查询扩展

上面的查询,是精确查询包含“rabbit”一词的行,但如果想找到不一定包含“rabbit”,但却与此有关的行,就可以使用查询扩展功能。

SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('anvils' WITH QUERY EXPANSION);

它的原理是,先进行一次普通的全文本搜索,找出与搜索条件匹配的所有行,然后检查这些匹配行并选择所有有用的词,根据这些词再进行一次全文本搜索,得出最终的结果。

布尔文本搜索

SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('heavy -rope*' IN BOOLEAN MODE);

上面的语句会查询包含“heavy”,但不包含以“rope”开头的单词的行。常见的布尔通配符如下

布尔操作符 说明
+ 包含(词必须存在)
- 排除(词必须不存在)
> 包含且增加等级值(即rank)
< 包含且减少等级值
( ) 把词组成子表达式
~ 取消一个词的等级值
* 词尾的通配符
“ ” 包含一个短语
  • 布尔方式即使在没有定义FULLTEXT索引时也可以用,但是速度会很低。

其他说明

  • 在索引全文本数据时,少于四个字符的行将被忽略并排除;
  • MySQL自带停用词表,这些词在索引时会被忽略。同样的,如果一个词出现在50%以上的行中,也会被忽略,但50%规则不适用于布尔搜索;
  • 如果表中的函数少于三行,则全文本搜索不会返回结果;
  • 词中的单引号会被忽略,如don’t会被索引为dont;
  • 不具备词分隔符的语言,如汉语不能恰当地被返回搜索结果。

增删改

使用INSERT插入数据

INSERT INTO products
VALUES('pencil',100);

上面的语句简单但不安全,完整的语法为

INSERT INTO products(prod_name, price)
VALUES('pencil', 100);

  • 比较推荐下面的写法,它更安全,且在表结构发生改变的时候也能继续使用;
  • 它不限制写入内容的次序,且可以只插入部分列的内容(如果约束条件允许的话);
  • INSERT可能限制等待处理的SELECT语句的性能。如果该数据库查询比插入更重要,可以改用INSERT LOW_PRIORITY INTO语句降低INSERT语句的优先级。UPDATE和DELETE同理,可以用LOW_PRIORITY降低优先级。

插入多行

各行之间用逗号隔开即可。

INSERT INTO products
VALUES('pencil',100), ('pen', 200);

将SELECT出来的数据插入到表中

INSERT INTO products
SELECT prod_name, price
FROM other_products;

使用UPDATE更新数据

UPDATE products
SET price = 50
WHERE prod_name = 'pencil';
  • 如果省略WHERE子句,则语句将会更新表中所有行;
  • 如果要更新多个列,则在SET中用逗号隔开即可,如SET price = 50, prod_name = ‘ruler’
  • 如果用UPDATE更新多行,只要其中一行出现错误,那么整个更新操作都会被撤回。如果要使得发生错误也能继续更新下去,可以使用IGNORE关键字,即UPDATE IGNORE

使用DELETE删除数据

DELETE FROM products
WHERE price > 50;
  • 同样,如果省略WHERE,那么语句会删除表中所有行;
  • 更高效的删除所有行的方法是TRUNCATE DELETE语句,它会删除整个表然后再创建一个同样的空表。

视图

从SQL的角度来看,视图和表是相同的,区别在于表中保存的是实际的数据,而视图保存的是 SELECT 语句而不实际保存数据。

视图的好处是可以复用使用较为频繁的SQL语句,简化SQL操作,且保护数据。

创建了视图后,可以用正常的查询语句来对视图进行查询

使用 CREATE VIEW 创建视图

CREATE VIEW view_name(column_name1, column_name2, ...)
AS
<SELECT 语句>
  • 创建视图时指定列名不是必须的;
  • 最好不要在创建视图时使用 ORDER BY,这在某些 DBMS 中这种操作是不允许的。而即使允许,如果从该视图中检索数据时使用了 ORDER BY,那么将覆盖视图中的 ORDER BY操作(非永久性);
  • 视图可以嵌套,但不允许索引和有关联的触发器和默认值;
  • 视图可以和表一起使用,比如创建一条连结表和视图的二重视图。

更新视图

使用 INSERT、UPDATE 等更新视图

当视图满足以下条件时,才能对其进行增删改操作:

  • 未使用分组函数(GROUP BY 或 HAVING);
  • 未使用表连结;
  • 未使用子查询;
  • 未使用 DISTINCT 函数;
  • 未使用聚集函数;
  • 未创建计算列。

这是因为,视图是基于原数据创建而来的,如果更新了包括聚合、计算的列,会难以将这种更新映射到原来的表中,这就破坏了数据的一致性。而如果成功进行了更新,那么修改也会映射到原表中。

另外,部分 DBMS(如 PostgreSQL)创建视图时默认只读,需要在创建时进行设置以允许更新。事实上,从 SQL 的理念来说,并不推荐对视图进行更新。

使用 CREATE OR REPLACE VIEW 更新视图

更新视图,而不更新原数据的方法是先用 DROP 再用 CREATE。而另一种是用 CREATE OR REPLACE VIEW,用这句语句来代替创建视图时的 CREATE VIEW,产生的效果就是,如果要更新的视图不存在,那么将会创建一个视图;而如果要更新的视图存在,则新的视图会取代原有视图。

其他操作

删除视图

DROP VIEW view_name;

查看创建视图的语句

SHOW CREATE VIEW view_name;

事务

事务是需要在同一个处理单元中执行的一系列更新处理的集合。比如在企业管理系统里,删除一个员工就需要对人事、财务、内网权限等信息进行更新,这时候就可以将这些操作包装成一个事务。事务操作可以用来维护数据库的完整性,即保证某种操作所牵扯到的SQL语句全部被执行。事务主要管理UPDATE、INSERT、DELETE语句。

事务必须满足四个条件(即ACID特性):

  • 原子性(Atomicity):一个事务中的所有操作,要么全部执行,要么全部不执行。即使事务执行到一半被强行终止,也应回滚到事务开始前的状态。
  • 一致性(Consistency):在事务开始前和结束后都没有破坏数据库的完整性,即事务的处理必须符合数据库提前设置的约束和预设规则。
  • 隔离性(Isolation):数据库允许并发事务,但不同事务之间执行互不干扰,互不嵌套。而且在一个事务结束前,它对其他事务而言是不可见的。事务隔离分为不同级别,包括读未提交、读提交、可重复读和串行化。
  • 持久性(Durability):事务结束后,对数据的修改就是永久的,即使系统故障也不会丢失。保证持久性的常用方法如将事务的执行记录(即日志)保存在存储空间中。

创建事务

START TRANSACTION;
    <DML语句>;
    <DML语句>;
    ...
COMMIT;
  • BEGINSTART TRANSACTION 显式地开启一个事务。但事实上这不是事务操作真正的开始(比如Oracle定义事务并不需要这一语句),标准SQL中定义了隐式开始事务处理的方法。
  • COMMIT 也可以使用 COMMIT WORK,不过二者是等价的。执行到这行代码时,事务里的指令会被真正且不可逆地执行。

使用ROLLBACK回滚事务

ROLLBACK是取消事务包含的全部更新处理的结束指令。

START TRANSACTION;
    <DML语句>;
    <DML语句>;
    ...
ROLLBACK;

以上的操作会使事务中的DML语句无效化,数据库回滚到事务开始之前的状态。

SQL中的提交模式

上文说过,START TRANSACTION 语句并不是用来真正开启事务的。大部分情况下,事务在数据库建立时就已开始了。另外,COMMIT 是提交事务的语句,但在实际使用中,经常是写完一个DML语句,DBMS便将该语句执行了,即执行 SQL 语句后就会马上执行 COMMIT 操作。这是因为该DBMS默认为自动提交模式

在自动提交模式中,每条SQL语句就是一个事务。而如果关闭了自动提交模式,则要在用户执行了COMMIT或ROLLBACK语句后,才算作一个事务。MySQL默认的提交模式为自动提交模式。

可以用 SET 来改变 MySQL 的自动提交模式

  • SET AUTOCOMMIT=0:禁止自动提交;
  • SET AUTOCOMMIT=1:开启自动提交。

事务的隔离级别

数据库操作中,并发情况可能会出现第一类更新丢失(事务A撤销时覆盖已提交的事务B的更新)、第二类更新丢失(事务A覆盖事务B已经提交的更新)、脏读(事务A在事务B的基础上操作,但B却回滚了,那么A读到的数据就是脏数据)、不可重复读(对同一行数据读两次却得到不同结果,比如事务A正在读取时事务B却同时更新数据)、幻象读(两次SELECT执行的效果不一样)等问题。为了解决这些问题,满足事务的隔离性,需要设置事务的隔离级别。

  • READ UNCOMMITTED(读未提交):一个事务可以读取另一个未提交事务的数据。
  • READ COMMITTED(读提交):一个事务要等另一个事务提交后才能读取数据。
  • REPEATABLE READ(重复读):两个事务同时读取一个数据,它们读到的数据都是一样的,即开始读取数据时,不再允许修改操作。
  • SERIALIZABLE(序列化):最高的隔离级别,仅允许事务串行化执行,不可并行。

大部分DBMS默认为 REPEATABLE READ,而 MySQL 默认为 REPEATABLE READ。可以通过下面的语句设置当前事务隔离级别

SET SESSION TRANSACTION ISOLATION LEVEL <隔离级别>;

事务的保存点

在事务的处理过程中,如果发生错误且回滚 ,那么整个事务对数据库的操作都将被撤销。事实上,可以通过设置多个保存点,将事务分割为几个小部分,使得一个部分的错误不至于牵扯到整个事务。

保存点设置在事务的各DML语句之间,方法为

SAVEPOINT <保存点名>;

设置了保存点后,可以使用下面的命令回滚到这个保存点之前

ROLLBACK TO <保存点名>;

使用下面的命令删除一个保存点

RELEASE SAVEPOINT <保存点名>;

索引

索引是一种特殊的查询表,可以看作指向表中数据的指针。索引能够提高 SELECT 查询和 WHERE 子句的速度,但是却降低了包含 UPDATE 语句或 INSERT 语句的数据输入过程的速度。索引的创建与删除不会对表中的数据产生影响。

索引分为两类,聚集索引和非聚集索引。

  • 聚集索引:数据表的物理存储顺序本身就是按照想要的规律排列,即数据表物理顺序与索引顺序一致的索引。由于一个数据表的物理顺序只有一种,所以只能有一个聚集索引。
  • 非聚集索引:独立于数据表结构的索引,可以有多个。

建立索引

CREATE [UNIQUE] [CLUSTERED|NONCLUSTERED] INDEX <索引名>  
ON <表名> ( <列名> [ASC|DESC]) WHERE [筛选条件];
  • 唯一索引:如果创建索引时加入了关键词UNIQUE,则该索引为唯一索引,即不存在索引值相同的两行。对于视图创建的聚集索引必须是唯一索引。
  • CLUSTERED|NONCLUSTERED:用于指定创建聚集索引或非聚集索引。默认为创建非聚集索引。
  • 如果是BLOB和TEXT类型,必须在列名后用括号指定 length

在创建表时也可以直接指定索引

CREATE TABLE <表名>
...
INDEX <索引名> <列名>
...

使用ALTER添加和删除索引

有四种方式来添加数据表的索引:

  • ALTER TABLE <表名> ADD PRIMARY KEY <列名列表>:添加一个主键,这意味着索引值必须是唯一的,且不能为NULL。
  • ALTER TABLE <表名> ADD UNIQUE <索引名> <列名列表> :添加唯一索引。
  • ALTER TABLE <表名> ADD INDEX <索引名> <列名列表> :添加普通索引。
  • ALTER TABLE <表名> ADD FULLTEXT <索引名> <列名列表>:指定了索引为 FULLTEXT ,用于全文索引。

删除索引的操作则为

  • ALTER TABLE <表名> DROP <索引名>
  • DROP INDEX <索引名> ON <表名>:也可以用这种方法

存储过程

存储过程可以理解为一个封装好的函数,或者一个批处理对象,它包括为了以后的使用而保存的一条或多条SQL语句的集合。使用存储过程的好处是简单、安全、高性能。

创建存储过程

CREATE PROCEDURE <存储过程名> (参数列表)
BEGIN
<存储过程体,如一条或多条查询语句>;
END;
  • 如果使用命令行编写存储过程,那么存储过程体中的分号会使命令行以为这是整个语句的分隔符,导致句法错误。解决方法是临时更改命令行实用程序里的语句分隔符

    DELIMITER //
    
    CREATE PROCEDURE <存储过程名> (参数列表)
    BEGIN
    <存储过程体,如一条或多条查询语句>;
    END//
    
    DELIMITER ;
    

    上面的解决方法是利用DELIMITER将分隔符定义为“//”,然后在编写完存储过程后,再将其改回分号。除”\“符号外,任何字符都可以定义为语句分隔符。

执行存储过程

CALL  <存储过程名> (参数列表);

删除存储过程

DROP PROCEDURE  <存储过程名> (参数列表);

关于参数(变量)

如果不定义参数列表,那么存储过程所返回的结果就直接被显示出来,而通过添加参数列表,存储过程可以将结果返回给其对应的变量

如定义以下的存储过程

CREATE PROCEDURE productpricing(
    OUT pl DECIMAL(8, 2),
    OUT ph DECIMAL(8, 2),
    OUT pa DECIMAL(8, 2)
)
BEGIN
    SELECT Min(prod_price)
    INTO pl
    FROM products;
    SELECT Max(prod_price)
    INTO ph
    FROM products;
    SELECT Avg(prod_price)
    INTO pa
    FROM products;
END;

上面的存储过程接受三个参数,而每个参数的定义为 <关键字 变量名 数据类型>,其中关键字包含IN(传递给存储过程)、OUT(从存储过程传出)、INOUT(对存储过程传入或传出)三种。而存储过程的参数允许的数据类型与表中使用的数据类型相同,但不允许使用记录集这种数据类型。

调用以上的存储过程,方法为

CALL productpricing(@pricelow, @pricehigh, @priceavg);
  • 所有MySQL变量都必须以@开始
  • 这时候,执行 SELECT @pricelow,可以输出对应的结果。

智能存储过程

可以在一个存储过程中定义更加复杂的操作,以包括所需的业务规则和智能处理过程。比如下面的例子,以下的存储过程有三个功能:获得订单合计、把营业税有条件地添加到合计、返回合计(带或不带税)。

-- Name:ordertotal
-- Parameters: onumber = order number
--             taxable = 0 if not taxable else 1
--             ototal  = order total variable
CREATE PROCEDURE ordertotal(
    IN onumber INT,
    IN taxable BOOLERN,
    OUT ototal DECIMAL(8, 2)
) COMMENT 'Obtain order total, optionally adding tax'
BEGIN
    -- Declare variable for total
    DECLARE total DECIMAL(8, 2);
    -- Declare tax pencentage
    DECLARE taxrate INT DEFAULT 6;
    
    -- Get the order total
    SELECT SUM(item_price * quantity)
    FROM orderitems
    WHERE order_num = onumber
    INTO total;
    
    -- Is this taxable?
    IF taxable THEN
        SELECT total + (total / 100 * taxrate) INTO total;
    END IF;
    
    -- Finally, save to out variable
    SELECT total INTO ototal;
END;

这个存储过程包含许多新内容:

  • SQL的注释以 ”--“ 开头;
  • COMMENT关键字可以用来给存储过程添加说明(不是必须的),这个说明可以通过 SHOW PROCEDURE STATUS 显示。
  • DECLARE语句定义了两个局部变量,在SQL中定义变量,需要指定类型,必要时可以通过DEFAULT语句指定默认值;
  • SQL的条件判断语句结构为IF - THEN,在条件判断结束后,需要END IF语句(关于条件判断,下文会再详解);

检查存储过程

要检查用来创建一个存储过程的语句,可以用

SHOW CREATE PROCEDURE <存储过程名>;

要检查数据库中包括何时、由谁创建等详细信息的存储过程列表,可以用

SHOW PROCEDURE STATUS;

可以使用LIKE指定一个过滤模式

SHOW PROCEDURE STATUS LIKE 'ordertotal';

游标

SQL检索后返回的是一组称为结果集的行,如果想得到结果集里的第一行、第十行等,可以使用游标。游标是存储在SQL服务器上的数据库查询,它不是一条SELECT语句,而是被该语句检索出来的结果集,在存储了游标之后,应用程序可以根据需要滚动或浏览其中的数据,所以游标常常用于交互式应用。

游标的使用有以下过程:

  1. 先声明(定义)一个游标,即定义要使用的SELECT语句;
  2. 声明游标后,必须打开游标以供使用;
  3. 对于填有数据的游标,根据需要取出(检索)各行;
  4. 结束使用必须关闭游标。

创建游标

CREATE PROCEDURE processorders()
BEGIN
    DECLARE ordernumbers CURSOR
    FOR
    SELECT order_num FROM orders;
END;

游标定义在一个存储过程里,用DECLARE创建,再定义相应的SELECT语句。

打开和关闭游标

OPEN语句打开一个游标

OPEN ordernumbers;

在执行OPEN语句时,会执行查询,将查询结果储存在游标里;

CLOSE语句关闭游标

CLOSE ordernumbers;

不需要使用游标后应该将其关闭,以释放其占用的内存和资源。如果不明确使用CLOSE关闭游标,SQL会在到达END语句时自动将其关闭。

使用游标

打开游标后,可以使用FETCH语句访问它的每一行。FETCH指定检索什么数据,以及检索出来的数据存储在什么地方。它还向前移动游标的内部行指针,使下一条FETCH语句检索下一行。

以下的例子从游标中检索第一行

CREATE PROCEDURE processorders()
BEGIN
    DECLARE o INT;
    
    DECLARE ordernumbers CURSOR
    FOR
    SELECT order_num FROM orders;
    
    OPEN ordernumbers;
    
    FETCH ordernumbers INTO o;
    
    CLOSE ordernumbers;
END;

以下的例子遍历游标,从第一行到最后一行

CREATE PROCEDURE processorders()
BEGIN
    DECLARE o INT;
    DECLARE done BOOLEAN DEFAULT 0;
    
    DECLARE ordernumbers CURSOR
    FOR
    SELECT order_num FROM orders;
    
    DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;
    
    OPEN ordernumbers;
    
    REPEAT
        FETCH ordernumbers INTO o;
    UNTIL done END REPEAT;
    
    CLOSE ordernumbers;
END;

其他内容

常见的日期和时间处理函数

函数 说明
AddDate() 增加一个日期
AddTime() 增加一个时间
CurDate() 返回当前日期
CurTime() 返回当前时间
Now() 返回当前日期和时间
Date() 返回日期时间中的日期部分
Time() 返回日期时间中的时间部分
DateDiff() 计算两个日期之差
Date_Add() 灵活的日期运算函数
Date_Format() 返回一个格式化的日期或时间串
Year() 返回日期中的年份部分
Month() 返回日期中的月份部分
Day() 返回日期中的天数部分
DayOfWeek() 返回一个日期对于的星期值(星期几)
Hour() 返回时间中的小时部分
Minute() 返回时间中的分钟部分
Second() 返回时间中的秒部分

常见的数值处理函数

函数 说明
Abs() 返回一个数的绝对值
Exp() 返回一个数的指数值
Sqrt() 返回一个数的平方根
Sin() 返回一个角度值的正弦
Cos() 返回一个角度值的余弦
Tan() 返回一个角度值的正切
Mod(x, y) 返回x被y除后的余数
Rand() 返回一个随机数
Pi() 返回圆周率
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • ORA-00001: 违反唯一约束条件 (.) 错误说明:当在唯一索引所对应的列上键入重复值时,会触发此异常。 O...
    我想起个好名字阅读 4,966评论 0 9
  • 今天看到一位朋友写的mysql笔记总结,觉得写的很详细很用心,这里转载一下,供大家参考下,也希望大家能关注他原文地...
    信仰与初衷阅读 4,720评论 0 30
  • 第三课: 排序检索数据 distinct关键字:distinct 列名1,列名2,列名3DISTINCT 关键字会...
    VictorBXv阅读 1,460评论 0 8
  • 前言 读《sql必知必会 第四版》随手做的笔记,写的比较乱,可读性并不好,读的是中文版,翻译过来的感觉有点怪怪的。...
    _老徐_阅读 610评论 0 0
  • 又是一个展新十二月,胡先生和我好像一时间不知道该写点什么。 昨晚两个人聊到十一点半左右,万分困意下挂了电话,聊了很...
    慕不照的君阅读 124评论 0 0