SQL(Structured Query Language:结构化查询语言)是一种用于管理关系型数据库系统的标准语言。它提供了一套强大的语句和命令,用于执行各种数据库操作,包括数据的定义、查询、更新和删除。SQL
支持数据库结构的创建和修改,允许用户定义表结构、索引以及其他数据库对象。同时,它还负责控制数据访问权限,确保数据的安全性和完整性。通过
SQL,用户可以高效地处理和分析数据,支持从简单查询到复杂的数据操作和数据管理任务。
SQL 语言的历史可以追溯到20世纪70年代。最初由 IBM 的 Donald D. Chamberlin 和 Raymond F.
Boyce 开发,用于管理关系型数据库模型。1974年,SQL 首次发布为 SEQUEL(Structured English Query
Language),其目的是简化数据访问和操作。1979年,Oracle 发布了第一个商业化的 SQL 数据库。1986年,ANSI(美国国家标准学会)首次发布
SQL 标准,标志着 SQL 成为数据库行业的标准语言。
随后,SQL 经历了多次版本更新和扩展,增加了更多功能,逐渐成为现代关系型数据库的核心语言。
RDBMS 指关系型数据库管理系统,全称 Relational Database Management System。
RDBMS 是 SQL 的基础,同样也是所有现代数据库系统的基础,比如 MS SQL Server、IBM DB2、Oracle、MySQL 以及 Microsoft Access。
RDBMS 中的数据存储在被称为表的数据库对象中。表是相关的数据项的集合,它由列和行组成。
Warning
SQL 语言对字母的大小写不敏感。也就是说,SELECT 和 select 是一个意思。
SQL 语法树
数据定义语言(DDL,Data Definition Language)是一类用于定义和管理数据库结构的 SQL(Structured Query Language)子集。DDL
主要用于创建、修改和删除数据库对象,如表、视图、索引、用户等。
CREATE DATABASE
用于创建数据库,语法如下:
CREATE
DATABASE database_name
CREATE
用于创建新的数据库对象,语法如下:
CREATE TABLE table_name
(
column1 data_type ,
column2 data_type ,
column3 data_type ,
)
数据类型(data_type)规定了列可容纳何种数据类型。 在 MySQL 中,有三种主要的类型:文本、数字和日期/时间类型。
数据类型
描述
CHAR(size )
保存固定长度的字符串(可包含字母、数字以及特殊字符)。在括号中指定字符串的长度。最多 255 个字符。
VARCHAR(size )
保存可变长度的字符串(可包含字母、数字以及特殊字符)。在括号中指定字符串的最大长度。最多 255 个字符。注释:如果值的长度大于 255,则被转换为 TEXT 类型。
TINYTEXT
存放最大长度为 255 个字符的字符串。
TEXT
存放最大长度为 65,535 个字符的字符串。
BLOB
用于 BLOBs (Binary Large OBjects)。存放最多 65,535 字节的数据。
MEDIUMTEXT
存放最大长度为 16,777,215 个字符的字符串。
MEDIUMBLOB
用于 BLOBs (Binary Large OBjects)。存放最多 16,777,215 字节的数据。
LONGTEXT
存放最大长度为 4,294,967,295 个字符的字符串。
LONGBLOB
用于 BLOBs (Binary Large OBjects)。存放最多 4,294,967,295 字节的数据。
ENUM(x ,y ,z ,etc.)
允许你输入可能值的列表。可以在 ENUM 列表中列出最大 65535 个值。如果列表中不存在插入的值,则插入空值。注释:这些值是按照你输入的顺序存储的。可以按照此格式输入可能的值:ENUM('X','Y','Z')
SET
与 ENUM 类似,SET 最多只能包含 64 个列表项,不过 SET 可存储一个以上的值。
数据类型
描述
TINYINT(size )
-128 到 127 常规。0 到 255 无符号*。在括号中规定最大位数。
SMALLINT(size )
-32768 到 32767 常规。0 到 65535 无符号*。在括号中规定最大位数。
MEDIUMINT(size )
-8388608 到 8388607 普通。0 to 16777215 无符号*。在括号中规定最大位数。
INT(size )
-2147483648 到 2147483647 常规。0 到 4294967295 无符号*。在括号中规定最大位数。
BIGINT(size )
-9223372036854775808 到 9223372036854775807 常规。0 到 18446744073709551615 无符号*。在括号中规定最大位数。
FLOAT(size ,d )
带有浮动小数点的小数字。在括号中规定最大位数。在 d 参数中规定小数点右侧的最大位数。
DOUBLE(size ,d )
带有浮动小数点的大数字。在括号中规定最大位数。在 d 参数中规定小数点右侧的最大位数。
DECIMAL(size ,d )
作为字符串存储的 DOUBLE 类型,允许固定的小数点。
这些整数类型拥有额外的选项 UNSIGNED。通常,整数可以是负数或正数。如果添加 UNSIGNED 属性,那么范围将从 0 开始,而不是某个负数。
数据类型
描述
DATE()
日期。格式:YYYY-MM-DD注释:支持的范围是从 '1000-01-01' 到 '9999-12-31'
DATETIME()
*日期和时间的组合。格式:YYYY-MM-DD HH:MM:SS注释:支持的范围是从 '1000-01-01 00:00:00' 到 '9999-12-31 23:59:59'
TIMESTAMP()
*时间戳。TIMESTAMP 值使用 Unix 纪元('1970-01-01 00:00:00' UTC) 至今的描述来存储。格式:YYYY-MM-DD HH:MM:SS注释:支持的范围是从 '1970-01-01 00:00:01' UTC 到 '2038-01-09 03:14:07' UTC
TIME()
时间。格式:HH:MM:SS注释:支持的范围是从 '-838:59:59' 到 '838:59:59'
YEAR()
2 位或 4 位格式的年。注释:4 位格式所允许的值:1901 到 2155。2 位格式所允许的值:70 到 69,表示从 1970 到 2069。
即便 DATETIME 和 TIMESTAMP 返回相同的格式,它们的工作方式很不同。在 INSERT 或 UPDATE 查询中,TIMESTAMP
自动把自身设置为当前的日期和时间。TIMESTAMP 也接受不同的格式,比如 YYYYMMDDHHMMSS、YYMMDDHHMMSS、YYYYMMDD 或 YYMMDD。
SQL 中约束用于限制加入表的数据的类型。可以在创建表时规定约束(通过 CREATE TABLE 语句),或者在表创建之后也可以(通过 ALTER
TABLE 语句)。常见的约束有:
NOT NULL
UNIQUE
PRIMARY KEY
FOREIGN KEY
CHECK
DEFAULT
NOT NULL 约束强制列不接受 NULL 值。NOT NULL 约束强制字段始终包含值。这意味着,如果不向字段添加值,就无法插入新记录或者更新记录。
CREATE TABLE Persons
(
Id_P int NOT NULL ,
LastName varchar ( 255 ) NOT NULL ,
FirstName varchar ( 255 ),
Address varchar ( 255 ),
City varchar ( 255 )
)
UNIQUE 约束唯一标识数据库表中的每条记录。UNIQUE 和 PRIMARY KEY 约束均为列或列集合提供了唯一性的保证。PRIMARY KEY 拥有自动定义的
UNIQUE 约束。
Warning
每个表可以有多个 UNIQUE 约束,但是每个表只能有一个 PRIMARY KEY 约束。
CREATE TABLE Persons
(
Id_P int NOT NULL ,
LastName varchar ( 255 ) NOT NULL ,
FirstName varchar ( 255 ),
Address varchar ( 255 ),
City varchar ( 255 ),
UNIQUE ( Id_P )
)
如果需要命名 UNIQUE 约束,以及为多个列定义 UNIQUE 约束,请使用下面的 SQL 语法:
CREATE TABLE Persons
(
Id_P int NOT NULL ,
LastName varchar ( 255 ) NOT NULL ,
FirstName varchar ( 255 ),
Address varchar ( 255 ),
City varchar ( 255 ),
CONSTRAINT uc_PersonID UNIQUE ( Id_P , LastName )
)
当表已被创建时,如需在 "Id_P" 列创建 UNIQUE 约束,请使用下列 SQL:
ALTER TABLE Persons
ADD UNIQUE ( Id_P )
如需命名 UNIQUE 约束,并定义多个列的 UNIQUE 约束,请使用下面的 SQL 语法:
ALTER TABLE Persons
ADD CONSTRAINT uc_PersonID UNIQUE ( Id_P , LastName )
如需撤销 UNIQUE 约束,请使用下面的 SQL:
ALTER TABLE Persons
DROP INDEX uc_PersonID
PRIMARY KEY 约束唯一标识数据库表中的每条记录。主键必须包含唯一的值。主键列不能包含 NULL 值。每个表都应该有一个主键,并且每个表只能有一个主键。
下面的 SQL 在 "Persons" 表创建时在 "Id_P" 列创建 PRIMARY KEY 约束:
CREATE TABLE Persons
(
Id_P int NOT NULL ,
LastName varchar ( 255 ) NOT NULL ,
FirstName varchar ( 255 ),
Address varchar ( 255 ),
City varchar ( 255 ),
PRIMARY KEY ( Id_P )
)
如果需要命名 PRIMARY KEY 约束,以及为多个列定义 PRIMARY KEY 约束,请使用下面的 SQL 语法:
CREATE TABLE Persons
(
Id_P int NOT NULL ,
LastName varchar ( 255 ) NOT NULL ,
FirstName varchar ( 255 ),
Address varchar ( 255 ),
City varchar ( 255 ),
CONSTRAINT pk_PersonID PRIMARY KEY ( Id_P , LastName )
)
如果在表已存在的情况下为 "Id_P" 列创建 PRIMARY KEY 约束,请使用下面的 SQL:
ALTER TABLE Persons
ADD PRIMARY KEY ( Id_P )
如果需要命名 PRIMARY KEY 约束,以及为多个列定义 PRIMARY KEY 约束,请使用下面的 SQL 语法:
ALTER TABLE Persons
ADD CONSTRAINT pk_PersonID PRIMARY KEY ( Id_P , LastName )
如需撤销 PRIMARY KEY 约束,请使用下面的 SQL:
ALTER TABLE Persons
DROP
PRIMARY KEY
一个表中的 FOREIGN KEY 指向另一个表中的 PRIMARY KEY。让我们通过一个例子来解释外键。请看下面两个表:
"Persons" 表:
Id_P
LastName
FirstName
Address
City
1
Adams
John
Oxford Street
London
2
Bush
George
Fifth Avenue
New York
3
Carter
Thomas
Changan Street
Beijing
"Orders" 表:
Id_O
OrderNo
Id_P
1
77895
3
2
44678
3
3
22456
1
4
24562
1
"Orders" 中的 "Id_P" 列指向 "Persons" 表中的 "Id_P" 列。"Persons" 表中的 "Id_P" 列是 "Persons" 表中的 PRIMARY KEY。"
Orders" 表中的 "Id_P" 列是 "Orders" 表中的 FOREIGN KEY。FOREIGN KEY 约束用于预防破坏表之间连接的动作。
FOREIGN KEY 约束也能防止非法数据插入外键列,因为它必须是它指向的那个表中的值之一。
下面的 SQL 在 "Orders" 表创建时为 "Id_P" 列创建 FOREIGN KEY:
CREATE TABLE Orders
(
Id_O int NOT NULL ,
OrderNo int NOT NULL ,
Id_P int ,
PRIMARY KEY ( Id_O ),
FOREIGN KEY ( Id_P ) REFERENCES Persons ( Id_P )
)
如果需要命名 FOREIGN KEY 约束,以及为多个列定义 FOREIGN KEY 约束,请使用下面的 SQL 语法:
CREATE TABLE Orders
(
Id_O int NOT NULL ,
OrderNo int NOT NULL ,
Id_P int ,
PRIMARY KEY ( Id_O ),
CONSTRAINT fk_PerOrders FOREIGN KEY ( Id_P )
REFERENCES Persons ( Id_P )
)
如果在 "Orders" 表已存在的情况下为 "Id_P" 列创建 FOREIGN KEY 约束,请使用下面的 SQL:
ALTER TABLE Orders
ADD FOREIGN KEY ( Id_P )
REFERENCES Persons ( Id_P )
如果需要命名 FOREIGN KEY 约束,以及为多个列定义 FOREIGN KEY 约束,请使用下面的 SQL 语法:
ALTER TABLE Orders
ADD CONSTRAINT fk_PerOrders
FOREIGN KEY ( Id_P )
REFERENCES Persons ( Id_P )
如需撤销 FOREIGN KEY 约束,请使用下面的 SQL:
ALTER TABLE Orders
DROP
FOREIGN KEY fk_PerOrders
CHECK 约束用于限制列中的值的范围。如果对单个列定义 CHECK 约束,那么该列只允许特定的值。如果对一个表定义 CHECK
约束,那么此约束会在特定的列中对值进行限制。
下面的 SQL 在 "Persons" 表创建时为 "Id_P" 列创建 CHECK 约束。CHECK 约束规定 "Id_P" 列必须只包含大于 0 的整数。
CREATE TABLE Persons
(
Id_P int NOT NULL ,
LastName varchar ( 255 ) NOT NULL ,
FirstName varchar ( 255 ),
Address varchar ( 255 ),
City varchar ( 255 ),
CHECK ( Id_P > 0 )
)
如果需要命名 CHECK 约束,以及为多个列定义 CHECK 约束,请使用下面的 SQL 语法:
CREATE TABLE Persons
(
Id_P int NOT NULL ,
LastName varchar ( 255 ) NOT NULL ,
FirstName varchar ( 255 ),
Address varchar ( 255 ),
City varchar ( 255 ),
CONSTRAINT chk_Person CHECK ( Id_P > 0 AND City = 'Sandnes' )
)
如果在表已存在的情况下为 "Id_P" 列创建 CHECK 约束,请使用下面的 SQL:
ALTER TABLE Persons
ADD CHECK ( Id_P > 0 )
如果需要命名 CHECK 约束,以及为多个列定义 CHECK 约束,请使用下面的 SQL 语法:
ALTER TABLE Persons
ADD CONSTRAINT chk_Person CHECK ( Id_P > 0 AND City = 'Sandnes' )
如需撤销 CHECK 约束,请使用下面的 SQL:
ALTER TABLE Persons
DROP
CHECK chk_Person
DEFAULT 约束用于向列中插入默认值。如果没有规定其他的值,那么会将默认值添加到所有的新记录。
下面的 SQL 在 "Persons" 表创建时为 "City" 列创建 DEFAULT 约束:
CREATE TABLE Persons
(
Id_P int NOT NULL ,
LastName varchar ( 255 ) NOT NULL ,
FirstName varchar ( 255 ),
Address varchar ( 255 ),
City varchar ( 255 ) DEFAULT 'Sandnes'
)
通过使用类似 GETDATE() 这样的函数,DEFAULT 约束也可以用于插入系统值:
CREATE TABLE Orders
(
Id_O int NOT NULL ,
OrderNo int NOT NULL ,
Id_P int ,
OrderDate date DEFAULT GETDATE ()
)
如果在表已存在的情况下为 "City" 列创建 DEFAULT 约束,请使用下面的 SQL:
ALTER TABLE Persons
ALTER City SET DEFAULT 'SANDNES'
如需撤销 DEFAULT 约束,请使用下面的 SQL:
ALTER TABLE Persons
ALTER City DROP DEFAULT
CREATE INDEX 语句用于在表中创建索引。在不读取整个表的情况下,索引使数据库应用程序可以更快地查找数据。用户无法看到索引,它们只能被用来加速搜索/查询。
Warning
更新一个包含索引的表需要比更新一个没有索引的表更多的时间,这是由于索引本身也需要更新。因此,理想的做法是仅仅在常常被搜索的列(以及表)上面创建索引。
在表上创建一个简单的索引。允许使用重复的值:
-- "column_name" 规定需要索引的列
CREATE INDEX index_name
ON table_name ( column_name )
在表上创建一个唯一的索引。唯一的索引意味着两个行不能拥有相同的索引值。
CREATE UNIQUE INDEX index_name
ON table_name ( column_name )
希望以降序索引某个列中的值,可以在列名称之后添加保留字 DESC:
CREATE INDEX Index_Pers
ON Person ( LastName DESC )
希望索引不止一个列,您可以在括号中列出这些列的名称,用逗号隔开:
CREATE INDEX Index_Pers
ON Person ( LastName , FirstName )
ALTER TABLE 语句用于在已有的表中添加、修改或删除列。如需在表中添加列,请使用下列语法:
ALTER TABLE table_name
ADD column_name datatype
要删除表中的列,请使用下列语法:
ALTER TABLE table_name
DROP
COLUMN column_name
要改变表中列的数据类型,请使用下列语法:
ALTER TABLE table_name
ALTER
COLUMN column_name datatype
我们可以使用 DROP INDEX 命令删除表格中的索引。
DROP INDEX table_name . index_name
DROP TABLE 语句用于删除表(表的结构、属性以及索引也会被删除):
如果仅仅需要删除表格的内容,而不删除表的话,可以使用 TRUNCATE TABLE:
TRUNCATE TABLE table_name
数据查询语言(DML, Data Manipulation Language)是用于操作数据库中数据的编程语言。DML
主要用于执行对数据的增、删、改、查操作,允许用户和应用程序在数据库中插入、更新、删除和检索数据。
SELECT 用于从数据库表中查询数据。用户可以指定要检索的列、表以及查询条件。SELECT 的语法如下:
SELECT column_name
FROM table_name
使用 SELECT 的时候,可以为列名称和表名称指定别名(Alias):
SELECT column_name ( s )
FROM table_name
AS alias_name
SELECT column_name AS alias_name
FROM table_name
当使用 SELECT 查询到的数据时,想要仅仅列出不同(DISTINCT)的值,可以使用 DISTINCT 关键字。
DISTINCT 关键字用于返回唯一不同的值:
SELECT DISTINCT column_name
FROM table_name
如需有条件地从表中选取数据,可将 WHERE 子句添加到 SELECT 语句。WHERE 的语法如下:
SELECT column_name
FROM table_name
WHERE column operator value
operator 的类型如下:
操作符
描述
备注
=
等于
<>
不等于
在某些版本的 SQL 中,操作符 <>
可以写为 !=
。
>
大于
<
小于
>=
大于等于
<=
小于等于
BETWEEN
在某个范围内
LIKE
搜索某种模式
例如,选取居住在城市 "Beijing" 中的人,我们需要向 SELECT 语句添加 WHERE 子句:
SELECT *
FROM Persons
WHERE city = 'Beijing'
Warning
在 SQL 中,要是文本值(字符串)类型需要使用单引号引起来。如果是数值,则不需要使用引号。
SELECT * FROM Persons WHERE FirstName = 'Bush'
SELECT * FROM Persons WHERE Year > 1965
LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式,使用 %
定义通配符(模式中缺少的字母)。
SELECT column_name ( s )
FROM table_name
WHERE column_name LIKE pattern
希望从 "Persons" 表中选取居住在以 "g" 结尾的城市里的人:
SELECT *
FROM Persons
WHERE City LIKE '%g'
使用 NOT LIKE 选择不包含的数据。可以从 "Persons" 表中选取居住在不包含 "lon" 的城市里的人:
SELECT *
FROM Persons
WHERE City NOT LIKE '%lon%'
除了 %
以外,还有其他的通配符可以在 LIKE 语言中使用。下表是支持的通配符:
通配符
描述
%
代表零个或多个字符
_
仅替代一个字符
[charlist]
字符列中的任何单一字符
[^charlist] 或者 [!charlist]
不在字符列中的任何单
IN 操作符允许我们在 WHERE 子句中规定多个值。
SELECT column_name ( s )
FROM table_name
WHERE column_name IN ( value1 , value2 , . . .)
例如,选取所有 LastName 为 Adams 和 Carter 的人:
SELECT *
FROM Persons
WHERE LastName IN ( 'Adams' , 'Carter' )
操作符 BETWEEN ... AND 会选取介于两个值之间的数据范围。这些值可以是数值、文本或者日期。
SELECT column_name ( s )
FROM table_name
WHERE column_name
BETWEEN value1 AND value2
以字母顺序显示介于 "Adams"(包括)和 "Carter"(不包括)之间的人,请使用下面的 SQL:
SELECT *
FROM Persons
WHERE LastName
BETWEEN 'Adams' AND 'Carter'
Danger
不同的数据库对 BETWEEN...AND 操作符的处理方式是有差异的。某些数据库会列出介于 "Adams" 和 "Carter" 之间的人,但不包括 "Adams" 和 "Carter" ;某些数据库会列出介于 "Adams" 和 "Carter" 之间并包括 "Adams" 和 "Carter" 的人;而另一些数据库会列出介于 "Adams" 和 "Carter" 之间的人,包括 "Adams" ,但不包括 "Carter" 。
所以,请检查你的数据库是如何处理 BETWEEN....AND 操作符的!
AND 和 OR 可在 WHERE 子语句中把两个或多个条件结合起来。如果第一个条件和第二个条件都成立,则 AND 运算符显示一条记录。
如果第一个条件和第二个条件中只要有一个成立,则 OR 运算符显示一条记录。
SELECT *
FROM Persons
WHERE FirstName = 'Thomas'
AND LastName = 'Carter'
SELECT *
FROM Persons
WHERE firstname = 'Thomas'
OR lastname = 'Carter'
Warning
对于一些复杂的 AND 和 OR 语句,可以使用圆括号()
括起来,组成复杂的查询语句:
SELECT * FROM Persons WHERE ( FirstName = 'Thomas' OR FirstName = 'William' )
AND LastName = 'Carter'
ORDER BY 语句用于根据指定的列对结果集进行排序。ORDER BY 语句默认按照升序对记录进行排序。希望按照降序对记录进行排序,可以使用
DESC 关键字。
SELECT Company , OrderNumber
FROM Orders
ORDER BY Company , OrderNumber
SELECT Company , OrderNumber
FROM Orders
ORDER BY Company DESC
INSERT INTO 用于向数据库表中插入新记录。用户需要指定要插入的数据值。语法如下:
INSERT INTO table_name
VALUES ( value1 , value2 , ....)
指定所要插入数据的列:
INSERT INTO table_name ( column1 , column2 , ...)
VALUES ( value1 , value2 , ....)
UPDATE 用于更新数据库表中的现有记录。用户需要指定更新的列和新的值,并可以设置条件来限制更新的范围。语法如下:
UPDATE table_name
SET column1 = new_value1 ,
column2 = new_value2
WHERE column1 = old_value_1
DELETE 用于从数据库表中删除记录。用户可以设置条件来确定哪些记录将被删除。
DELETE
FROM table_name
WHERE column1 = value1 , ...
可以在不删除表的情况下删除所有的行。这意味着表的结构、属性和索引都是完整的:
或者:
事务控制语言(TCL, Transaction Control Language)用于管理数据库事务的执行和控制。事务是指一系列操作的集合,这些操作要么全部成功,要么全部失败,以保证数据的一致性和完整性。
COMMIT 用于提交当前事务的所有操作,将其永久保存到数据库中。如果事务中的所有操作都成功执行,使用 COMMIT 确保这些更改是最终的。
ROLLBACK 用于撤销当前事务的所有操作,将数据库恢复到事务开始前的状态。如果事务中的操作出现错误或不满足某些条件,可以使用
ROLLBACK 撤销所有更改。
SAVEPOINT 用于在事务中设置一个保存点,允许在事务中指定一个恢复点,方便在发生错误时仅撤销到该保存点,而不是整个事务。
SAVEPOINT savepoint_name ;
SET TRANSACTION 用于设置事务的属性,比如隔离级别和访问模式,以影响事务的执行和并发控制。
SET TRANSACTION ISOLATION LEVEL level ;
假设我们有一个银行系统,涉及两个账户:AccountA 和 AccountB。我们要将 100 元从 AccountA 转账到 AccountB。以下是一个完整的事务示例:
BEGIN ;
-- 从 AccountA 扣款
UPDATE Accounts
SET balance = balance - 100
WHERE account_id = 'AccountA' ;
-- 向 AccountB 存款
UPDATE Accounts
SET balance = balance + 100
WHERE account_id = 'AccountB' ;
-- 提交事务
COMMIT ;
如果在扣款或存款过程中出现错误,可以使用 ROLLBACK 撤销所有操作,以保持数据的一致性。
高阶 SQL 查询包括复杂的查询功能和技巧,用于处理和分析数据库中的数据。高阶查询可以分成如下类型:
子查询:在主查询中嵌套一个查询,用于提供主查询所需的数据。
联接:结合多个表的数据。
聚合函数:对数据进行统计和汇总。
窗口函数:对结果集进行排序和计算窗口内的值。
CTE(公用表达式):用于简化复杂查询,提供临时的结果集。
在主查询中嵌套一个查询,用于提供主查询所需的数据。
SELECT employee_name
FROM employees
WHERE salary > ( SELECT AVG ( salary ) FROM employees );
联接(JOIN)指的是通过使用 JOIN...ON 关键字结合两个或多个表的数据进行查询。联接有如下几种:
INNER JOIN:返回两个表中匹配的记录。
LEFT JOIN(或 LEFT OUTER JOIN):返回左表中的所有记录,以及右表中匹配的记录;如果右表没有匹配的记录,则结果中右表的列会显示为
NULL。
RIGHT JOIN(或 RIGHT OUTER JOIN):返回右表中的所有记录,以及左表中匹配的记录;如果左表没有匹配的记录,则结果中左表的列会显示为
NULL。
FULL JOIN(或 FULL OUTER JOIN):返回两个表中的所有记录,包括那些在一个表中没有匹配的记录;这些记录在没有匹配的表中的列会显示为
NULL。
CROSS JOIN:返回两个表的笛卡尔积,即每一条记录都会与另一个表的每一条记录组合,结果集的大小是两个表记录数的乘积。
上述的关系可以总结成如下的一张图:
假设我们有两个表:employees 和 departments:
表:employees
employee_id
name
department_id
1
Alice
1
2
Bob
2
3
Charlie
3
4
David
NULL
表:departments
department_id
department_name
1
HR
2
Engineering
3
Marketing
4
Sales
INNER JOIN(内联接)和 JOIN 是相同的。
SELECT e . name , d . department_name
FROM employees e
INNER JOIN departments d
ON e . department_id = d . department_id ;
name
department_name
Alice
HR
Bob
Engineering
Charlie
Marketing
在 INNER JOIN 语法中,INNER 关键字可以经常会被省略。也就是说:INNER JOIN 和 JOIN 在 SQL 中实际上是相同的。INNER JOIN
是一种明确指定的连接类型,而 JOIN 默认情况下也是 INNER JOIN。两者都会返回两个表中匹配条件的记录。
SELECT e . name , d . department_name
FROM employees e
JOIN departments d
ON e . department_id = d . department_id ;
当两个表中有同名的列时,可以使用 USING 指定这些列进行连接。
上面的 SQL 语句也可以写成:
SELECT e . name , d . department_name
FROM employees e
JOIN departments d USING ( department_id )
LEFT JOIN(或 LEFT OUTER JOIN):返回左表中的所有记录,以及右表中匹配的记录;如果右表没有匹配的记录,则结果中右表的列会显示为
NULL。
SELECT e . name , d . department_name
FROM employees e
LEFT JOIN departments d
ON e . department_id = d . department_id ;
name
department_name
Alice
HR
Bob
Engineering
Charlie
Marketing
David
NULL
RIGHT JOIN(或 RIGHT OUTER JOIN):返回右表中的所有记录,以及左表中匹配的记录;如果左表没有匹配的记录,则结果中左表的列会显示为
NULL。
SELECT e . name , d . department_name
FROM employees e
RIGHT JOIN departments d
ON e . department_id = d . department_id ;
name
department_name
Alice
HR
Bob
Engineering
Charlie
Marketing
NULL
Sales
FULL JOIN(或 FULL OUTER JOIN):返回两个表中的所有记录,包括那些在一个表中没有匹配的记录;这些记录在没有匹配的表中的列会显示为
NULL。
SELECT e . name , d . department_name
FROM employees e
FULL JOIN departments d
ON e . department_id = d . department_id ;
name
department_name
Alice
HR
Bob
Engineering
Charlie
Marketing
David
NULL
NULL
Sales
CROSS JOIN 返回两个表的笛卡尔积,即每一条记录都会与另一个表的每一条记录组合,结果集的大小是两个表记录数的乘积。
SELECT e . name , d . department_name
FROM employees e
CROSS JOIN departments d ;
name
department_name
Alice
HR
Alice
Engineering
Alice
Marketing
Alice
Sales
Bob
HR
Bob
Engineering
Bob
Marketing
Bob
Sales
Charlie
HR
Charlie
Engineering
Charlie
Marketing
Charlie
Sales
David
HR
David
Engineering
David
Marketing
David
Sales
聚合函数用于对一组值进行计算并返回一个单一的值。常见的函数如下表所示:
函数
描述
AVG(column )
返回某列的平均值
BINARY_CHECKSUM
计算行的二进制校验和,用于数据更改检测
CHECKSUM
计算单个或多个列的校验和
CHECKSUM_AGG
计算一组值的校验和的总和
COUNT(column )
返回某列的行数(不包括NULL值)
COUNT(*)
返回被选行数
COUNT(DISTINCT column )
返回相异结果的数目
FIRST(column )
返回在指定的域中第一个记录的值(SQLServer2000 不支持)
LAST(column )
返回在指定的域中最后一个记录的值(SQLServer2000 不支持)
MAX(column )
返回某列的最高值
MIN(column )
返回某列的最低值
STDEV(column )
计算某列的样本标准差
STDEVP(column )
计算某列的总体标准差
SUM(column )
返回某列的总和
VAR(column )
计算某列的样本方差
VARP(column )
计算某列的总体方差
COUNT():返回行数。例如,COUNT(*) 计算表中的总行数,COUNT(column_name) 计算非 NULL 值的行数。
SELECT COUNT ( * )
FROM employees ;
SUM():计算指定列的总和。例如,SUM(salary) 计算薪资列的总和。
SELECT SUM ( salary )
FROM employees ;
窗口函数用于对结果集中的行进行分析,通常与 OVER() 子句一起使用。窗口函数不会像聚合函数那样将多行合并为一行,而是提供额外的分析能力。常见的窗口函数有:
函数
描述
ROW_NUMBER()
为结果集中的每一行分配唯一的序号
RANK()
为结果集中的每一行分配排名,如果存在相同的值,则分配相同的排名,并跳过后续的排名
DENSE_RANK()
为结果集中的每一行分配排名,如果存在相同的值,则分配相同的排名,不跳过排名
NTILE(n)
将结果集划分为 n 个桶,并为每一行分配一个桶编号
SUM()
计算指定列的累计和,在窗口内对数据进行汇总
AVG()
计算指定列的累计平均值,在窗口内对数据进行计算
MIN()
返回指定列的最小值,在窗口内对数据进行计算
MAX()
返回指定列的最大值,在窗口内对数据进行计算
COUNT()
计算指定列的行数,在窗口内对数据进行计算
LEAD()
返回当前行后面指定行偏移量的值
LAG()
返回当前行前面指定行偏移量的值
FIRST_VALUE()
返回窗口内第一个值
LAST_VALUE()
返回窗口内最后一个值
NTH_VALUE(n)
返回窗口内第 n 个值
在 MySQL 中,OVER()
子句用于与窗口函数一起使用,以定义窗口的范围。窗口函数允许你在查询结果中执行一些复杂的分析操作,而不需要子查询或自连接。OVER()
子句的主要作用是指定窗口函数操作的数据范围或“窗口”。
< 窗口函数 > OVER ([ PARTITION BY < 列名 > ] [ ORDER BY < 列名 > ] [ ROWS / RANGE < 窗口范围 > ])
其中:
PARTITION BY:将数据分区。窗口函数在每个分区内独立计算。如果省略此部分,整个结果集被视为一个分区。
ORDER BY:定义数据的排序顺序。窗口函数在分区内按此顺序操作。如果省略此部分,窗口函数的计算顺序可能是不确定的。
ROWS/RANGE:指定窗口的范围(可选)。ROWS 和 RANGE 用于定义窗口的起始和结束行。具体用法取决于需要计算的函数类型。
以下是一些使用 OVER() 子句的例子:
例子 1:查询计算员工按薪水排序的排名
SELECT employee_id ,
salary ,
RANK () OVER ( ORDER BY salary DESC ) AS salary_rank
FROM employees ;
例子 2:计算每个部门的员工数量
SELECT department_id ,
employee_id ,
COUNT ( * ) OVER ( PARTITION BY department_id ) AS department_employee_count
FROM employees ;
例子 3:查询计算每行的过去 7 天(包括当前行)的销售总和
SELECT order_date ,
sales_amount ,
SUM ( sales_amount ) OVER ( ORDER BY order_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW ) AS rolling_sum
FROM sales ;
CTE(Common Table Expression,公用表表达式)是一种 SQL 语法,用于在查询中定义一个临时的结果集,这个结果集可以在同一查询中的其他部分进行引用。CTE
通常用来简化复杂的查询,增加代码的可读性,并在多个地方重用查询结果。CTE 的基本语法如下:
WITH cte_name AS (
-- <CTE 查询>
)
SELECT < 列名 >
FROM cte_name
WHERE < 条件 > ;
其中:
WITH cte_name AS:定义 CTE 的名称(cte_name)和查询语句()。
CTE 查询:定义 CTE 的查询,它可以是任何合法的 SQL 查询。
SELECT:从 CTE 中选择数据, 的结果作为临时表使用。
例 1:查询 sales 表中销售额大于 1000 的记录
WITH SalesCTE AS ( SELECT order_date ,
sales_amount
FROM sales
WHERE sales_amount > 1000 )
SELECT order_date ,
sales_amount
FROM SalesCTE
ORDER BY order_date ;
例 2:递归 CTE。递归 CTE 允许处理层级结构或递归数据。递归 CTE 包含两个部分:锚查询和递归查询。
WITH RECURSIVE EmployeeHierarchy AS (
-- 锚查询
SELECT employee_id ,
manager_id ,
employee_name
FROM employees
WHERE manager_id IS NULL
UNION ALL
-- 递归查询
SELECT e . employee_id ,
e . manager_id ,
e . employee_name
FROM employees e
INNER JOIN EmployeeHierarchy eh
ON e . manager_id = eh . employee_id )
SELECT *
FROM EmployeeHierarchy ;
在这个例子中,EmployeeHierarchy 是一个递归 CTE,首先选择所有没有上级的员工(锚查询),然后递归地选择这些员工的下属(递归查询)。