SQL 教程

SQL 基础教程

SQL简介


​ SQL(结构化查询语言)是用于访问和操作数据库中的数据的标准数据库编程语言。

​ SQL是关系数据库系统的标准语言。所有关系数据库管理系统(RDMS),如MySQL、MS Access、Oracle、Sybase、Informix、Postgres和SQL Server都使用SQL作为它们的标准数据库语言。

​ 为了处理数据库和数据库相关的编程,程序员需要有一些介质,或者可以说接口来详细说明一组命令或代码来处理数据库或访问数据库的数据。在本章中,将简要介绍在学习SQL的过程中您将学习的术语。

你会从SQL中学到什么?


​ SQL为结构化查询语言提供了独特的学习和数据库处理技术,并将帮助您更好地控制SQL查询并有效处理这些代码。由于SQL帮助您包括数据库创建,数据库或表删除,获取行数据和修改这些数据等,并行SQL使得事情自动和平滑,最终用户可以轻松访问和处理该应用程序的数据。

SQL 是什么?


  • SQL 发音为“sequel”。
  • SQL 指结构化查询语言,全称是 Structured Query Language(是最初由IBM开发)。
  • SQL 是关系数据库系统的标准语言。
  • SQL 是一种 ANSI(American National Standards Institute 美国国家标准化组织)标准的计算机语言。

SQL 能做什么?


  • SQL可以创建新的数据库及其对象(表,索引,视图,存储过程,函数和触发器)。

  • SQL可以修改现有数据库的结构。

  • SQL可以从数据库中删除(删除)对象。

  • SQL可以TRUNCATE(删除)表中的所有记录。

  • SQL可以对数据字典进行COMMENT。

  • SQL可以RENAME一个对象。

  • SQL可以从数据库中选择(检索)数据。

  • SQL可以将数据插入到表中。

  • SQL可以更新表中的现有数据。

  • SQL可以从数据库表中删除记录。

  • SQL可以在数据库中设置用户的GRANT和REVOKE权限。

    SQL标准命令


    ​ 与关系数据库交互的标准SQL命令是创建、选择、插入、更新、删除和删除,简单分为以下几组:

    DDL(数据定义语言)

    ​ 数据定义语言用于改变数据库结构,包括创建、更改和删除数据库对象。用于操纵表结构的数据定义语言命令有:

    • CREATE TABLE-- 创建(在数据库中创建新表、表视图或其他对象)
    • ALTER TABLE-- 更改 (修改现有的数据库对象,如表)
    • DROP TABLE-- 删除 (删除数据库中的整个表、表或其他对象的视图)

    DML(数据操纵语言)

    ​ 数据操纵语言用于检索、插入和修改数据,数据操纵语言是最常见的SQL命令。

    ​ 数据操纵语言命令包括:

    • INSERT-- 插入 (创建记录)
    • DELETE-- 删除 (删除记录)
    • UPDATE-- 修改(修改记录)
    • SELECT -- 检索 (从一个或多个表检索某些记录)

    DCL(数据控制语言)

    ​ 数据控制语言为用户提供权限控制命令。

    ​ 用于权限控制的命令有:

    • GRANT-- 授予权限
    • REVOKE-- 撤销已授予的权限

    更多


SQL 高级教程

SQL SELECT TOP, LIMIT, ROWNUM


  • SELECT TOP 子句用于指定要返回的记录数量。
  • SELECT TOP子句在包含数千条记录的大型表上很有用。返回大量记录会影响性能。

**注:**并不是所有的数据库系统都支持SELECT TOP子句。MySQL支持LIMIT子句来选择有限数量的记录,而Oracle使用ROWNUM。

SQL Server / MS Access 语法
SELECT TOP number|percent column_name(s)
FROM table_name
WHERE condition;
MySQL语法:
SELECT column_name(s)
FROM table_name
WHERE condition
LIMIT number;
实例
SELECT *
FROM Persons
LIMIT 5;
Oracle 语法
SELECT column_name(s)
FROM table_name
WHERE ROWNUM <= number;
实例
SELECT *
FROM Persons
WHERE ROWNUM <=5;

SQL LIKE 运算符


在WHERE子句中使用LIKE运算符来搜索列中的指定模式。

有两个通配符与LIKE运算符一起使用:

  • - 百分号表示零个,一个或多个字符
  • _ - 下划线表示单个字符

注意: MS Access使用问号(?)而不是下划线(_)。

百分号和下划线也可以组合使用!

SQL LIKE 语法
SELECT column1, column2, ...
FROM table_name
WHERE columnN LIKE pattern;

提示:您还可以使用AND或OR运算符组合任意数量的条件。

LIKE 运算符 描述
WHERE CustomerName LIKE 'a%' 查找以“a”开头的任何值
WHERE CustomerName LIKE '%a' 查找以“a”结尾的任何值
WHERE CustomerName LIKE '%or%' 在任何位置查找任何具有“or”的值
WHERE CustomerName LIKE '_r%' 在第二个位置查找任何具有“r”的值
WHERE CustomerName LIKE 'a_%_%' 查找以“a”开头且长度至少为3个字符的值
WHERE ContactName LIKE 'a%o' 找到以"a"开头,以"o"结尾的值

SQL IN 运算符


IN运算符允许您在WHERE子句中指定多个值。

IN运算符是多个OR条件的简写。

SQL IN 语法
SELECT column_name(s)
FROM table_name
WHERE column_name IN (value1, value2, ...);
或者
SELECT column_name(s)
FROM table_name
WHERE column_name IN (SELECT STATEMENT);

SQL BETWEEN 运算符


BETWEEN运算符用于选取介于两个值之间的数据范围内的值。

BETWEEN运算符选择给定范围内的值。值可以是数字,文本或日期。

BETWEEN运算符是包含性的:包括开始和结束值,且开始值需小于结束值。

SQL BETWEEN 语法
SELECT column_name(s)
FROM table_name
WHERE column_name BETWEEN value1 AND value2;

要否定BETWEEN运算符的结果,可以添加NOT运算符:

SELECT column_name(s)
FROM table_name
WHERE column_name NOT BETWEEN value1 AND value2;

SQL 数据类型快速参考手册


然而,不同的数据库为数据类型定义提供了不同的选择。  下表显示了不同数据库平台上某些数据类型的通用名称:

数据类型 Access SQLServer Oracle MySQL PostgreSQL
boolean Yes/No Bit Byte N/A Boolean
integer Number (integer) Int Number Int Integer Int Integer
float Number (single) Float Real Number Float Numeric
currency Currency Money N/A N/A Money
string (fixed) N/A Char Char Char Char
string (variable) Text (<256) Memo (65k+) Varchar Varchar Varchar2 Varchar Varchar
binary object OLE Object Memo Binary (fixed up to 8K) Varbinary (<8K) Image (<2GB) Long Raw Blob Text Binary Varbinary

SQL 连接(Joins)


SQL JOIN 子句用于把来自两个或多个表的行结合起来,基于这些表之间的共同字段。

简单地说,就是先确定一个主表作为结果集,然后,把其他表的行有选择性地“连接”在主表结果集上。

最常见的 JOIN 类型:SQL INNER JOIN(简单的 JOIN)。 SQL INNER JOIN 从多个表中返回满足 JOIN 条件的所有行。

让我们看看选自 "Orders" 表的数据:

OrderID CustomerID OrderDate
10308 2 1996-09-18
10309 37 1996-09-19
10310 77 1996-09-20

然后,看看选自 "Customers" 表的数据:

CustomerID CustomerName ContactName Country
1 Alfreds Futterkiste Maria Anders Germany
2 Ana Trujillo Emparedados y helados Ana Trujillo Mexico
3 Antonio Moreno Taquería Antonio Moreno Mexico

请注意,"Orders" 表中的 "CustomerID" 列指向 "Customers" 表中的客户。上面这两个表是通过 "CustomerID" 列联系起来的。

然后,如果我们运行下面的 SQL 语句(包含 INNER JOIN):

实例

SELECT Orders.OrderID, Customers.CustomerName, Orders.OrderDate FROM Orders INNER JOIN Customers ON Orders.CustomerID=Customers.CustomerID;

运行结果如下所示:

OrderID CustomerName OrderDate
10308 Ana Trujillo Emparedados y helados 1996-09-18

值得注意的是,连接是在WHERE子句中执行的。

可以使用几个操作符连接表,例如=、<、>、<=、>=、!=、BETWEEN、LIKE、 和NOT。

不同的 SQL JOIN

在我们继续讲解实例之前,我们先列出您可以使用的不同的 SQL JOIN 类型:

  • INNER JOIN:如果表中有至少一个匹配,则返回行
  • LEFT JOIN:即使右表中没有匹配,也从左表返回所有的行
  • RIGHT JOIN:即使左表中没有匹配,也从右表返回所有的行
  • FULL JOIN:只要其中一个表中存在匹配,则返回行
  • SELF JOIN:用于将表连接到自己,就好像该表是两个表一样,临时重命名了SQL语句中的至少一个表
  • CARTESIAN JOIN:从两个或多个连接表返回记录集的笛卡儿积
SQL INNER JOIN 关键字(内部连接)

内部链接INNER JOIN关键字选择两个表中具有匹配值的记录。

######## SQL INNER JOIN 语法

SELECT column_name(s)
FROM table1
INNER JOIN table2 ON table1.column_name = table2.column_name;

**注释:**INNER JOIN 与 JOIN 是相同的。

SQL INNER JOIN

######## 加入三张表


以下SQL语句选择包含客户和货运单信息的所有订单:

代码示例:

SELECT Orders.OrderID, Customers.CustomerName, Shippers.ShipperName
FROM ((Orders
INNER JOIN Customers ON Orders.CustomerID = Customers.CustomerID)
INNER JOIN Shippers ON Orders.ShipperID = Shippers.ShipperID);
SQL 左连接 LEFT JOIN 关键字

SQL左链接LEFT JOIN关键字返回左表(表1)中的所有行,即使在右表(表2)中没有匹配。如果在正确的表中没有匹配,结果是NULL。

######## SQL LEFT JOIN 语法

SELECT column_name(s)                
FROM table1                
LEFT JOIN table2                
ON table1.column_name=table2.column_name;       

或:

SELECT column_name(s)                
FROM table1                
LEFT OUTER JOIN table2                
ON table1.column_name=table2.column_name;      

**注释:**在一些数据库中,LEFT JOIN称为LEFT OUT ER JOIN。

SQL LEFT JOIN

SQL右连接 RIGHT JOIN 关键字

SQL右链接 RIGHT JOIN 关键字返回右表(table2)的所有行,即使在左表(table1)上没有匹配。如果左表没有匹配,则结果为NULL。

######## SQL RIGHT JOIN 语法

SELECT column_name(s)
FROM table1
RIGHT JOIN table2 ON table1.column_name = table2.column_name;

**注释:**在一些数据库中,RIGHT JOIN 称为 RIGHT OUTER JOIN。

SQL RIGHT JOIN

######SQL FULL OUTER JOIN 关键字


当左(表1)或右(表2)表记录匹配时,FULL OUTER JOIN关键字将返回所有记录。

注意: FULL OUTER JOIN可能会返回非常大的结果集!

########SQL FULL OUTER JOIN 语法

SELECT column_name(s)
FROM table1
FULL OUTER JOIN table2 ON table1.column_name = table2.column_name;

SQL FULL OUTER JOIN

######SQL自连接


自联接是一种常规联接,但表本身是连接的。

########Self JOIN语法

SELECT column_name(s)
FROM table1 T1, table1 T2
WHERE condition;

演示数据库


在本教程中,我们将使用着名的Northwind示例数据库。

以下是"Customers"表中的选择:

CustomerID CustomerName ContactName Address City PostalCode Country
1 Alfreds Futterkiste Maria Anders Obere Str. 57 Berlin 12209 Germany
2 Ana Trujillo Emparedados y helados Ana Trujillo Avda. de la Constitución 2222 México D.F. 05021 Mexico
3 Antonio Moreno Taquería Antonio Moreno Mataderos 2312 México D.F. 05023 Mexico

######## SQL Self JOIN示例


以下SQL语句匹配来自同一城市的客户:

######### 代码示例

SELECT A.CustomerName AS CustomerName1, B.CustomerName AS CustomerName2, A.City
FROM Customers A, Customers B
WHERE A.CustomerID <> B.CustomerID
AND A.City = B.City 
ORDER BY A.City;

SQL UNION 运算符


UNION运算符用于组合两个或更多SELECT语句的结果集,而不返回任何重复的行。

  • UNION中的每个SELECT语句必须具有相同的列数
  • 这些列也必须具有相似的数据类型
  • 每个SELECT语句中的列也必须以相同的顺序排列
  • 每个SELECT语句必须有相同数目的列表达式
  • 但是每个SELECT语句的长度不必相同
SQL UNION 语法1
SELECT column_name(s) FROM table1
UNION
SELECT column_name(s) FROM table2;

**注释:**默认情况下,UNION 运算符选择一个不同的值。如果允许重复值,请使用 UNION ALL。

SQL UNION 语法2
SELECT column_name(s) FROM table1
[WHERE condition]

UNION
SELECT column_name(s) FROM table2
[WHERE condition];

给定的条件可以是基于您的需求的任何给定表达式。

SQL UNION ALL 语法1

UNION All运算符用于组合两个SELECT语句(包括重复行)的结果。

适用于UNION子句的相同规则将适用于UNION All操作符。

SELECT column_name(s) FROM table1
UNION ALL
SELECT column_name(s) FROM table2;

**注释:**UNION结果集中的列名总是等于UNION中第一个SELECT语句中的列名。

SQL UNION ALL 语法2
SELECT column_name(s) FROM table1
[WHERE condition]
UNION ALL
SELECT column_name(s) FROM table2
[WHERE condition];

SQL SELECT INTO 语句


使用 SQL,您可以将信息从一个表中复制到另一个表中。

SELECT INTO 语句从一个表中复制数据,然后将数据插入到另一个新表中。

SQL SELECT INTO 语法

我们可以把所有的列都复制到新表中:

SELECT *
INTO newtable [IN externaldb]
FROM table1;

或者只复制希望的列插入到新表中:

SELECT column_name(s)
INTO newtable [IN externaldb]
FROM table1;

SQL INSERT INTO SELECT 语句


使用SQL,您可以将信息从一个表中复制到另一个表中。

INSERT INTO SELECT 语句从表中复制数据,并将数据插入现有的表中。目标表中的任何现有行都不会受到影响。

SQL INSERT INTO SELECT 语法

我们可以将所有列从一个表中复制到另一个已经存在的表中:

INSERT INTO table2              
SELECT * FROM table1;    

或者我们可以把想要的列复制到另一个现有的表中:

INSERT INTO table2               
(column_name(s))              
SELECT column_name(s)             
FROM table1;   

insert into select和select into区别,前者目标表必须存在

SQL 撤销索引、撤销表以及撤销数据库


通过使用 DROP 语句,可以轻松地删除索引、表和数据库。

DROP INDEX 语句


DROP INDEX 语句用于删除表中的索引。

用于 MS Access 的 DROP INDEX 语法:
DROP INDEX index_name ON table_name      
用于 MS SQL Server 的 DROP INDEX 语法:
DROP INDEX table_name.index_name     
用于 DB2/Oracle 的 DROP INDEX 语法:
DROP INDEX index_name   
用于 MySQL 的 DROP INDEX 语法:
ALTER TABLE table_name DROP INDEX index_name       

DROP TABLE 语句


DROP TABLE 语句用于删除表。

DROP TABLE table_name      

DROP DATABASE 语句


DROP DATABASE 语句用于删除数据库。

DROP DATABASE database_name    

TRUNCATE TABLE 语句


如果我们只需要删除表中的数据,而不删除表本身,那么我们该怎么做?

使用TRUNCATE TABLE语句:

TRUNCATE TABLE table_name   

SQL CREATE/ALTER语句

SQL CREATE DATABASE 语法
CREATE DATABASE dbname;
SQL CREATE TABLE 语法
CREATE TABLE table_name                
(                
column_name1 data_type(size),                
column_name2 data_type(size),                
column_name3 data_type(size),                
....                
);       
SQL ALTER TABLE 语法

若要向表中添加列,请使用以下语法:

ALTER TABLE table_name                
ADD column_name datatype      

若要删除表中的列,请使用以下语法(请注意,一些数据库系统不允许这样删除数据库表中的列):

ALTER TABLE table_name                
DROP COLUMN column_name      

若要更改表中列的数据类型,请使用以下语法:

SQL Server / MS Access:

ALTER TABLE table_name                
ALTER COLUMN column_name datatype       

My SQL / Oracle:

ALTER TABLE table_name                
MODIFY COLUMN column_name datatype        

SQL AUTO INCREMENT 字段


Auto-increment 会在新记录插入表中时生成一个唯一的数字。

AUTO INCREMENT 字段


我们通常希望在每次插入新记录时自动创建主键字段的值。

我们可以在表中创建一个自动增量(auto-increment)字段。

用于 MySQL 的语法


以下SQL语句将 "Persons" 表中的“ID”列定义为自动递增(auto-increment)主键字段:

CREATE TABLE Persons                
(                
ID int NOT NULL AUTO_INCREMENT,                
LastName varchar(255) NOT NULL,                
FirstName varchar(255),                
Address varchar(255),                
City varchar(255),                
PRIMARY KEY (ID)                
)

MySQL使用AUTO_INREMENT关键字来执行自动增量( auto-increment )任务。

默认情况下,AUTO_INREMENT的起始值为1,每个新记录增加1。

若要以其他值开始AUTO_INREMENT序列,请使用以下SQL语法:

ALTER TABLE Persons AUTO_INCREMENT=100       

要在 "Persons" 表中插入新记录,我们不需要为"ID"栏指定值(自动添加唯一值):

INSERT INTO Persons (FirstName,LastName)                
VALUES ('Lars','Monsen')        

上面的SQL语句在 "Persons" 表中插入一个新记录。“ID”栏将得到唯一值。"FirstName"栏设置为"Lars","LastName"栏设置为"Monsen"。

用于 SQL Server 的语法


以下SQL语句将 "Persons" 表中的“ID”列定义为自动递增( auto-increment )主键字段:

CREATE TABLE Persons                
(                
ID int IDENTITY(1,1) PRIMARY KEY,                
LastName varchar(255) NOT NULL,                
FirstName varchar(255),                
Address varchar(255),                
City varchar(255)                
)       

MS SQL Server使用IDENTITY关键字执行自动增量( auto-increment )任务。

在上面的示例中,IDENTITY的起始值为1,每个新记录增量为1。

提示:指定“ID”列以10开头,并递增5,将标识( identity )更改为IDENTITY(10,5)。

要在 "Persons" 表中插入新记录,我们不需要为"ID"栏指定值(自动添加唯一值):

INSERT INTO Persons (FirstName,LastName)                
VALUES ('Lars','Monsen')        

上面的 SQL 语句在 "Persons" 表中插入一个新记录。“ID”栏将得到唯一值。"FirstName"栏设置为"Lars","LastName"栏设置为"Monsen"。

用于 Access 的语法


以下 SQL 语句将 "Persons" 表中的“ID”列定义为自动递增( auto-increment )主键字段:

CREATE TABLE Persons                
(                
ID Integer PRIMARY KEY AUTOINCREMENT,                
LastName varchar(255) NOT NULL,                
FirstName varchar(255),                
Address varchar(255),                
City varchar(255)                
)       

MS Access使用 AUTOINCREMENT 关键字执行自动增量( auto-increment )任务。

默认情况下,AUTOINCREMEN的起始值为1,每个新记录递增 1。

**提示:**指定“ID”栏以10开头,并递增5,将自动递增( autoincrement )更改为自动递增(105)( AUTOINCREMENT(10,5))。

要在 "Persons" 表中插入新记录,我们不需要为"ID"栏指定值(自动添加唯一值):

INSERT INTO Persons (FirstName,LastName)                
VALUES ('Lars','Monsen')        

上面的 SQL 语句在 "Persons" 表中插入一个新记录。“ID”栏将得到唯一值。"FirstName"栏设置为"Lars","LastName"栏设置为"Monsen"。

语法 for Oracle


在 Oracle 中,代码有点复杂。

您必须使用序列( sequence )对象(该对象生成数字序列)创建自动增量( auto-increment )字段。

使用以下CREATSEQUENT语法:

CREATE SEQUENCE seq_person                
MINVALUE 1                
START WITH 1                
INCREMENT BY 1                
CACHE 10        

上面的代码创建了一个名为seq_pean的序列( sequence) 对象,它以1开头,以1递增。此对象缓存10个值以提高性能。缓存选项指定要存储多少序列值以提高访问速度。

要在"Persons" 表中插入新记录,我们必须使用nextval函数,该函数从seq_hor序列检索下一个值:

INSERT INTO Persons (ID,FirstName,LastName)                
VALUES (seq_person.nextval,'Lars','Monsen')       

上面的SQL语句在 "Persons" 表中插入一个新记录。"ID" 列从 seq_person 序列中分配下一个数字。"FirstName"栏设置为"Lars","LastName"栏设置为"Monsen"。

SQL 视图(Views)


视图是可视化的表。

本章讲解如何创建、更新和删除视图。

SQL CREATE VIEW 语句


在 SQL 中,视图是基于 SQL 语句的结果集的可视化表。

视图包含行和列,就像真正的表一样。视图中的字段是一个或多个数据库中真实表中的字段。

您可以添加 SQL 函数,在哪里添加,并将语句连接到视图,或者您可以呈现数据,就像数据来自单个表一样。

SQL CREATE VIEW 语法
CREATE VIEW view_name AS                
SELECT column_name(s)                
FROM table_name                
WHERE condition      

**注释:**视图总是显示最新数据!每当用户查询视图时,数据库引擎就使用视图的 SQL 语句重新构建数据。

SQL CREATE VIEW 实例


示例数据库 Northwind 默认安装了一些视图。

"Current Product List"(当前产品列表)视图从"Products"表中列出了所有正在使用的产品(未停产的产品)。这个视图使用下面的 SQL 创建:

CREATE VIEW [Current Product List] AS                
SELECT ProductID,ProductName                
FROM Products                
WHERE Discontinued=No      

我们可以像这样查询上面这个视图:

SELECT * FROM [Current Product List]        

Northwind 样本数据库的另一个视图会选取 "Products" 表中所有单位价格高于平均单位价格的产品:

CREATE VIEW [Products Above Average Price] AS                
SELECT ProductName,UnitPrice                
FROM Products                
WHERE UnitPrice>(SELECT AVG(UnitPrice) FROM Products)     

我们可以像这样查询上面这个视图:

SELECT * FROM [Products Above Average Price]        

Northwind 样本数据库的另一个视图会计算在 1997 年每个种类的销售总数。请注意,这个视图会从另一个名为 "Product Sales for 1997" 的视图那里选取数据:

CREATE VIEW [Category Sales For 1997] AS                
SELECT DISTINCT CategoryName,Sum(ProductSales) AS CategorySales                
FROM [Product Sales for 1997]                
GROUP BY CategoryName       

我们可以像这样查询上面这个视图:

SELECT * FROM [Category Sales For 1997]       

我们也可以向查询添加条件。现在,我们仅仅需要查看 "Beverages" 类的销售总数:

SELECT * FROM [Category Sales For 1997]                
WHERE CategoryName='Beverages'        

SQL 更新视图


您可以使用下面的语法来更新视图:

SQL CREATE OR REPLACE VIEW 语法
CREATE OR REPLACE VIEW view_name AS                
SELECT column_name(s)                
FROM table_name                
WHERE condition        

现在,我们希望向 "Current Product List" 视图添加 "Category" 列。我们将通过下列 SQL 更新视图:

CREATE OR REPLACE VIEW [Current Product List] AS                
SELECT ProductID,ProductName,Category                
FROM Products                
WHERE Discontinued=No        

SQL 撤销视图


您可以通过 DROP VIEW 命令来删除视图。

SQL DROP VIEW 语法
DROP VIEW view_name     

SQL 别名(Aliases)


通过使用 SQL,可以为表名称或列名称指定别名(Alias)。

  • SQL 别名用于为表或表中的列提供临时名称,数据库中的实际表名不会更改。
  • SQL 别名通常用于使列名更具可读性。
  • SQL 一个别名只存在于查询期间。
  • 表别名的使用是在特定SQL语句中重命名表。
  • 列别名用于为特定SQL查询重命名表的列。
列的 SQL Alias 语法
SELECT column_name AS alias_name
FROM table_name;
WHERE [condition];
表的 SQL Alias 语法
SELECT column_name(s)
FROM table_name AS alias_name;
WHERE [condition];

SQL 约束


约束是作用于数据表中列上的规则,用于限制表中数据的类型。约束的存在保证了数据库中数据的精确性和可靠性。

约束有列级和表级之分,列级约束作用于单一的列,而表级约束作用于整张数据表。

下面是 SQL 中常用的约束,这些约束虽然已经在关系型数据库管理系统一章中讨论过了,但是仍然值得在这里回顾一遍。

约束可以在创建表时规定(通过 CREATE TABLE 语句),或者在表创建之后规定(通过 ALTER TABLE 语句)。

SQL 克隆数据表


有些情况下,你可能需要原样拷贝某张数据表。但是,CREATE TABEL 却不能满足你的需要,因为复制表必须和原表拥有一样的索引、默认值等等。

如果你在使用 MySQL 关系型数据库管理系统的话,下面几个步骤可以帮你解决这个问题:

  • 使用 SHOW CREATE TABLE 命令来获取一条指定了原表的结构、索引等信息的 CREATE TABLE 语句。
  • 将语句中的表名修改为克隆表的名字,然后执行该语句。这样你就可以得到一张与原表完全相同的克隆表了。
  • 如果你还想要复制表中的数据的话,请执行 INSERT INTO ... SELECT 语句。

示例:


请尝试下面的示例,为 TUTORIALS_TBL 创建一张克隆表,其结构如下所示:

步骤一:

获取数据表的完整结构:

SQL> SHOW CREATE TABLE TUTORIALS_TBL \G;
*************************** 1. row ***************************
       Table: TUTORIALS_TBL
Create Table: CREATE TABLE `TUTORIALS_TBL` (
  `tutorial_id` int(11) NOT NULL auto_increment,
  `tutorial_title` varchar(100) NOT NULL default '',
  `tutorial_author` varchar(40) NOT NULL default '',
  `submission_date` date default NULL,
  PRIMARY KEY  (`tutorial_id`),
  UNIQUE KEY `AUTHOR_INDEX` (`tutorial_author`)
) TYPE=MyISAM
1 row in set (0.00 sec)
步骤二:

改变表名,创建新表:

SQL> CREATE TABLE `CLONE_TBL` (
  -> `tutorial_id` int(11) NOT NULL auto_increment,
  -> `tutorial_title` varchar(100) NOT NULL default '',
  -> `tutorial_author` varchar(40) NOT NULL default '',
  -> `submission_date` date default NULL,
  -> PRIMARY KEY  (`tutorial_id`),
  -> UNIQUE KEY `AUTHOR_INDEX` (`tutorial_author`)
  -> ) TYPE=MyISAM;
Query OK, 0 rows affected (1.80 sec)
步骤三:

执行完步骤二之后,数据库就会有克隆表了。如果你还想要复制旧表中的数据的话,可以执行 INSERT INTO... SELECT 语句。

SQL> INSERT INTO CLONE_TBL (tutorial_id,
    ->                        tutorial_title,
    ->                        tutorial_author,
    ->                        submission_date)
    -> SELECT tutorial_id,tutorial_title,
    ->        tutorial_author,submission_date,
    -> FROM TUTORIALS_TBL;
Query OK, 3 rows affected (0.07 sec)
Records: 3  Duplicates: 0  Warnings: 0

最终,你将如期拥有一张完全相同的克隆表。

SQL 索引


索引是一种特殊的查询表,可以被数据库搜索引擎用来加速数据的检索。简单说来,索引就是指向表中数据的指针。数据库的索引同书籍后面的索引非常相像。

例如,如果想要查阅一本书中与某个特定主题相关的所有页面,你会先去查询索引(索引按照字母表顺序列出了所有主题),然后从索引中找到一页或者多页与该主题相关的页面。

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

创建索引需要使用 CREATE INDEX 语句,该语句允许对索引命名,指定要创建索引的表以及对哪些列进行索引,还可以指定索引按照升序或者降序排列。

同 UNIQUE 约束一样,索引可以是唯一的。这种情况下,索引会阻止列中(或者列的组合,其中某些列有索引)出现重复的条目。

CREATE INDEX 命令:


CREATE INDEX命令的基本语法如下:

CREATE INDEX index_name ON table_name;
单列索引:

单列索引基于单一的字段创建,其基本语法如下所示:

CREATE INDEX index_name
ON table_name (column_name);
唯一索引:

唯一索引不止用于提升查询性能,还用于保证数据完整性。唯一索引不允许向表中插入任何重复值。其基本语法如下所示:

CREATE UNIQUE INDEX index_name
on table_name (column_name);
聚簇索引:

聚簇索引在表中两个或更多的列的基础上建立。其基本语法如下所示:

CREATE INDEX index_name
on table_name (column1, column2);

创建单列索引还是聚簇索引,要看每次查询中,哪些列在作为过滤条件的 WHERE 子句中最常出现。

如果只需要一列,那么就应当创建单列索引。如果作为过滤条件的 WHERE 子句用到了两个或者更多的列,那么聚簇索引就是最好的选择。

隐式索引:

隐式索引由数据库服务器在创建某些对象的时候自动生成。例如,对于主键约束和唯一约束,数据库服务器就会自动创建索引。

DROP INDEX 命令:

索引可以用 SQL DROP 命令删除。删除索引时应当特别小心,数据库的性能可能会因此而降低或者提高。

其基本语法如下:

DROP INDEX table_name.index_name;

什么时候应当避免使用索引?


尽管创建索引的目的是提升数据库的性能,但是还是有一些情况应当避免使用索引。下面几条指导原则给出了何时应当重新考虑是否使用索引:

  • 小的数据表不应当使用索引;
  • 需要频繁进行大批量的更新或者插入操作的表;
  • 如果列中包含大数或者 NULL 值,不宜创建索引;
  • 频繁操作的列不宜创建索引。

SQL 子查询


子查询(Sub Query)或者说内查询(Inner Query),也可以称作嵌套查询(Nested Query),是一种嵌套在其他 SQL 查询的 WHERE 子句中的查询。

子查询用于为主查询返回其所需数据,或者对检索数据进行进一步的限制。

子查询可以在 SELECT、INSERT、UPDATE 和 DELETE 语句中,同 =、<、>、>=、<=、IN、BETWEEN 等运算符一起使用。

使用子查询必须遵循以下几个规则:

  • 子查询必须括在圆括号中。
  • 子查询的 SELECT 子句中只能有一个列,除非主查询中有多个列,用于与子查询选中的列相比较。
  • 子查询不能使用 ORDER BY,不过主查询可以。在子查询中,GROUP BY 可以起到同 ORDER BY 相同的作用。
  • 返回多行数据的子查询只能同多值操作符一起使用,比如 IN 操作符。
  • SELECT 列表中不能包含任何对 BLOB、ARRAY、CLOB 或者 NCLOB 类型值的引用。
  • 子查询不能直接用在集合函数中。
  • BETWEEN 操作符不能同子查询一起使用,但是 BETWEEN 操作符可以用在子查询中。

SELECT 语句中的子查询


通常情况下子查询都与 SELECT 语句一起使用,其基本语法如下所示:

SELECT column_name [, column_name ]
FROM   table1 [, table2 ]
WHERE  column_name OPERATOR
      (SELECT column_name [, column_name ]
      FROM table1 [, table2 ]
      [WHERE])

示例:


考虑 CUSTOMERS 表,表中记录如下所示:

+----+----------+-----+-----------+----------+
| ID | NAME     | AGE | ADDRESS   | SALARY   |
+----+----------+-----+-----------+----------+
|  1 | Ramesh   |  35 | Ahmedabad |  2000.00 |
|  2 | Khilan   |  25 | Delhi     |  1500.00 |
|  3 | kaushik  |  23 | Kota      |  2000.00 |
|  4 | Chaitali |  25 | Mumbai    |  6500.00 |
|  5 | Hardik   |  27 | Bhopal    |  8500.00 |
|  6 | Komal    |  22 | MP        |  4500.00 |
|  7 | Muffy    |  24 | Indore    | 10000.00 |
+----+----------+-----+-----------+----------+

现在,让我们试一下在 SELECT 语句中进行子查询:

SQL> SELECT * 
     FROM CUSTOMERS 
     WHERE ID IN (SELECT ID 
                  FROM CUSTOMERS 
                  WHERE SALARY > 4500) ;

上述语句的执行结果如下所示:

+----+----------+-----+---------+----------+
| ID | NAME     | AGE | ADDRESS | SALARY   |
+----+----------+-----+---------+----------+
|  4 | Chaitali |  25 | Mumbai  |  6500.00 |
|  5 | Hardik   |  27 | Bhopal  |  8500.00 |
|  7 | Muffy    |  24 | Indore  | 10000.00 |
+----+----------+-----+---------+----------+

INSERT 语句中的子查询:


子查询还可以用在 INSERT 语句中。INSERT 语句可以将子查询返回的数据插入到其他表中。子查询中选取的数据可以被任何字符、日期或者数值函数所修饰。

其基本语法如下所示:

INSERT INTO table_name [ (column1 [, column2 ]) ]
           SELECT [ *|column1 [, column2 ]
           FROM table1 [, table2 ]
           [ WHERE VALUE OPERATOR ]

示例:


考虑与 CUSTOMERS 表拥有相似结构的 CUSTOMERS_BKP 表。现在要将 CUSTOMER 表中所有的数据复制到 CUSTOMERS_BKP 表中,代码如下:

SQL> INSERT INTO CUSTOMERS_BKP
     SELECT * FROM CUSTOMERS 
     WHERE ID IN (SELECT ID 
                  FROM CUSTOMERS) ;

UPDATE 语句中的子查询:


子查询可以用在 UPDATE 语句中。当子查询同 UPDATE 一起使用的时候,既可以更新单个列,也可更新多个列。

其基本语法如下:

UPDATE table
SET column_name = new_value
[ WHERE OPERATOR [ VALUE ]
   (SELECT COLUMN_NAME
   FROM TABLE_NAME)
   [ WHERE) ]

示例:


假设我们有一份 CUSTOMERS_BKP 表作为 CUSTOMERS 表的备份。

下面的示例将 CUSTOMERS 表中所有 AGE 大于或者等于 27 的客户的 SALARY 字段都变为了原来的 0.25 倍:

SQL> UPDATE CUSTOMERS
     SET SALARY = SALARY * 0.25
     WHERE AGE IN (SELECT AGE FROM CUSTOMERS_BKP
                   WHERE AGE >= 27 );

这将影响两行数据,随后 CUSTOMERS 表中的记录将如下所示:

+----+----------+-----+-----------+----------+
| ID | NAME     | AGE | ADDRESS   | SALARY   |
+----+----------+-----+-----------+----------+
|  1 | Ramesh   |  35 | Ahmedabad |   125.00 |
|  2 | Khilan   |  25 | Delhi     |  1500.00 |
|  3 | kaushik  |  23 | Kota      |  2000.00 |
|  4 | Chaitali |  25 | Mumbai    |  6500.00 |
|  5 | Hardik   |  27 | Bhopal    |  2125.00 |
|  6 | Komal    |  22 | MP        |  4500.00 |
|  7 | Muffy    |  24 | Indore    | 10000.00 |
+----+----------+-----+-----------+----------+

DELETE 语句中的子查询:


如同前面提到的其他语句一样,子查询还可以同 DELETE 语句一起使用。

其基本语法如下所示:

DELETE FROM TABLE_NAME
[ WHERE OPERATOR [ VALUE ]
   (SELECT COLUMN_NAME
   FROM TABLE_NAME)
   [ WHERE) ]

示例:


假设我们有一份 CUSTOMERS_BKP 表作为 CUSTOMERS 表的备份。

下面的示例将从 CUSTOMERS 表中删除所有 AGE 大于或者等于 27 的记录:

SQL> DELETE FROM CUSTOMERS
     WHERE AGE IN (SELECT AGE FROM CUSTOMERS_BKP
                   WHERE AGE > 27 );

这将影响两行数据,随后 CUSTOMERS 表中的记录将如下所示:

+----+----------+-----+---------+----------+
| ID | NAME     | AGE | ADDRESS | SALARY   |
+----+----------+-----+---------+----------+
|  2 | Khilan   |  25 | Delhi   |  1500.00 |
|  3 | kaushik  |  23 | Kota    |  2000.00 |
|  4 | Chaitali |  25 | Mumbai  |  6500.00 |
|  6 | Komal    |  22 | MP      |  4500.00 |
|  7 | Muffy    |  24 | Indore  | 10000.00 |
+----+----------+-----+---------+----------+

SQL TRUNCATE TABLE 命令


SQL TRUNCATE TABLE 命令用于删除现有数据表中的所有数据。

你也可以使用 DROP TABLE 命令来删除整个数据表,不过 DROP TABLE 命令不但会删除表中所有数据,还会将整个表结构从数据库中移除。如果想要重新向表中存储数据的话,必须重建该数据表。

语法:


TRUNCATE TABLE 的基本语法如下所示:

TRUNCATE TABLE  table_name;

SQL 处理重复数据


有时候,数据表中会存在相同的记录。在获取表中记录时,相较于取得重复记录来说,取得唯一的记录显然更有意义。

我们之前讨论过的 SQL DISTINCT 关键字,与 SELECT 语句一起使用可以时,可以达到消除所有重复记录,只返回唯一记录的目的。

语法:


利用 DISTINCT 关键字来消除重复记录的基本语法如下所示:

SELECT DISTINCT column1, column2,.....columnN 
FROM table_name
WHERE [condition]

SQL 使用视图


视图无非就是存储在数据库中并具有名字的 SQL 语句,或者说是以预定义的 SQL 查询的形式存在的数据表的成分。

视图可以包含表中的所有列,或者仅包含选定的列。视图可以创建自一个或者多个表,这取决于创建该视图的 SQL 语句的写法。

视图,一种虚拟的表,允许用户执行以下操作:

  • 以用户或者某些类型的用户感觉自然或者直观的方式来组织数据;
  • 限制对数据的访问,从而使得用户仅能够看到或者修改(某些情况下)他们需要的数据;
  • 从多个表中汇总数据,以产生报表。

创建视图


在 SQL 中,视图是基于 SQL 语句的结果集的可视化表。

数据库视图由 CREATE VIEW 语句创建。视图可以创建自单个表、多个表或者其他视图。

视图中的字段是一个或多个数据库中真实表中的字段。

在使用时视图可以被视为一个“虚拟表”。

要创建视图的话,用户必须有适当的系统权限。具体需要何种权限随数据库系统实现的不同而不同。

CREATE VIEW 语句的基本语法如下所示:

CREATE VIEW view_name AS
SELECT column1, column2.....
FROM table_name
WHERE [condition];

和普通的 SQL SELECT 查询一样,你可以在上面的 SELECT 语句中包含多个数据表。

注释:视图总是显示最新数据!每当用户查询视图时,数据库引擎就使用视图的 SQL 语句重新构建数据。

SQL CREATE VIEW 示例


示例一

考虑 CUSTOMERS 表,表中的记录如下所示:

+----+----------+-----+-----------+----------+
| ID | NAME     | AGE | ADDRESS   | SALARY   |
+----+----------+-----+-----------+----------+
|  1 | Ramesh   |  32 | Ahmedabad |  2000.00 |
|  2 | Khilan   |  25 | Delhi     |  1500.00 |
|  3 | kaushik  |  23 | Kota      |  2000.00 |
|  4 | Chaitali |  25 | Mumbai    |  6500.00 |
|  5 | Hardik   |  27 | Bhopal    |  8500.00 |
|  6 | Komal    |  22 | MP        |  4500.00 |
|  7 | Muffy    |  24 | Indore    | 10000.00 |
+----+----------+-----+-----------+----------+

下面是由 CUSTOMERS 表创建视图的例子。该视图包含来自 CUSTOMERS 表的顾客的名字(name)和年龄(age):

SQL > CREATE VIEW CUSTOMERS_VIEW AS
SELECT name, age
FROM  CUSTOMERS;

现在,你就可以像查询普通的数据表一样查询 CUSTOMERS_VIEW 了:

SQL > SELECT * FROM CUSTOMERS_VIEW;

上述语句将会产生如下运行结果:

+----------+-----+
| name     | age |
+----------+-----+
| Ramesh   |  32 |
| Khilan   |  25 |
| kaushik  |  23 |
| Chaitali |  25 |
| Hardik   |  27 |
| Komal    |  22 |
| Muffy    |  24 |
+----------+-----+
示例2

下面是由 CUSTOMERS 表创建视图的例子。该视图包含来自 CUSTOMERS 表中年龄(age)为25的顾客的ADDRESS信息:

SQL > CREATE VIEW CUSTOMERS_ADDRESS AS
SELECT ADDRESS
FROM  CUSTOMERS;
WHERE AGE=25;

我们可以像这样查询上面这个 CUSTOMERS_ADDRESS 视图:

SQL > SELECT * FROM CUSTOMERS_ADDRESS;

我们也可以向查询添加条件。现在,我们仅仅需要查看 "Delhi" 的数据:

SELECT * FROM CUSTOMERS_ADDRESS
WHERE ADDRESS='Delhi';

WITH CHECK OPTION


WITH CHECK OPTION 是 CREATE VIEW 语句的一个可选项。

WITH CHECK OPTION 用于保证所有的 UPDATE 和 INSERT 语句都满足视图定义中的条件。

如果不能满足这些条件,UPDATE 或 INSERT 就会返回错误。

下面的例子创建的也是 CUSTOMERS_VIEW 视图,不过这次 WITH CHECK OPTION 是打开的:

CREATE VIEW CUSTOMERS_VIEW AS
SELECT name, age
FROM  CUSTOMERS
WHERE age IS NOT NULL
WITH CHECK OPTION;

这里 WITH CHECK OPTION 使得视图拒绝任何 AGE 字段为 NULL 的条目,因为视图的定义中,AGE 字段不能为空。

更新视图


在SQL视图上也可以使用修改数据的DML语句,如 INSERT、UPDATE和DELETE。

视图可以在特定的情况下更新:

  • SELECT 子句不能包含 DISTINCT 关键字
  • SELECT 子句不能包含任何汇总函数(summary functions)
  • SELECT 子句不能包含任何集合函数(set functions)
  • SELECT 子句不能包含任何集合运算符(set operators)
  • SELECT 子句不能包含 ORDER BY 子句
  • 视图不能包含连接操作符
  • 视图不能包含伪列或表达式
  • FROM 子句中不能有多个数据表
  • WHERE 子句不能包含子查询(subquery)
  • 查询语句中不能有 GROUP BY 或者 HAVING
  • 计算得出的列不能更新
  • 视图必须包含原始数据表中所有的 NOT NULL 列,从而使 INSERT 查询生效。

如果视图满足以上所有的条件,该视图就可以被更新。下面的例子中,Ramesh 的年龄被更新了:

SQL > UPDATE CUSTOMERS_VIEW
      SET AGE = 35
      WHERE name='Ramesh';

最终更新的还是原始数据表,只是其结果反应在了视图上。现在查询原始数据表,SELECT 语句将会产生以下结果:

+----+----------+-----+-----------+----------+
| ID | NAME     | AGE | ADDRESS   | SALARY   |
+----+----------+-----+-----------+----------+
|  1 | Ramesh   |  35 | Ahmedabad |  2000.00 |
|  2 | Khilan   |  25 | Delhi     |  1500.00 |
|  3 | kaushik  |  23 | Kota      |  2000.00 |
|  4 | Chaitali |  25 | Mumbai    |  6500.00 |
|  5 | Hardik   |  27 | Bhopal    |  8500.00 |
|  6 | Komal    |  22 | MP        |  4500.00 |
|  7 | Muffy    |  24 | Indore    | 10000.00 |
+----+----------+-----+-----------+----------+

向视图中插入新行


可以向视图中插入新行,其规则同(使用 UPDATE 命令)更新视图所遵循的规则相同。

这里我们不能向 CUSTOMERS_VIEW 视图中添加新行,因为该视图没有包含原始数据表中所有 NOT NULL 的列。否则的话,你就可以像在数据表中插入新行一样,向视图中插入新行。

句法:

INSERT INTO view_name
VALUES (value1, value2, value3, ...);

删除视图中的行


视图中的数据行可以被删除。删除数据行与更新视图和向视图中插入新行遵循相同的规则。

下面的例子将删除 CUSTOMERS_VIEW 视图中 AGE=22 的数据行:

SQL > DELETE FROM CUSTOMERS_VIEW
      WHERE age = 22;

该语句最终会将原始数据表中对应的数据行删除,只不过其结果反应在了视图上。现在查询原始数据表,SELECT 语句将会产生以下结果:

+----+----------+-----+-----------+----------+
| ID | NAME     | AGE | ADDRESS   | SALARY   |
+----+----------+-----+-----------+----------+
|  1 | Ramesh   |  35 | Ahmedabad |  2000.00 |
|  2 | Khilan   |  25 | Delhi     |  1500.00 |
|  3 | kaushik  |  23 | Kota      |  2000.00 |
|  4 | Chaitali |  25 | Mumbai    |  6500.00 |
|  5 | Hardik   |  27 | Bhopal    |  8500.00 |
|  7 | Muffy    |  24 | Indore    | 10000.00 |
+----+----------+-----+-----------+----------+

删除视图


很明显,当我们不再需要某个视图的时候,需要有一种方式可以让我们将其删除。删除视图的语法非常简单,如下所示:

DROP VIEW view_name;

下面的例子展示了如何从 CUSTOMERS 表中删除 CUSTOMERS_VIEW 视图:

DROP VIEW CUSTOMERS_VIEW;

SQL 注入


如果你从网页中获取用户输入,并将其插入到 SQL 数据库中的话,那么你很可能已经暴露于一种被称作 SQL 注入的安全风险之下了。

本节将会教你如何防止 SQL 注入,以及如何保护 Perl 这样的服务器端脚本中的程序和 SQL 语句。

注入通常发生在获取用户输入的时候,例如预期得到用户的名字,但是得到的却是一段很可能会在你不知情的情况下运行的 SQL 语句。

绝对不要相信用户提供的数据,处理这些数据之前必须进行验证;通常,验证工作由模式匹配来完成。

下面的例子中,name 仅限由字母、数字和下划线组成,并且长度在 8 到 20 之间(你可以根据需要修改这些规则)。

if (preg_match("/^\w{8,20}$/", $_GET['username'], $matches))
{
   $result = mysql_query("SELECT * FROM CUSTOMERS 
                          WHERE name=$matches[0]");
}
else 
{
   echo "user name not accepted";
}

为了展示问题所在,请考虑下面这段代码:

// supposed input
$name = "Qadir'; DELETE FROM CUSTOMERS;";
mysql_query("SELECT * FROM CUSTOMSRS WHERE name='{$name}'");

下面的函数调用本来是要从 CUSTOMERS 表中取得 name 字段与用户给定的输入相匹配的记录。通常情况下,$name 只包含字母和数字,或许还有空格,例如字符串 ilia。但是,这里通过在 $name 上附加一段全新的查询语句,将原有的函数调用变为了数据库的灾难:注入的 DELETE 语句将会删除表中所有的记录。

幸运的是,如果你在使用 MySQL 的话,mysql_query() 函数不允许查询堆积(query stacking),或者说在一次函数调用中执行多次 SQL 查询。如果你试图进行堆积式查询的话,函数调用将会失败。

然而,其他的 PHP 数据库扩展,例如 SQLite 和 PostgreSQL 会愉快地接受堆积式查询,执行字符串中所有的查询,并由此产生严重的安全问题。

阻止 SQL 注入


你可以在 Perl 或者 PHP 等脚本语言中巧妙地处理所有的转义字符。PHP 的 MySQL 扩展提供了一个 mysql_real_escape_string() 函数,来转义那些对 MySQL 有特殊意义的字符。

if (get_magic_quotes_gpc()) 
{
  $name = stripslashes($name);
}
$name = mysql_real_escape_string($name);
mysql_query("SELECT * FROM CUSTOMERS WHERE name='{$name}'");

LIKE 困境


要破解 LIKE 困境,必须有一种专门的转义机制,将用户提供的 '%' 和 '_' 转换为字面值。为此你可以使用 addcslashes() 函数,该函数允许指定要进行转义的字符的范围。

$sub = addcslashes(mysql_real_escape_string("%str"), "%_");
// $sub == \%str\_
mysql_query("SELECT * FROM messages 
             WHERE subject LIKE '{$sub}%'");

SQL HAVING 子句


HAVING 子句使你能够指定过滤条件,从而控制查询结果中哪些组可以出现在最终结果里面。

WHERE 子句对被选择的列施加条件,而 HAVING 子句则对 GROUP BY 子句所产生的组施加条件。

语法

下面可以看到 HAVING 子句在 SEL ECT 查询中的位置:

SELECT
FROM
WHERE
GROUP BY
HAVING
ORDER BY

在 SELECT 查询中,HAVING 子句必须紧随 GROUP BY 子句,并出现在 ORDER BY 子句(如果有的话)之前。带有 HAVING 子句的 SELECT 语句的语法如下所示:

SELECT column1, column2   
FROM table1, table2
WHERE [ conditions ]
GROUP BY column1, column2
HAVING [ conditions ]
ORDER BY column1, column2

SQL 事务


事务是在数据库上按照一定的逻辑顺序执行的任务序列,既可以由用户手动执行,也可以由某种数据库程序自动执行。

事务实际上就是对数据库的一个或者多个更改。当你在某张表上创建更新或者删除记录的时,你就已经在使用事务了。控制事务以保证数据完整性,并对数据库错误做出处理,对数据库来说非常重要。

实践中,通常会将很多 SQL 查询组合在一起,并将其作为某个事务一部分来执行。

事务的属性


事务具有以下四个标准属性,通常用缩略词 ACID 来表示:

  • **原子性:**保证任务中的所有操作都执行完毕;否则,事务会在出现错误时终止,并回滚之前所有操作到原始状态。
  • **一致性:**如果事务成功执行,则数据库的状态得到了进行了正确的转变。
  • **隔离性:**保证不同的事务相互独立、透明地执行。
  • **持久性:**即使出现系统故障,之前成功执行的事务的结果也会持久存在。

事务控制


有四个命令用于控制事务:

  • **COMMIT:**提交更改;
  • **ROLLBACK:**回滚更改;
  • **SAVEPOINT:**在事务内部创建一系列可以 ROLLBACK 的还原点;
  • **SET TRANSACTION:**命名事务;

COMMIT 命令


COMMIT 命令用于保存事务对数据库所做的更改。

COMMIT 命令会将自上次 COMMIT 命令或者 ROLLBACK 命令执行以来所有的事务都保存到数据库中。

COMMIT 命令的语法如下所示:

COMMIT;

示例


考虑 CUSTOMERS 表,表中的记录如下所示:

+----+----------+-----+-----------+----------+
| ID | NAME     | AGE | ADDRESS   | SALARY   |
+----+----------+-----+-----------+----------+
|  1 | Ramesh   |  32 | Ahmedabad |  2000.00 |
|  2 | Khilan   |  25 | Delhi     |  1500.00 |
|  3 | kaushik  |  23 | Kota      |  2000.00 |
|  4 | Chaitali |  25 | Mumbai    |  6500.00 |
|  5 | Hardik   |  27 | Bhopal    |  8500.00 |
|  6 | Komal    |  22 | MP        |  4500.00 |
|  7 | Muffy    |  24 | Indore    | 10000.00 |
+----+----------+-----+-----------+----------+

下面的示例将会删除表中 age=25 的记录,然后将更改提交(COMMIT)到数据库中。

SQL> DELETE FROM CUSTOMERS
     WHERE AGE = 25;
SQL> COMMIT;

上述语句将会从表中删除两行记录,再执行 SELECT 语句将会得到如下结果:

+----+----------+-----+-----------+----------+
| ID | NAME     | AGE | ADDRESS   | SALARY   |
+----+----------+-----+-----------+----------+
|  1 | Ramesh   |  32 | Ahmedabad |  2000.00 |
|  3 | kaushik  |  23 | Kota      |  2000.00 |
|  5 | Hardik   |  27 | Bhopal    |  8500.00 |
|  6 | Komal    |  22 | MP        |  4500.00 |
|  7 | Muffy    |  24 | Indore    | 10000.00 |
+----+----------+-----+-----------+----------+

ROLLBACK 命令


ROLLBACK 命令用于撤销尚未保存到数据库中的事务。

ROLLBACK 命令只能撤销自上次 COMMIT 命令或者 ROLLBACK 命令执行以来的事务。

ROLLBACK 命令的语法如下所示:

ROLLBACK;

示例:


考虑 CUSTOMERS 表,表中的记录如下所示:

+----+----------+-----+-----------+----------+
| ID | NAME     | AGE | ADDRESS   | SALARY   |
+----+----------+-----+-----------+----------+
|  1 | Ramesh   |  32 | Ahmedabad |  2000.00 |
|  2 | Khilan   |  25 | Delhi     |  1500.00 |
|  3 | kaushik  |  23 | Kota      |  2000.00 |
|  4 | Chaitali |  25 | Mumbai    |  6500.00 |
|  5 | Hardik   |  27 | Bhopal    |  8500.00 |
|  6 | Komal    |  22 | MP        |  4500.00 |
|  7 | Muffy    |  24 | Indore    | 10000.00 |
+----+----------+-----+-----------+----------+

下面的示例将会从表中删除所有 age=25 的记录,然后回滚(ROLLBACK)对数据库所做的更改。

SQL> DELETE FROM CUSTOMERS
     WHERE AGE = 25;
SQL> ROLLBACK;

结果是删除操作并不会对数据库产生影响。现在,执行 SELECT 语句将会得到如下结果:

+----+----------+-----+-----------+----------+
| ID | NAME     | AGE | ADDRESS   | SALARY   |
+----+----------+-----+-----------+----------+
|  1 | Ramesh   |  32 | Ahmedabad |  2000.00 |
|  2 | Khilan   |  25 | Delhi     |  1500.00 |
|  3 | kaushik  |  23 | Kota      |  2000.00 |
|  4 | Chaitali |  25 | Mumbai    |  6500.00 |
|  5 | Hardik   |  27 | Bhopal    |  8500.00 |
|  6 | Komal    |  22 | MP        |  4500.00 |
|  7 | Muffy    |  24 | Indore    | 10000.00 |
+----+----------+-----+-----------+----------+

SAVEPOINT 命令


SAVEPOINT 是事务中的一个状态点,使得我们可以将事务回滚至特定的点,而不是将整个事务都撤销。

SAVEPOINT 命令的记录如下所示:

SAVEPOINT SAVEPOINT_NAME;

该命令只能在事务语句之间创建保存点(SAVEPOINT)。ROLLBACK 命令可以用于撤销一系列的事务。

回滚至某一保存点的语法如下所示:

ROLLBACK TO SAVEPOINT_NAME;

下面的示例中,你计划从 CUSTOMERS 表中删除三条不同的记录,并在每次删除之前创建一个保存点(SAVEPOINT),从而使得你可以在任何任何时候回滚到任意的保存点,以恢复数据至其原始状态。

示例


考虑 CUSTOMERS 表,表中的记录如下所示:

+----+----------+-----+-----------+----------+
| ID | NAME     | AGE | ADDRESS   | SALARY   |
+----+----------+-----+-----------+----------+
|  1 | Ramesh   |  32 | Ahmedabad |  2000.00 |
|  2 | Khilan   |  25 | Delhi     |  1500.00 |
|  3 | kaushik  |  23 | Kota      |  2000.00 |
|  4 | Chaitali |  25 | Mumbai    |  6500.00 |
|  5 | Hardik   |  27 | Bhopal    |  8500.00 |
|  6 | Komal    |  22 | MP        |  4500.00 |
|  7 | Muffy    |  24 | Indore    | 10000.00 |
+----+----------+-----+-----------+----------+

操作序列如下所示:

SQL> SAVEPOINT SP1;
Savepoint created.
SQL> DELETE FROM CUSTOMERS WHERE ID=1;
1 row deleted.
SQL> SAVEPOINT SP2;
Savepoint created.
SQL> DELETE FROM CUSTOMERS WHERE ID=2;
1 row deleted.
SQL> SAVEPOINT SP3;
Savepoint created.
SQL> DELETE FROM CUSTOMERS WHERE ID=3;
1 row deleted.

现在,三次删除操作已经生效了,如果此时你改变主意决定回滚至名字为 SP2 的保存点,由于 SP2 于第一次删除操作之后创建,所以后两次删除操作将会被撤销。

SQL> ROLLBACK TO SP2;
Rollback complete.

注意,由于你将数据库回滚至 SP2,所以只有第一次删除真正起效了:

SQL> SELECT * FROM CUSTOMERS;
+----+----------+-----+-----------+----------+
| ID | NAME     | AGE | ADDRESS   | SALARY   |
+----+----------+-----+-----------+----------+
|  2 | Khilan   |  25 | Delhi     |  1500.00 |
|  3 | kaushik  |  23 | Kota      |  2000.00 |
|  4 | Chaitali |  25 | Mumbai    |  6500.00 |
|  5 | Hardik   |  27 | Bhopal    |  8500.00 |
|  6 | Komal    |  22 | MP        |  4500.00 |
|  7 | Muffy    |  24 | Indore    | 10000.00 |
+----+----------+-----+-----------+----------+
6 rows selected.

RELEASE SAVEPOINT 命令


RELEASE SAVEPOINT 命令用于删除先前创建的保存点。

RELEASE SAVEPOINT 的语法如下所示:

RELEASE SAVEPOINT SAVEPOINT_NAME;

保存点一旦被释放,你就不能够再用 ROLLBACK 命令来撤销该保存点之后的事务了。

SET TRANSACTION 命令


SET TRANSACTION 命令可以用来初始化数据库事务,指定随后的事务的各种特征。

例如,你可以将某个事务指定为只读或者读写。

SET TRANSACTION 命令的语法如下所示:

SET TRANSACTION [ READ WRITE | READ ONLY ];

SQL 临时表


某些关系型数据库管理系统支持临时表。临时表是一项很棒的特性,能够让你像操作普通的 SQL 数据表一样,使用 SELECT、UPDATE 和 JOIN 等功能来存储或者操作中间结果。

临时表有时候对于保存临时数据非常有用。有关临时表你需要知道的最重要的一点是,它们会在当前的终端会话结束后被删除。

临时表自 MySQL 3.23 起受到支持。如果你的 MySQL 版本比 3.23 还老,那么你就不能使用临时表了,不过你可以使用堆表(heap table)。

如先前所言,临时表只在会话期间存在。如果你在 PHP 脚本中操作数据库,那么临时表将在脚本执行完毕时被自动销毁。如果你是通过 MySQL 的客户端程序连接到 MySQL 数据库服务器的,那么临时表将会存在到你关闭客户端或者手动将其删除。

示例


下面的示例向你展示了如何使用临时表:

mysql> CREATE TEMPORARY TABLE SALESSUMMARY (
    -> product_name VARCHAR(50) NOT NULL
    -> , total_sales DECIMAL(12,2) NOT NULL DEFAULT 0.00
    -> , avg_unit_price DECIMAL(7,2) NOT NULL DEFAULT 0.00
    -> , total_units_sold INT UNSIGNED NOT NULL DEFAULT 0
);
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO SALESSUMMARY
    -> (product_name, total_sales, avg_unit_price, total_units_sold)
    -> VALUES
    -> ('cucumber', 100.25, 90, 2);

mysql> SELECT * FROM SALESSUMMARY;
+--------------+-------------+----------------+------------------+
| product_name | total_sales | avg_unit_price | total_units_sold |
+--------------+-------------+----------------+------------------+
| cucumber     |      100.25 |          90.00 |                2 |
+--------------+-------------+----------------+------------------+
1 row in set (0.00 sec)

当你下达 SHOW TABLES 命令的时候,临时表是不会出现在结果列表当中的。现在,如果你退出 MySQL 会话,然后再执行 SELECT 命令的话,你将不能从数据库中取回任何数据,你的临时表也已经不复存在了。

删除临时表


默认情况下,所有的临时表都由 MySQL 在数据库连接关闭时删除。不过,有时候你还是会想要在会话期间将其删除,此时你需要使用 DROP TABLE 命令来达到目的。

下面是删除临时表的示例:

mysql> CREATE TEMPORARY TABLE SALESSUMMARY (
    -> product_name VARCHAR(50) NOT NULL
    -> , total_sales DECIMAL(12,2) NOT NULL DEFAULT 0.00
    -> , avg_unit_price DECIMAL(7,2) NOT NULL DEFAULT 0.00
    -> , total_units_sold INT UNSIGNED NOT NULL DEFAULT 0
);
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO SALESSUMMARY
    -> (product_name, total_sales, avg_unit_price, total_units_sold)
    -> VALUES
    -> ('cucumber', 100.25, 90, 2);

mysql> SELECT * FROM SALESSUMMARY;
+--------------+-------------+----------------+------------------+
| product_name | total_sales | avg_unit_price | total_units_sold |
+--------------+-------------+----------------+------------------+
| cucumber     |      100.25 |          90.00 |                2 |
+--------------+-------------+----------------+------------------+
1 row in set (0.00 sec)
mysql> DROP TABLE SALESSUMMARY;
mysql>  SELECT * FROM SALESSUMMARY;
ERROR 1146: Table 'TUTORIALS.SALESSUMMARY' doesn't exist

SQL 函数

SQL 函数


SQL 拥有很多可用于计数和计算的内建函数。

SQL Aggregate 函数


SQL Aggregate 函数计算从列中取得的值,返回一个单一的值。

有用的 Aggregate 函数:

  • AVG() - 返回平均值
  • COUNT() - 返回行数
  • FIRST() - 返回第一个记录的值
  • LAST() - 返回最后一个记录的值
  • MAX() - 返回最大值
  • MIN() - 返回最小值
  • SUM() - 返回总和

SQL Scalar 函数


SQL Scalar 函数基于输入值,返回一个单一的值。

有用的 Scalar 函数:

  • UCASE() - 将某个字段转换为大写
  • LCASE() - 将某个字段转换为小写
  • MID() - 从某个文本字段提取字符
  • LEN() - 返回某个文本字段的长度
  • ROUND() - 对某个数值字段进行指定小数位数的四舍五入
  • NOW() - 返回当前的系统日���和时间
  • FORMAT() - 格式化某个字段的显示方式

**提示:**在下面的章节,我们会详细讲解 Aggregate 函数和 Scalar 函数。

SQL MAX() 函数


MAX() 函数

MAX() 函数返回所选列的最大值。

SQL MAX() 语法
SELECT MAX(column_name)
FROM table_name
WHERE condition;

SQL MIN() Function


MIN() 函数

MIN() 函数返回所选列的最小值。

SQL MIN() 语法
SELECT MIN(column_name)
FROM table_name
WHERE condition;

SQL COUNT() 函数


COUNT() 函数返回符合指定条件的行数。

SQL COUNT(column_name) 语法

COUNT(column_name) 函数返回指定列的值的数目(NULL 不计入):

SELECT COUNT(column_name)
FROM table_name
WHERE condition;
SQL COUNT(*) 语法

COUNT(*) 函数返回表中的记录数:

SELECT COUNT(*) FROM table_name;
SQL COUNT(DISTINCT column_name) 语法

COUNT(DISTINCT column_name) 函数返回指定列的不同值的数目:

SELECT COUNT(DISTINCT column_name) FROM table_name;

**注释:**COUNT(DISTINCT) 适用于 ORACLE 和 Microsoft SQL Server,但是无法用于 Microsoft Access。

SQL AVG() 函数


AVG() 函数

AVG() 函数返回数字列的平均值。

SQL AVG() 语法
SELECT AVG(column_name)
FROM table_name
WHERE condition;

SQL SUM() 函数


SUM() 函数返回数字列的总和。

SQL SUM() 语法
SELECT SUM(column_name)
FROM table_name
WHERE condition;

SQL 日期函数


下面的列表中是 SQL 中所有与日期和时间相关的重要函数。你所用的 RDBMS 可能会支持更多其他的函数。下面的列表基于 MySQL 关系型数据库管理系统。

名称 描述
ADDDATE() 增加日期
ADDTIME() 增加时间
CONVERT_TZ() 将当前时区更改为另一时区
CURDATE() 返回当前日期
CURRENT_DATE(), CURRENT_DATE CURDATE() 的别名
CURRENT_TIME(), CURRENT_TIME CURTIME() 的别名
CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP NOW() 的别名
CURTIME() 返回当前时间
DATE_ADD() 将两个日期相加
DATE_FORMAT() 按照指定格式格式化日期
DATE_SUB() 将两个日期相减
DATE() 从 date 或者 datetime 表达式中提取出日期部分
DATEDIFF() 将两个日期相减
DAY() DAYOFMONTH() 的别名
DAYNAME() 返回某天在用星期中的名称
DAYOFMONTH() 返回某天是当月的第几天 (1-31)
DAYOFWEEK() 返回某天是该星期的第几天
DAYOFYEAR() 返回某天是一年中的第几天(1-366)
EXTRACT 提取日期中的某一部分
FROM_DAYS() 将天数转换为日期
FROM_UNIXTIME() 将某个日期格式化为 UNIX 时间戳
HOUR() 提取小时
LAST_DAY 返回参数日期所在月份的最后一天
LOCALTIME(), LOCALTIME NOW() 的别名
LOCALTIMESTAMP, LOCALTIMESTAMP() NOW() 的别名
MAKEDATE() 利用年份和某天在该年所处的天数来创建日期
MAKETIME MAKETIME()
MICROSECOND() 由参数返回微秒
MINUTE() 由参数返回分钟
MONTH() 返回日期参数的月份
MONTHNAME() 返回月份的名字
NOW() 返回当前日期和时间
PERIOD_ADD() 向年月格式的日期数据之间添加一段时间
PERIOD_DIFF() 返回两个年月格式的日期数据之间的月份数
QUARTER() 返回日期参数所在的季度
SEC_TO_TIME() 将秒数转换为 'HH:MM:SS' 格式
SECOND() 返回参数中的秒数 (0-59)
STR_TO_DATE() 将字符串转换为日期数据
SUBDATE() 以三个参数调用的时候是 DATE_SUB() 的同义词
SUBTIME() 减去时间
SYSDATE() 返回函数执行的时的时刻
TIME_FORMAT() 格式化时间
TIME_TO_SEC() 将时间参数转换为秒数
TIME() 返回参数表达式中的时间部分
TIMEDIFF() 将两个时间相减
TIMESTAMP() 只有一个参数时,该函数返回 date 或者 datetime 表达式。当有两个参数时,将两个参数相加。
TIMESTAMPADD() 在 datetime 表达式上加上一段时间
TIMESTAMPDIFF() 在 datetime 表达式上减去一段时间
TO_DAYS() 将日期参数转换为天数
UNIX_TIMESTAMP() 返回 UNIX 时间戳
UTC_DATE() 返回当前 UTC 日期
UTC_TIME() 返回当前 UTC 时间
UTC_TIMESTAMP() 返回当前 UTC 日期和时间
WEEK() 返回参数的星期数
WEEKDAY() 返回日期参数时一个星期中的第几天
WEEKOFYEAR() 返回日期参数是日历上的第几周 (1-53)
YEAR() 返回日期参数中的年份
YEARWEEK() 返回年份和星期

ADDDATE(date, INTERVAL expr unit), ADDDATE(expr, days)


如果调用时第二个参数为 INTERVAL 形式的话,ADDDATE() 就是 DATE_ADD() 的同义词。同样的情况下,SUBDATE() 是 DATE_SUB() 的同义词。有关 INTERVAL 单位参数的信息,见有关 DATE_ADD() 的讨论。

mysql> SELECT DATE_ADD('1998-01-02', INTERVAL 31 DAY);
+---------------------------------------------------------+
| DATE_ADD('1998-01-02', INTERVAL 31 DAY)                 |
+---------------------------------------------------------+
| 1998-02-02                                              |
+---------------------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT ADDDATE('1998-01-02', INTERVAL 31 DAY);
+---------------------------------------------------------+
| ADDDATE('1998-01-02', INTERVAL 31 DAY)                  |
+---------------------------------------------------------+
| 1998-02-02                                              |
+---------------------------------------------------------+
1 row in set (0.00 sec)

如果调用时第二个参数为天数形式的话,则 MySQL 会将其作为整数加到 expr 上。

mysql> SELECT ADDDATE('1998-01-02', 31);
+---------------------------------------------------------+
| DATE_ADD('1998-01-02', INTERVAL 31 DAY)                 |
+---------------------------------------------------------+
| 1998-02-02                                              |
+---------------------------------------------------------+
1 row in set (0.00 sec)

ADDTIME(expr1,expr2)


ADDTIME() 将 expr2 加到 expr1 上,并返回结果。expr1 为 time 或者 datetime 表达式,expr2 为 time 表达式。

mysql> SELECT ADDTIME('1997-12-31 23:59:59.999999','1 1:1:1.000002');
+---------------------------------------------------------+
| DATE_ADD('1997-12-31 23:59:59.999999','1 1:1:1.000002') |
+---------------------------------------------------------+
| 1998-01-02 01:01:01.000001                              |
+---------------------------------------------------------+
1 row in set (0.00 sec)

CONVERT_TZ(dt,from_tz,to_tz)


该函数将 datetime 类型的值 dt 的时区从 from_dt 转换为 to_dt,并返回结果。如果参数无效,则函数返回 NULL。

mysql> SELECT CONVERT_TZ('2004-01-01 12:00:00','GMT','MET');
+---------------------------------------------------------+
| CONVERT_TZ('2004-01-01 12:00:00','GMT','MET')           |
+---------------------------------------------------------+
| 2004-01-01 13:00:00                                     |
+---------------------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT CONVERT_TZ('2004-01-01 12:00:00','+00:00','+10:00');
+---------------------------------------------------------+
| CONVERT_TZ('2004-01-01 12:00:00','+00:00','+10:00')     |
+---------------------------------------------------------+
| 2004-01-01 22:00:00                                     |
+---------------------------------------------------------+
1 row in set (0.00 sec)

CURDATE()


以 'YYYY-MM-DD'(字符串) 或者 YYYYMMDD(数值) 的形式返回当前日期, 具体形式取决于函数处于字符串还是数值型的上下文环境中。

mysql> SELECT CURDATE();
+---------------------------------------------------------+
| CURDATE()                                               |
+---------------------------------------------------------+
| 1997-12-15                                              |
+---------------------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT CURDATE() + 0;
+---------------------------------------------------------+
| CURDATE() + 0                                           |
+---------------------------------------------------------+
| 19971215                                                |
+---------------------------------------------------------+
1 row in set (0.00 sec)

CURRENT_DATE and CURRENT_DATE()


CURRENT_DATE 和 CURRENT_DATE() 是 CURDATE() 的别名。

CURTIME()


以 'HH:MM:SS'(字符串) 或者 HHMMSS(数值) 的形式返回当前时间, 具体形式取决于函数处于字符串还是数值型的上下文环境中。该函数按照当前时区来表示返回值。

mysql> SELECT CURTIME();
+---------------------------------------------------------+
| CURTIME()                                               |
+---------------------------------------------------------+
| 23:50:26                                                |
+---------------------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT CURTIME() + 0;
+---------------------------------------------------------+
| CURTIME() + 0                                           |
+---------------------------------------------------------+
| 235026                                                  |
+---------------------------------------------------------+
1 row in set (0.00 sec)

CURRENT_TIME and CURRENT_TIME()


CURRENT_TIME 和 CURRENT_TIME() 是 CURTIME() 的别名。

CURRENT_TIMESTAMP and CURRENT_TIMESTAMP()


CURRENT_TIMESTAMP 和 CURRENT_TIMESTAMP() 是 NOW() 的别名。

DATE(expr)


提取 date 表达式或者 datetime 表达式中的日期部分。

mysql> SELECT DATE('2003-12-31 01:02:03');
+---------------------------------------------------------+
| DATE('2003-12-31 01:02:03')                             |
+---------------------------------------------------------+
|  2003-12-31                                             |
+---------------------------------------------------------+
1 row in set (0.00 sec)

DATEDIFF(expr1,expr2)


DATEDIFF() 返回 expr1 和 expr2 的差,以天数的形式表示。expr1 和 expr2 应为 date 或者 datetime 表达式。只有参数的日期部分参与了计算。

mysql> SELECT DATEDIFF('1997-12-31 23:59:59','1997-12-30');
+---------------------------------------------------------+
| DATEDIFF('1997-12-31 23:59:59','1997-12-30')            |
+---------------------------------------------------------+
| 1                                                       |
+---------------------------------------------------------+
1 row in set (0.00 sec)

DATE_ADD(date,INTERVAL expr unit), DATE_SUB(date,INTERVAL expr unit)


这些函数进行有关日期的算术运算。date 是一个 DATETIME 或者 DATE 类型的值,指明了起始时间。expr 表达式则是 date 要增加或者减去的时间间隔。expr 是一个字符串,可以以 '-' 开始来表示负时间区间。 unit 是一个关键词,指明了expr 的单位。

INTERVAL 关键字和 unit(单位)指示符不区分大小写。

下表列出了对于每种单位,expr 应有的形式。

unit 值 expr 应有的格式
MICROSECOND 微秒
SECOND
MINUTE 分钟
HOUR 小时
DAY
WEEK 星期
MONTH
QUARTER 季度
YEAR
SECOND_MICROSECOND '秒.微秒'
MINUTE_MICROSECOND '分.微秒'
MINUTE_SECOND '分:秒'
HOUR_MICROSECOND '小时.微秒'
HOUR_SECOND '时:分:秒'
HOUR_MINUTE '时:分'
DAY_MICROSECOND '天.微秒'
DAY_SECOND '天 时:分:秒'
DAY_MINUTE '天 时:分'
DAY_HOUR '天 时'
YEAR_MONTH '年-月'

QUARTER 和 WEEK 自 MySQL 5.0.0 起受到支持。

mysql> SELECT DATE_ADD('1997-12-31 23:59:59', 
    -> INTERVAL '1:1' MINUTE_SECOND);
+---------------------------------------------------------+
| DATE_ADD('1997-12-31 23:59:59', INTERVAL...             |
+---------------------------------------------------------+
| 1998-01-01 00:01:00                                     |
+---------------------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT DATE_ADD('1999-01-01', INTERVAL 1 HOUR);
+---------------------------------------------------------+
| DATE_ADD('1999-01-01', INTERVAL 1 HOUR)                 |
+---------------------------------------------------------+
| 1999-01-01 01:00:00                                     |
+---------------------------------------------------------+
1 row in set (0.00 sec)

DATE_FORMAT(date,format)


根据格式字符串对日期值进行格式化。

下面这些占位符可以用在格式字符串中,'%' 必须出现在特定的格式字符之前。

占位符 描述
%a 简写的星期名称(Sun..Sat)
%b 简写的月份名称 (Jan..Dec)
%c 月份,以数值形式表示(0..12)
%D 月份中的日期,带有英文后缀(0th,1st,2nd,3rd 等等)
%d 月份中的日期,以数值表示 (00..31)
%e 月份中的日期,以数值表示 (0..31)
%f 微秒(000000..999999)
%H 小时(00..23)
%h 小时(01..12)
%I 小时(01..12)
%i 分钟,以数值表示(00..59)
%j 一年中的第几天(001..366)
%k 小时(0..23)
%l 小时(1..12)
%M 月份的名称(January..December)
%m 月份,以数值形式表示(00..12)
%p AM 或者 PM
%r 时间,12 小时制(hh:mm:ss followed by AM or PM)
%S 秒(00..59)
%s 秒(00..59)
%T 时间,24小时制(hh:mm:ss)
%U 星期(00..53),此处星期日为一周的第一天
%u 星期(00..53),此处星期一为一周的第一天
%V 星期(01..53),此处星期日为一周的第一天;与 %X 一起使用。
%v 星期(01..53),此处星期一为一周的第一天;与 %x 一起使用。
%W 一周中日期的名称(Sunday..Saturday)
%w 一周中的第几天(0=Sunday..6=Saturday)
%X 以星期日为第一天的周所处的年份,四位数字表示;同 %V 一起使用。
%x 以星期一为第一天的周所处的年份,四位数字表示;同 %v 一起使用。
%Y 年份,四位数字表示。
%y 年份,两位数字表示。
%% % 字面值
%x x,针对任何以上没有列出的情况。
mysql> SELECT DATE_FORMAT('1997-10-04 22:23:00', '%W %M %Y');
+---------------------------------------------------------+
| DATE_FORMAT('1997-10-04 22:23:00', '%W %M %Y')          |
+---------------------------------------------------------+
| Saturday October 1997                                   |
+---------------------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT DATE_FORMAT('1997-10-04 22:23:00'
    -> '%H %k %I %r %T %S %w');
+---------------------------------------------------------+
| DATE_FORMAT('1997-10-04 22:23:00.......                 |
+---------------------------------------------------------+
|  22 22 10 10:23:00 PM 22:23:00 00 6                     |
+---------------------------------------------------------+
1 row in set (0.00 sec)

DATE_SUB(date,INTERVAL expr unit)


同 DATE_ADD() 函数相似。

DAY(date)


DAY() 是 DAYOFMONTH() 的别名。

DAYNAME(date)


返回 date 在星期中的名称。

mysql> SELECT DAYNAME('1998-02-05');
+---------------------------------------------------------+
| DAYNAME('1998-02-05')                                   |
+---------------------------------------------------------+
| Thursday                                                |
+---------------------------------------------------------+
1 row in set (0.00 sec)

DAYOFMONTH(date)


返回 date 是当月的第几天,范围为 0 到 31。

mysql> SELECT DAYOFMONTH('1998-02-03');
+---------------------------------------------------------+
| DAYOFMONTH('1998-02-03')                                |
+---------------------------------------------------------+
| 3                                                       |
+---------------------------------------------------------+
1 row in set (0.00 sec)

DAYOFWEEK(date)


返回 date 是其所在星期的第几天(1 = Sunday, 2 = Monday,.., 7 = Saturday),这里一星期中日期的名称与数字的对应关系符合 ODBC 标准。

mysql> SELECT DAYOFWEEK('1998-02-03');
+---------------------------------------------------------+
|DAYOFWEEK('1998-02-03')                                  |
+---------------------------------------------------------+
| 3                                                       |
+---------------------------------------------------------+
1 row in set (0.00 sec)

DAYOFYEAR(date)


返回 date 是当年的第几天,范围为 1 到 366。

mysql> SELECT DAYOFYEAR('1998-02-03');
+---------------------------------------------------------+
| DAYOFYEAR('1998-02-03')                                 |
+---------------------------------------------------------+
| 34                                                      |
+---------------------------------------------------------+
1 row in set (0.00 sec)

EXTRACT(unit FROM date)


EXTRACT() 与 DATE_ADD() 和 DATE_SUB() 使用相同的表示单位的占位符,其作用是提取日期值中相应的组成部分,而不是进行日期运算。

mysql> SELECT EXTRACT(YEAR FROM '1999-07-02');
+---------------------------------------------------------+
| EXTRACT(YEAR FROM '1999-07-02')                         |
+---------------------------------------------------------+
| 1999                                                    |
+---------------------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT EXTRACT(YEAR_MONTH FROM '1999-07-02 01:02:03');
+---------------------------------------------------------+
| EXTRACT(YEAR_MONTH FROM '1999-07-02 01:02:03')          |
+---------------------------------------------------------+
| 199907                                                  |
+---------------------------------------------------------+
1 row in set (0.00 sec)

FROM_DAYS(N)


给出天数 N,返回 DATE 值。

mysql> SELECT FROM_DAYS(729669);
+---------------------------------------------------------+
| FROM_DAYS(729669)                                       |
+---------------------------------------------------------+
| 1997-10-07                                              |
+---------------------------------------------------------+
1 row in set (0.00 sec)

在使用 FROM_DAYS() 处理比较老的日期的时候应当特别小心,该函数不适用于格里高利历诞生(1582)之前的日期。

FROM_UNIXTIME(unix_timestamp), FROM_UNIXTIME(unix_timestamp,format)


返回 UNIX 时间戳对应的日期值,根据函数所处的上下文环境不同,返回值得格式也不同,字符串上下文返回格式为 'YYYY-MM-DD HH:MM:SS',数值型上下文返回格式则为 YYYYMMDDHHMMSS。返回值的时区为系统当前时区。UNIX 时间戳是一种系统内部时间表示,例如 UNIX_TIMESTAMP() 的返回值。

如果给定格式的话,返回结果将会根据格式字符串进行格式化,其规则同 DATE_FORMAT() 函数。

mysql> SELECT FROM_UNIXTIME(875996580);
+---------------------------------------------------------+
| FROM_UNIXTIME(875996580)                                |
+---------------------------------------------------------+
| 1997-10-04 22:23:00                                     |
+---------------------------------------------------------+
1 row in set (0.00 sec)

HOUR(time)


返回时间值的小时部分。对于一天中的时间来说,返回值的范围为 0 到 23。不过,TIME 类型的值可以大得多,所以 HOUR 函数可以返回比 23 大的值。

mysql> SELECT HOUR('10:05:03');
+---------------------------------------------------------+
| HOUR('10:05:03')                                        |
+---------------------------------------------------------+
| 10                                                      |
+---------------------------------------------------------+
1 row in set (0.00 sec)

LAST_DAY(date)


返回 date 或者 datetime 值所在月份的最后一天。如果参数无效的话,返回 NULL。

mysql> SELECT LAST_DAY('2003-02-05');
+---------------------------------------------------------+
| LAST_DAY('2003-02-05')                                  |
+---------------------------------------------------------+
| 2003-02-28                                              |
+---------------------------------------------------------+
1 row in set (0.00 sec)

LOCALTIME and LOCALTIME()


LOCALTIME 和 LOCALTIME() 是 NOW() 的别名。

LOCALTIMESTAMP and LOCALTIMESTAMP()


LOCALTIMESTAMP 和 LOCALTIMESTAMP() 是 NOW() 的别名。

MAKEDATE(year,dayofyear)


给定年份和(某天在一年中)的天数,返回对应的日期值。天数必须大于 0,否则返回值为 NULL。

mysql> SELECT MAKEDATE(2001,31), MAKEDATE(2001,32);
+---------------------------------------------------------+
| MAKEDATE(2001,31), MAKEDATE(2001,32)                    |
+---------------------------------------------------------+
| '2001-01-31', '2001-02-01'                              |
+---------------------------------------------------------+
1 row in set (0.00 sec)

MAKETIME(hour,minute,second)


根据参数给出的时、分、秒,返回对应的时间值。

mysql> SELECT MAKETIME(12,15,30);
+---------------------------------------------------------+
| MAKETIME(12,15,30)                                      |
+---------------------------------------------------------+
| '12:15:30'                                              |
+---------------------------------------------------------+
1 row in set (0.00 sec)

MICROSECOND(expr)


根据 time 或者 datetime 表达式 expr,返回微秒数,结果在 0 到 999999 之间。

mysql> SELECT MICROSECOND('12:00:00.123456');
+---------------------------------------------------------+
| MICROSECOND('12:00:00.123456')                          |
+---------------------------------------------------------+
| 123456                                                  |
+---------------------------------------------------------+
1 row in set (0.00 sec)

MINUTE(time)


返回时间型值中的分钟部分,范围为 0 到 59。

mysql> SELECT MINUTE('98-02-03 10:05:03');
+---------------------------------------------------------+
| MINUTE('98-02-03 10:05:03')                             |
+---------------------------------------------------------+
| 5                                                       |
+---------------------------------------------------------+
1 row in set (0.00 sec)

MONTH(date)


返回日期型值中的月份,范围为 0 到 12。

mysql> SELECT MONTH('1998-02-03')
+---------------------------------------------------------+
| MONTH('1998-02-03')                                     |
+---------------------------------------------------------+
| 2                                                       |
+---------------------------------------------------------+
1 row in set (0.00 sec)

MONTHNAME(date)


返回日期型值所处月份的全名。

mysql> SELECT MONTHNAME('1998-02-05');
+---------------------------------------------------------+
| MONTHNAME('1998-02-05')                                 |
+---------------------------------------------------------+
| February                                                |
+---------------------------------------------------------+
1 row in set (0.00 sec)

NOW()


返回当前的日期和时间,结果的格式为 'YYYY-MM-DD HH:MM:SS' 或者 YYYYMMDDHHMMSS,如果函数上下文环境为字符型,则返回前者,否则如果函数处于数值型的上下文环境,则返回后者。返回值的时区为系统当前时区。

mysql> SELECT NOW();
+---------------------------------------------------------+
| NOW()                                                   |
+---------------------------------------------------------+
| 1997-12-15 23:50:26                                     |
+---------------------------------------------------------+
1 row in set (0.00 sec)

PERIOD_ADD(P,N)


在时间 P(格式为 YYMM 或者 YYYYMM)上加上 N 个月,结果格式为 YYYYMM。注意,时间参数 P 并不是日期型值。

mysql> SELECT PERIOD_ADD(9801,2);
+---------------------------------------------------------+
| PERIOD_ADD(9801,2)                                      |
+---------------------------------------------------------+
| 199803                                                  |
+---------------------------------------------------------+
1 row in set (0.00 sec)

PERIOD_DIFF(P1,P2)


返回时间 P1 和 P2 之间相差的月份。 P1 和 P2 的格式应为 YYMM 或者 YYYYMM。注意I,P1 和 P2 不是日期型值。

mysql> SELECT PERIOD_DIFF(9802,199703);
+---------------------------------------------------------+
| PERIOD_DIFF(9802,199703)                                |
+---------------------------------------------------------+
| 11                                                      |
+---------------------------------------------------------+
1 row in set (0.00 sec)

QUARTER(date)


返回日期型值 date 所处的季度值,范围为 1 到 4。

mysql> SELECT QUARTER('98-04-01');
+---------------------------------------------------------+
| QUARTER('98-04-01')                                     |
+---------------------------------------------------------+
| 2                                                       |
+---------------------------------------------------------+
1 row in set (0.00 sec)

SECOND(time)


返回时间型值中秒的部分,范围为 0 到 59。

mysql> SELECT SECOND('10:05:03');
+---------------------------------------------------------+
| SECOND('10:05:03')                                      |
+---------------------------------------------------------+
| 3                                                       |
+---------------------------------------------------------+
1 row in set (0.00 sec)

SEC_TO_TIME(seconds)


将参数中的秒数转换为时分秒的格式 'HH:MM:SS' 或者 HHMMSS,如果函数所处的上下文为字符串型,则返回前者,否则如果上下文环境为数值型,则返回后者。

STR_TO_DATE(str,format)


这是 DATE_FORMATE() 函数的逆函数,其参数为表示时间和日期的字符串 str 和一个格式字符串 format。如果格式字符串中既有日期又有时间,则 STR_TO_DATE() 返回 DATETIME() 型的值,否则返回日期型(DATE)或者时间型(TIME)的值。

mysql> SELECT STR_TO_DATE('04/31/2004', '%m/%d/%Y');
+---------------------------------------------------------+
| STR_TO_DATE('04/31/2004', '%m/%d/%Y')                   |
+---------------------------------------------------------+
| 2004-04-31                                              |
+---------------------------------------------------------+
1 row in set (0.00 sec)

SUBDATE(date,INTERVAL expr unit) and SUBDATE(expr,days)


当第二个参数为 INTERVAL 形式时,SUBDATE() 就是 DATE_SUB() 的别名。INTERVAL 参数中单位的信息,请见有关 DATE_ADD() 的讨论。

mysql> SELECT DATE_SUB('1998-01-02', INTERVAL 31 DAY);
+---------------------------------------------------------+
| DATE_SUB('1998-01-02', INTERVAL 31 DAY)                 |
+---------------------------------------------------------+
| 1997-12-02                                              |
+---------------------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT SUBDATE('1998-01-02', INTERVAL 31 DAY);
+---------------------------------------------------------+
| SUBDATE('1998-01-02', INTERVAL 31 DAY)                  |
+---------------------------------------------------------+
| 1997-12-02                                              |
+---------------------------------------------------------+
1 row in set (0.00 sec)

SUBTIME(expr1,expr2)


SUBTIME() 返回 expr1-expr2,结果的格式与 expr1 相同。expr1 是一个时间型(time)或者 datetime 型的表达式,expr2 是时间型值。

SYSDATE()


返回当前的日期和时间,格式为 'YYYY-MM-DD HH:MM:SS' 或 YYYYMMDDHHMMSS,如果函数所处的上下文环境为字符串,则返回前者,否则如果上下文环境为数值型,则返回后者。

mysql> SELECT SYSDATE();
+---------------------------------------------------------+
| SYSDATE()                                               |
+---------------------------------------------------------+
| 2006-04-12 13:47:44                                     |
+---------------------------------------------------------+
1 row in set (0.00 sec)

TIME(expr)


提取时间型或者 datetime 型表达式 expr 中的时间部分,返回结果为字符串。

mysql> SELECT TIME('2003-12-31 01:02:03');
+---------------------------------------------------------+
| TIME('2003-12-31 01:02:03')                             |
+---------------------------------------------------------+
| 01:02:03                                                |
+---------------------------------------------------------+
1 row in set (0.00 sec)

TIMEDIFF(expr1,expr2)


TIMEDIFF() 返回 expr1-expr2,结果为时间型值。expr1 和 expr2 可以为时间型或者 datetime 型表达式,不过二者必须为相同类型。

mysql> SELECT TIMEDIFF('1997-12-31 23:59:59.000001',
    -> '1997-12-30 01:01:01.000002');
+---------------------------------------------------------+
| TIMEDIFF('1997-12-31 23:59:59.000001'.....              |
+---------------------------------------------------------+
|  46:58:57.999999                                        |
+---------------------------------------------------------+
1 row in set (0.00 sec)

TIMESTAMP(expr), TIMESTAMP(expr1,expr2)


只有一个参数的时候,该函数由日期型或者 datetime 型表达式返回一个 datetime 型值。有两个参数的时候,该函数将 expr2 加到日期型或 datetime 型值 expr1 上,并返回 datetime 型的结果。

mysql> SELECT TIMESTAMP('2003-12-31');
+---------------------------------------------------------+
| TIMESTAMP('2003-12-31')                                 |
+---------------------------------------------------------+
| 2003-12-31 00:00:00                                     |
+---------------------------------------------------------+
1 row in set (0.00 sec)

TIMESTAMPADD(unit,interval,datetime_expr)


将整数型的表达式 interval 加到日期型或者 datetime 型表达式 datetime_expr 上。单位由 unit 参数给出,其取值应为以下几种中的一种:FRAC_SECOND、SECOND、MINUTE、HOUR、DAY、WEEK、MONTH、QUARTER 或者 YEAR。

单位 unit 可以为上述关键字中的一个,也可以添加一个 SQLTSI 前缀,例如 DAY 和 SQL_TSI_DAY 都是合法的。

mysql> SELECT TIMESTAMPADD(MINUTE,1,'2003-01-02');
+---------------------------------------------------------+
| TIMESTAMPADD(MINUTE,1,'2003-01-02')                     |
+---------------------------------------------------------+
| 2003-01-02 00:01:00                                     |
+---------------------------------------------------------+
1 row in set (0.00 sec)

TIMESTAMPDIFF(unit,datetime_expr1,datetime_expr2)


返回日期型或者 datetime 型表达式 datetime_expr1 和 datetime_expr2 的差。结果的单位由 unit 参数给出,unit 的取值规定同 TIMESTAMPADD() 函数。

mysql> SELECT TIMESTAMPDIFF(MONTH,'2003-02-01','2003-05-01');
+---------------------------------------------------------+
| TIMESTAMPDIFF(MONTH,'2003-02-01','2003-05-01')          |
+---------------------------------------------------------+
| 3                                                       |
+---------------------------------------------------------+
1 row in set (0.00 sec)

TIME_FORMAT(time,format)


该函数使用起来类似 DATE_FORMAT() 函数,但是格式字符串 format 中只能有与小时、分钟和秒有关的那些占位符。

如果时间型值的小时部分大于 23,则 %H 和 %k 格式占位符将会产生一个大于通常的 0-23 的值,其他与小时有关的占位符则会返回小时值除以 12 后的余数(modulo 12)。

mysql> SELECT TIME_FORMAT('100:00:00', '%H %k %h %I %l');
+---------------------------------------------------------+
| TIME_FORMAT('100:00:00', '%H %k %h %I %l')              |
+---------------------------------------------------------+
| 100 100 04 04 4                                         |
+---------------------------------------------------------+
1 row in set (0.00 sec)

TIME_TO_SEC(time)


将时间型值转换为秒。

mysql> SELECT TIME_TO_SEC('22:23:00');
+---------------------------------------------------------+
| TIME_TO_SEC('22:23:00')                                 |
+---------------------------------------------------------+
| 80580                                                   |
+---------------------------------------------------------+
1 row in set (0.00 sec)

TO_DAYS(date)


给定日期型值 date,返回天数(自公元 0 年以来的天数)。

mysql> SELECT TO_DAYS(950501);
+---------------------------------------------------------+
| TO_DAYS(950501)                                         |
+---------------------------------------------------------+
| 728779                                                  |
+---------------------------------------------------------+
1 row in set (0.00 sec)

UNIX_TIMESTAMP(), UNIX_TIMESTAMP(date)


不带任何参数时,该函数返回一个 unsigned integer 型的 UNIX 时间戳(自 '1970-01-01 00:00:00' UTC 以来的秒数)。如果有一个参数 date 的话,该函数返回自 '1970-01-01 00:00:00' UTC 至 date 的秒数。date 可以是日期型的字符串、DATETIME 型的字符串、时间戳或者 YYMMDD 或 YYYYMMDD 格式的数字。

mysql> SELECT UNIX_TIMESTAMP();
+---------------------------------------------------------+
| UNIX_TIMESTAMP()                                        |
+---------------------------------------------------------+
| 882226357                                               |
+---------------------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT UNIX_TIMESTAMP('1997-10-04 22:23:00');
+---------------------------------------------------------+
| UNIX_TIMESTAMP('1997-10-04 22:23:00')                   |
+---------------------------------------------------------+
| 875996580                                               |
+---------------------------------------------------------+
1 row in set (0.00 sec)

UTC_DATE, UTC_DATE()


返回当前 UTC 日期,格式为 'YYYY-MM-DD' 或者 YYYYMMDD,如果函数所处的上下文环境为字符串,则返回前者,否则如果上下文环境为数值型的,则返回后者。

mysql> SELECT UTC_DATE(), UTC_DATE() + 0;
+---------------------------------------------------------+
| UTC_DATE(), UTC_DATE() + 0                              |
+---------------------------------------------------------+
| 2003-08-14, 20030814                                    |
+---------------------------------------------------------+
1 row in set (0.00 sec)

UTC_TIME, UTC_TIME()


返回当前 UTC 时间,格式为 'HH:MM:SS' 或者 HHMMSS,如果函数所处的上下文环境为字符串,则返回前者,否则如果上下文环境为数值型的,则返回后者。

mysql> SELECT UTC_TIMESTAMP(), UTC_TIMESTAMP() + 0;
+---------------------------------------------------------+
| UTC_TIMESTAMP(), UTC_TIMESTAMP() + 0                    |
+---------------------------------------------------------+
| 2003-08-14 18:08:04, 20030814180804                     |
+---------------------------------------------------------+
1 row in set (0.00 sec)

WEEK(date[,mode])


该函数将返回 date 所在的周是当年的第几周。两个参数的 WEEK() 函数的使你能够指明一周起始于周日还是周一,以及返回值的范围应该是 0 到 53,还是 1 到 53。如果 mode 参数被忽略,则将使用 default_week_format 系统变量。

Mode 一周的第一天 范围 周 1 是第一周
0 Sunday 0-53 该年包括一个星期天
1 Monday 0-53 该年包含超过 3 天
2 Sunday 1-53 该年包括一个星期天
3 Monday 1-53 该年包含超过 3 天
4 Sunday 0-53 该年包含超过 3 天
5 Monday 0-53 该年包括一个星期一
6 Sunday 1-53 该年包含超过 3 天
7 Monday 1-53 该年包括一个星期一
mysql> SELECT WEEK('1998-02-20');
+---------------------------------------------------------+
| WEEK('1998-02-20')                                      |
+---------------------------------------------------------+
| 7                                                       |
+---------------------------------------------------------+
1 row in set (0.00 sec)

WEEKDAY(date)


返回 date 是其所在星期的第几天 (0 = Monday, 1 = Tuesday, . 6 = Sunday)。

mysql> SELECT WEEKDAY('1998-02-03 22:23:00');
+---------------------------------------------------------+
| WEEKDAY('1998-02-03 22:23:00')                          |
+---------------------------------------------------------+
| 1                                                       |
+---------------------------------------------------------+
1 row in set (0.00 sec)

WEEKOFYEAR(date)


返回 date 所在的周是当年的第几周,范围从 1 到 53. WEEKOFYEAR() 是一个兼容性函数,其功能同 WEEK(date, 3)相同。

mysql> SELECT WEEKOFYEAR('1998-02-20');
+---------------------------------------------------------+
| WEEKOFYEAR('1998-02-20')                                |
+---------------------------------------------------------+
| 8                                                       |
+---------------------------------------------------------+
1 row in set (0.00 sec)

YEAR(date)


返回 date 的年份部分,范围为 1000 到 9999,对于日期 0 则返回 0。

mysql> SELECT YEAR('98-02-03');
+---------------------------------------------------------+
| YEAR('98-02-03')                                        |
+---------------------------------------------------------+
| 1998                                                    |
+---------------------------------------------------------+
1 row in set (0.00 sec)

YEARWEEK(date), YEARWEEK(date,mode)


返回 date 所在的年份和周数。mode 参数意义与 WEEK() 函数的完全一样。对于一年中的第一周和最后一周来说,结果中的年份可能会和 date 参数中的年份不同。

mysql> SELECT YEARWEEK('1987-01-01');
+---------------------------------------------------------+
| YEAR('98-02-03')YEARWEEK('1987-01-01')                  |
+---------------------------------------------------------+
| 198653                                                  |
+---------------------------------------------------------+
1 row in set (0.00 sec)

注意,这里的周数同 WEEK() 返回的不同,因为 WEEK() 函数的返回值在给定年份的的上下文环境中得出。

SQL FIELD()函数


SQL FIELD()函数实例代码教程

FIELD()函数返回的索引(从1开始的位置)的str在str1,str2,str3,...列表中。如果str没有找到,则返回0。

就是用第一个参数str,跟后面的N个字符串参数中寻找,如果寻找到一模一样的字符串,则返回其索引位置

FIELD(str,str1,str2,str3,...)


返回的索引(从1开始的位置)的str在str1,str2,str3,...列表中。如果str没有找到,则返回0。

例子

在第2个位置找到了字符串“ej”

SQL> SELECT FIELD('ej', 'Hej', 'ej', 'Heja', 'hej', 'foo');
+---------------------------------------------------------+
| FIELD('ej', 'Hej', 'ej', 'Heja', 'hej', 'foo')          |
+---------------------------------------------------------+
| 2                                                       |
+---------------------------------------------------------+
1 row in set (0.00 sec) 

SQL GROUP BY 语句


Aggregate 函数常常需要添加 GROUP BY 语句。

GROUP BY语句通常与集合函数(COUNT,MAX,MIN,SUM,AVG)一起使用,以按一个或多个列对结果集进行分组。

GROUP BY 语句


GROUP BY 语句用于结合 Aggregate 函数,根据一个或多个列对结果集进行分组。

SQL GROUP BY 语法
SELECT column_name(s)
FROM table_name
WHERE condition
GROUP BY column_name(s)
ORDER BY column_name(s);

SQL HAVING 子句


在 SQL 中增加 HAVING 子句原因是,WHERE 关键字无法与 Aggregate 函数一起使用。

HAVING子句已添加到SQL中,因为WHERE关键字不能用于聚合函数。

SQL HAVING 语法
SELECT column_name(s)
FROM table_name
WHERE condition
GROUP BY column_name(s)
HAVING condition
ORDER BY column_name(s);

SQL UCASE() 函数


UCASE() 函数把字段的值转换为大写。

SQL UCASE() 语法
SELECT UCASE(column_name) FROM table_name;      

SQL LCASE() 函数


LCASE() 函数把字段的值转换为小写。

SQL LCASE() 语法
SELECT LCASE(column_name) FROM table_name;     

​ SQL MID() 函数


MID() 函数用于从文本字段中提取字符。

SQL MID() 语法
SELECT MID(column_name,start[,length]) FROM table_name;       
参数 描述
column_name 必需。要提取字符的字段。
start 必需。规定开始位置(起始值是 1)。
length 可选。要返回的字符数。如果省略,则 MID() 函数返回剩余文本。

SQL ROUND() 函数


ROUND() 函数用于把数值字段舍入为指定的小数位数。

SQL ROUND() 语法
SELECT ROUND(column_name,decimals) FROM table_name;        
参数 描述
column_name 必需。要舍入的字段。
decimals 必需。规定要返回的小数位数。

SQL NOW() 函数


NOW() 函数返回当前系统的日期和时间。

SQL NOW() 语法
SELECT NOW() FROM table_name;        

SQL FORMAT() 函数


FORMAT() 函数用于对字段的显示进行格式化。

SQL FORMAT() 语法
SELECT FORMAT(column_name,format) FROM table_name;        
参数 描述
column_name 必需。要格式化的字段。
format 必需。规定格式。

SQL SQRT 函数


SQRT 函数用于计算得出任何数值的平方根。你可以像下面这样使用 SELECT 语句计算任何数值的平方根:

    SQL>  select SQRT(16);
    +----------+
    | SQRT(16) |
    +----------+
    | 4.000000 |
    +----------+
    1 row in set (0.00 sec)

你在这里看到的是浮点数,因为 SQL 以浮点数类型来进行平方根的计算。

你还可以使用 SQRT 函数来计算表中记录的平方根。要获得对 SQRT 函数更深入的了解,请考虑 employee_tbl 表,表中记录如下所示:

    SQL> SELECT * FROM employee_tbl;
    +------+------+------------+--------------------+
    | id   | name | work_date  | daily_typing_pages |
    +------+------+------------+--------------------+
    |    1 | John | 2007-01-24 |                250 |
    |    2 | Ram  | 2007-05-27 |                220 |
    |    3 | Jack | 2007-05-06 |                170 |
    |    3 | Jack | 2007-04-06 |                100 |
    |    4 | Jill | 2007-04-06 |                220 |
    |    5 | Zara | 2007-06-06 |                300 |
    |    5 | Zara | 2007-02-06 |                350 |
    +------+------+------------+--------------------+
    7 rows in set (0.00 sec)

现在,假设你想要获取每个记录中 daily_typing_pages 的平方根,那么你可以用如下命令来达到目的:

    SQL> SELECT name, SQRT(daily_typing_pages)
        -> FROM employee_tbl;
    +------+--------------------------+
    | name | SQRT(daily_typing_pages) |
    +------+--------------------------+
    | John |                15.811388 |
    | Ram  |                14.832397 |
    | Jack |                13.038405 |
    | Jack |                10.000000 |
    | Jill |                14.832397 |
    | Zara |                17.320508 |
    | Zara |                18.708287 |
    +------+--------------------------+
    7 rows in set (0.00 sec)

SQL RAND 函数


SQL 有一个 RAND 函数,用于产生 0 至 1 之间的随机数:

    SQL>  SELECT RAND( ), RAND( ), RAND( );
    +------------------+-----------------+------------------+
    | RAND( )          | RAND( )         | RAND( )          |
    +------------------+-----------------+------------------+
    | 0.45464584925645 | 0.1824410643265 | 0.54826780459682 |
    +------------------+-----------------+------------------+
    1 row in set (0.00 sec)

当以某个整数值作为参数来调用的时候,RAND() 会将该值作为随机数发生器的种子。对于每一个给定的种子,RAND() 函数都会产生一列可以复现的数字:

    SQL>  SELECT RAND(1), RAND( ), RAND( );
    +------------------+------------------+------------------+
    | RAND(1 )         | RAND( )          | RAND( )          |
    +------------------+------------------+------------------+
    | 0.18109050223705 | 0.75023211143001 | 0.20788908117254 |
    +------------------+------------------+------------------+
    1 row in set (0.00 sec)

你可以使用 ORDER BY RAND() 来对一组记录进行随机化排列,如下所示:

    SQL> SELECT * FROM employee_tbl;
    +------+------+------------+--------------------+
    | id   | name | work_date  | daily_typing_pages |
    +------+------+------------+--------------------+
    |    1 | John | 2007-01-24 |                250 |
    |    2 | Ram  | 2007-05-27 |                220 |
    |    3 | Jack | 2007-05-06 |                170 |
    |    3 | Jack | 2007-04-06 |                100 |
    |    4 | Jill | 2007-04-06 |                220 |
    |    5 | Zara | 2007-06-06 |                300 |
    |    5 | Zara | 2007-02-06 |                350 |
    +------+------+------------+--------------------+
    7 rows in set (0.00 sec)

现在,试试下面的命令:

    SQL> SELECT * FROM employee_tbl ORDER BY RAND();
    +------+------+------------+--------------------+
    | id   | name | work_date  | daily_typing_pages |
    +------+------+------------+--------------------+
    |    5 | Zara | 2007-06-06 |                300 |
    |    3 | Jack | 2007-04-06 |                100 |
    |    3 | Jack | 2007-05-06 |                170 |
    |    2 | Ram  | 2007-05-27 |                220 |
    |    4 | Jill | 2007-04-06 |                220 |
    |    5 | Zara | 2007-02-06 |                350 |
    |    1 | John | 2007-01-24 |                250 |
    +------+------+------------+--------------------+
    7 rows in set (0.01 sec)

    SQL> SELECT * FROM employee_tbl ORDER BY RAND();
    +------+------+------------+--------------------+
    | id   | name | work_date  | daily_typing_pages |
    +------+------+------------+--------------------+
    |    5 | Zara | 2007-02-06 |                350 |
    |    2 | Ram  | 2007-05-27 |                220 |
    |    3 | Jack | 2007-04-06 |                100 |
    |    1 | John | 2007-01-24 |                250 |
    |    4 | Jill | 2007-04-06 |                220 |
    |    3 | Jack | 2007-05-06 |                170 |
    |    5 | Zara | 2007-06-06 |                300 |
    +------+------+------------+--------------------+
    7 rows in set (0.00 sec)

SQL CONCAT 函数


CONCAT 函数用于将两个字符串连接为一个字符串,试一下下面这个例子:

    SQL> SELECT CONCAT('FIRST ', 'SECOND');
    +----------------------------+
    | CONCAT('FIRST ', 'SECOND') |
    +----------------------------+
    | FIRST SECOND               |
    +----------------------------+
    1 row in set (0.00 sec)

要对 CONCAT 函数有更为深入的了解,请考虑 employee_tbl 表,表中记录如下所示:

    SQL> SELECT * FROM employee_tbl;
    +------+------+------------+--------------------+
    | id   | name | work_date  | daily_typing_pages |
    +------+------+------------+--------------------+
    |    1 | John | 2007-01-24 |                250 |
    |    2 | Ram  | 2007-05-27 |                220 |
    |    3 | Jack | 2007-05-06 |                170 |
    |    3 | Jack | 2007-04-06 |                100 |
    |    4 | Jill | 2007-04-06 |                220 |
    |    5 | Zara | 2007-06-06 |                300 |
    |    5 | Zara | 2007-02-06 |                350 |
    +------+------+------------+--------------------+
    7 rows in set (0.00 sec)

现在,假设你想要将上表中所有的姓名(name)、id和工作日(work_date)连接在一起,那么可以通过如下的命令来达到目的:

    SQL> SELECT CONCAT(id, name, work_date)
        -> FROM employee_tbl;
    +-----------------------------+
    | CONCAT(id, name, work_date) |
    +-----------------------------+
    | 1John2007-01-24             |
    | 2Ram2007-05-27              |
    | 3Jack2007-05-06             |
    | 3Jack2007-04-06             |
    | 4Jill2007-04-06             |
    | 5Zara2007-06-06             |
    | 5Zara2007-02-06             |
    +-----------------------------+
    7 rows in set (0.00 sec)

SQL NULL 函数


SQL ISNULL()、NVL()、IFNULL() 和 COALESCE() 函数

请看下面的 "Products" 表:

P_Id ProductName UnitPrice UnitsInStock UnitsOnOrder
1 Jarlsberg 10.45 16 15
2 Mascarpone 32.56 23
3 Gorgonzola 15.67 9 20

假如 "UnitsOnOrder" 是可选的,而且可以包含 NULL 值。

我们使用下面的 SELECT 语句:

SELECT ProductName,UnitPrice*(UnitsInStock+UnitsOnOrder)                
FROM Products        

在上面的实例中,如果有 "UnitsOnOrder" 值是 NULL,那么结果是 NULL。

微软的 ISNULL() 函数用于规定如何处理 NULL 值。

NVL()、IFNULL() 和 COALESCE() 函数也可以达到相同的结果。

在这里,我们希望 NULL 值为 0。

下面,如果 "UnitsOnOrder" 是 NULL,则不会影响计算,因为如果值是 NULL 则 ISNULL() 返回 0:

SQL Server / MS Access
SELECT ProductName,UnitPrice*(UnitsInStock+ISNULL(UnitsOnOrder,0))                
FROM Products       
Oracle

Oracle 没有 ISNULL() 函数。不过,我们可以使用 NVL() 函数达到相同的结果:

SELECT ProductName,UnitPrice*(UnitsInStock+NVL(UnitsOnOrder,0))                
FROM Products        
MySQL

MySQL 也拥有类似 ISNULL() 的函数。不过它的工作方式与微软的 ISNULL() 函数有点不同。

在 MySQL 中,我们可以使用 IFNULL() 函数,如下所示:

SELECT ProductName,UnitPrice*(UnitsInStock+IFNULL(UnitsOnOrder,0))                
FROM Products        

或者我们可以使用 COALESCE() 函数,如下所示:

SELECT ProductName,UnitPrice*(UnitsInStock+COALESCE(UnitsOnOrder,0))               
FROM Products       

SQL REPLACE()字符串替换函数


实例

把数据库表article中的所有title字段里的w3cschool字符串替换成hello。

update `article` set title=replace(title,'w3cschool','hello');
replace函数定义
replace(original-string,search-string,replace-string)
参数

original-string: 被搜索的字符串。可为任意长度。 search-string: 要搜索并被 replace-string 替换的字符串。该字符串的长度不应超过 255 个字节。如果 search-string 是空字符串,则按原样返回原始字符串。 replace-string: 该字符串用于替换 search-string。可为任意长度。如果 replacement-string 是空字符串,则删除出现的所有 search-string。

说明

用字符串表达式3替换字符串表达式1中出现的所有字符串表达式2的匹配项。返回新的字符串。  如果有某个参数为 NULL,此函数返回 NULL。

SQL TRIM()函数去除字符串头尾空格


SQL 中的 TRIM 函数是用来移除掉一个字串中的字头或字尾。最常见的用途是移除字首或字尾的空白。这个函数在不同的资料库中有不同的名称:

  • MySQL: TRIM( ), RTRIM( ), LTRIM( )
  • Oracle: RTRIM( ), LTRIM( )
  • SQL Server: RTRIM( ), LTRIM( )

各种 trim 函数的语法如下:

  • TRIM ( [ [位置] [要移除的字串] FROM ] 字串): [位置] 的可能值为 LEADING (起头), TRAILING (结尾), or BOTH (起头及结尾)。 这个函数将把 [要移除的字串] 从字串的起头、结尾,或是起头及结尾移除。如果我们没有列出 [要移除的字串] 是什么的话,那空白就会被移除。
  • LTRIM(字串): 将所有字串起头的空白移除。
  • RTRIM(字串): 将所有字串结尾的空白移除。
例1 TRIM()
SELECT TRIM('   Sample   ');

结果:

'Sample'
例2 LTRIM()
SELECT LTRIM('   Sample   ');

结果:

'Sample   '
例3 RTRIM()
SELECT RTRIM('   Sample   ');

结果:

'   Sample'

拓展

详细实例全面解析SQL存储过程

详尽的SQL语句大全分类整理

工业数据库

Oracle

Mysql

SQL Server

Redis

MongoDB