打好基础——数据库——SQL语句的执行顺序

SQL语句的执行顺序对于编写和理解复杂的查询是非常重要的。这是一个典型的SQL查询(例如SELECT)的基本执行顺序:

  1. FROMJOIN: 这些子句是首先执行的。在这个阶段,数据库会组合所有的输入表格,形成一个结果集。JOIN条件定义了如何组合这些表格。
  2. WHERE: 然后数据库将根据WHERE子句的条件过滤结果集。这个阶段移除所有不满足条件的行。
  3. GROUP BY: 然后如果有的话,将对剩下的行进行分组。这创建了一个可以用于聚合函数(如SUMAVGMAX等)的新的结果集。
  4. HAVING: 如果存在HAVING子句,那么它将在GROUP BY子句之后执行,用来过滤由GROUP BY子句创建的分组结果。同样,这个阶段会移除所有不满足条件的行。
  5. SELECT: 在此阶段,SQL将执行所有在SELECT子句中的表达式。
  6. DISTINCT: 如果有的话,DISTINCT关键字会移除任何重复的行。
  7. UNION:表联合。
  8. ORDER BY: 最后,如果存在ORDER BY子句,数据库将对结果集进行排序。
  9. LIMIT / OFFSET: 这些子句最后执行,确定返回给客户端的最终行数。

这是标准的逻辑执行顺序,实际物理执行顺序可能会根据数据库优化器的选择而变化,但是结果会与这个逻辑顺序产生的结果一致。这种逻辑顺序也解释了为什么你不能在WHERE子句中使用SELECT子句中定义的别名,因为SELECT子句在WHERE子句之后执行。


SQL语句可以看做以下的过程:

  1. 列出并连接需要用到的表。
  2. 过滤结果集,这过程会移除不满住条件的行。
  3. 创建分组
  4. 过滤分组,过滤分组中不满足条件的行。
  5. 执行表达式。
  6. 去重
  7. 排序
  8. 返回指定的行数。

打好基础——数据库——条件函数

CASE函数

可以根据条件列表的值返回多个可能的结果表达式中的一个。

可用在任何允许使用表达式的地方,但不能单独作为一个语句执行。

分为简单CASE函数和搜索CASE函数。

CASE 测试表达式
WHEN 简单表达式1 THEN 结果表达式1
WHEN 简单表达式2 THEN 结果表达式2 …
WHEN 简单表达式n THEN 结果表达式n
[ ELSE 结果表达式n+1 ]
END
CASE
WHEN 布尔表达式1 THEN 结果表达式1
WHEN 布尔表达式2 THEN 结果表达式2 …
WHEN 布尔表达式n THEN 结果表达式n
[ ELSE 结果表达式n+1 ]
END

下面这段代码,从user_profile表中选取出了三列,分别是device_id、gender和age_cut。分别表示:设备id、性别和年龄范围。第三列根据age的条件给出了不同的结果,这就是条件函数所做的事。

select
device_id,
gender,
case
    when age>=25 then '25岁及以上'
    when age>=20 then '20-24岁'
    when age<20 then '20岁以下'
    else '其他'
end as age_cut
from user_profile

select需要想象数据库是一条一条进入sql语句的,如果这条数据的满足某个case条件,则使某条数据的值。

打好基础——数据库——多表查询

在 SQL 中,多表查询是指同时涉及两个或多个表的查询操作。基于表之间的关系和联接方式的不同,可以归纳出以下几种常见的多表查询类型:

  1. 内连接(Inner Join): 内连接使用 INNER JOIN 或 JOIN 关键字来联接两个或多个表,并返回满足关联条件的匹配行。
  2. 左连接(Left Join): 左连接使用 LEFT JOIN 或 LEFT OUTER JOIN 关键字来联接两个表,并返回左表的所有行以及满足关联条件的匹配行。如果右表中没有匹配的行,对应的列将包含 NULL 值。
  3. 右连接(Right Join): 右连接使用 RIGHT JOIN 或 RIGHT OUTER JOIN 关键字来联接两个表,并返回右表的所有行以及满足关联条件的匹配行。如果左表中没有匹配的行,对应的列将包含 NULL 值。
  4. 全连接(Full Join): 全连接使用 FULL JOIN 或 FULL OUTER JOIN 关键字来联接两个表,并返回左表和右表的所有行。如果某个表中没有匹配的行,对应的列将包含 NULL 值。
  5. 交叉连接(Cross Join): 交叉连接使用 CROSS JOIN 关键字来联接两个表,返回两个表的笛卡尔积,即左表中的每一行与右表中的每一行的组合。

这些多表查询类型提供了不同的联接方式,用于处理涉及多个表的复杂查询需求。可以根据具体的数据关系和查询目的来选择合适的多表查询方式。

需要注意的是,不是所有的数据库系统都支持所有类型的JOIN操作。例如,MySQL支持所有这些JOIN操作,但SQLite就只支持内连接和左连接。因此左连接和右连接在功能上是等价的,但在实际使用中,左连接在各种数据库系统中的支持更好。


内连接

内连接(INNER JOIN):只返回两个表中匹配的行。如果某行在其中一个表中没有匹配,那么结果集中将不会显示这一行。例如:

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

上述查询会选择Orders表和Customers表中CustomerID相同的所有订单和客户名。

左连接

左连接(LEFT JOIN):返回左表中的所有行,即使在右表中没有匹配的行。如果没有匹配,结果集中右表的部分将为NULL。例如:

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

这个查询会返回Orders表中的所有订单,并包含与这些订单匹配的Customers表中的客户名。如果某个订单没有匹配的客户,那么CustomerName将为NULL。

右连接

右连接(RIGHT JOIN):返回右表中的所有行,即使在左表中没有匹配的行。如果没有匹配,结果集中左表的部分将为NULL。例如:

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

这个查询会返回Customers表中的所有客户,并包含与这些客户匹配的Orders表中的订单。如果某个客户没有匹配的订单,那么OrderID将为NULL。

全连接

全连接(FULL JOIN):返回两个表中所有的行。如果没有匹配,结果集中将会使用NULL来填充。例如:

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

这个查询会返回Orders表和Customers表中的所有订单和客户。如果某个订单没有匹配的客户,那么CustomerName将为NULL。如果某个客户没有匹配的订单,那么OrderID将为NULL。

笛卡尔积

在SQL中,笛卡尔积是两个表中所有行的所有可能组合。如果你想要得到笛卡尔积,你可以直接列出两个表,而不需要指定任何连接条件。

SELECT colors.color, sizes.size
FROM colors, sizes;

在实际应用中,我们通常会避免笛卡尔积,因为它往往会导致结果集过大,包含大量的无关数据。一般情况下,我们会指定一个连接条件(使用ON关键字),以便只获取需要的数据。

连接条件

在SQL中on后面表示的是连接条件,例如:

SELECT Orders.OrderID, Customers.CustomerName
FROM Orders
FULL JOIN Customers ON Orders.CustomerID = Customers.CustomerID AND month(date)=8;

上面这段SQL表示只有两个表的ID相等,并且连接表中date字段为8月的数据才允许被连接。

打好基础——数据库——字符匹配

一般形式为:

列名 [NOT ] LIKE

匹配串中可包含如下四种通配符:

_:匹配任意一个字符;
%:匹配0个或多个字符;
[ ]:匹配[ ]中的任意一个字符(若要比较的字符是连续的,则可以用连字符“-”表 达 );
[^ ]:不匹配[ ]中的任意一个字符。

查询学生表中姓‘张’的学生的详细信息。

SELECT * FROM 学生表 WHERE 姓名 LIKE ‘张%’

查询姓“张”且名字是3个字的学生姓名。

SELECT * FROM 学生表 WHERE 姓名 LIKE ‘张

如果把姓名列的类型改为nchar(20),在SQL Server 2012中执行没有结果。原因是姓名列的类型是char(20),当姓名少于20个汉字时,系统在存储这些数据时自动在后边补空格,空格作为一个字符,也参加LIKE的比较。可以用rtrim()去掉右空格。

SELECT * FROM 学生表 WHERE rtrim(姓名) LIKE ‘张’

查询学生表中姓‘张’、姓‘李’和姓‘刘’的学生的情况。

SELECT * FROM 学生表 WHERE 姓名 LIKE ‘[张李刘]%’

查询学生表表中名字的第2个字为“小”或“大”的学生的姓名和学号。

SELECT 姓名,学号 FROM 学生表 WHERE 姓名 LIKE ‘_[小大]%’

查询学生表中所有不姓“刘”的学生。

SELECT 姓名 FROM 学生 WHERE 姓名 NOT LIKE ‘刘%’

从学生表表中查询学号的最后一位不是2、3、5的学生信息。

SELECT * FROM 学生表 WHERE 学号 LIKE ‘%[^235]’

打好基础——数据库——排序和分组

在 SQL 中,你可以使用 ORDER BY 子句对结果进行排序。ORDER BY 子句允许你按照一个或多个列对查询结果进行升序(顺序)或降序(倒序)排序。

  1. 单列排序
  2. 多列排序

单列排序

SELECT column1, column2, ...
FROM table_name
ORDER BY column1 ASC; -- 升序排序

SELECT column1, column2, ...
FROM table_name
ORDER BY column1 DESC; -- 降序排序

在这个例子中,column1 是你想要按照其进行排序的列。使用 ASC 关键字进行升序排序(默认),使用 DESC 关键字进行降序排序。

多列排序

SELECT column1, column2, ...
FROM table_name
ORDER BY column1 ASC, column2 DESC; -- 先按 column1 升序排序,再按 column2 降序排序

在这个例子中,查询结果将首先按照 column1 进行升序排序,然后在具有相同 column1 值的情况下,按照 column2 进行降序排序。

注意事项

需要注意的是,ORDER BY 子句应该在查询的最后使用,以确保在排序之前已经执行了所有其他操作,例如过滤和连接。

通过适当地使用 ORDER BY 子句,你可以按照指定的顺序对查询结果进行排序,以满足你的需求。


GROUP BY 是 SQL 中的一个语句,它的主要作用是对结果集进行分组,以便对每个组执行聚合操作,如求和 (SUM)、平均值 (AVG)、计数 (COUNT)、最大值 (MAX)、最小值 (MIN)等。

分组需要满足以下规则:

“only_full_group_by”规则要求SELECT语句中的每一列,如果不是聚合函数(如SUM, COUNT, AVG等)的参数,那么就必须包含在GROUP BY子句中。这样可以确保查询的结果对于每个组都是”函数上依赖的”,即在每个组内具有唯一的值。

举个例子,假设你有一个”销售记录”的表,其中有”日期”、”商品”和”销售额”三个字段,你想要查询每一天的总销售额,可以使用 GROUP BY 来实现:

SELECT 日期, SUM(销售额) as 总销售额
FROM 销售记录
GROUP BY 日期;

这个查询将”销售记录”表按”日期”字段进行分组,然后对每一组(也就是每一天的销售记录)计算总销售额。

如果你想按照多个字段进行分组,可以在 GROUP BY 语句中列出多个字段。例如,如果你想按照日期和商品来计算每个商品每天的销售额,可以这样写:

SELECT 日期, 商品, SUM(销售额) as 总销售额
FROM 销售记录
GROUP BY 日期, 商品;

需要注意的是,SELECT 语句中列出的每个非聚合字段(在这个例子中是”日期”和”商品”),都应该包含在 GROUP BY 语句中。这样才能确保查询的结果在每个组内都是唯一的。这就是前面提到的 “only_full_group_by” 规则。

数据库全局排名方法论。

排名是数据库中的一个经典题目,实际上又根据排名的具体细节可分为3种场景:

  1. 连续排名,例如薪水3000、2000、2000、1000排名结果为1-2-3-4,体现同薪不同名,排名类似于编号
  2. 同薪同名但总排名不连续,例如同样的薪水分布,排名结果为1-2-2-4
  3. 同薪同名且总排名连续,同样的薪水排名结果为1-2-2-3

而且是全局排名,属于在某一范围内的全部数据进行排名。

一共有六种思路:

  1. 单表查询
  2. 子查询
  3. 自连接
  4. 笛卡尔积
  5. 自定义变量
  6. 窗口函数

打好基础——数据库——存储过程和函数

在 SQL 中,存储过程(Procedure)和函数(Function)是两种可执行的数据库对象,它们在功能和用法上有一些区别。

  1. 存储过程(Procedure):
    • 存储过程是一组预定义的 SQL 语句集合,可以执行一系列操作并返回零个或多个结果集。
    • 存储过程可以接受输入参数,用于定制操作行为。
    • 存储过程通常用于执行复杂的数据库操作,如数据插入、更新、删除等。
    • 存储过程不一定返回结果,可以只进行一系列操作而不返回数据。
  2. 函数(Function):
    • 函数是一段可重用的代码逻辑,接受输入参数并返回单个值。
    • 函数在 SQL 查询中用于计算和返回单个值,这个值可以在查询中使用或赋给其他变量。
    • 函数可以接受输入参数,并根据这些参数计算并返回一个值。
    • 函数的返回值可以是标量(如整数、字符串等),也可以是表或表值。
  • PROCEDURE:是一个子程序,但是它可以返回多个值。它通常用于执行一个特定的动作或一系列动作,如修改数据库中的数据。过程不能在 SQL 语句中使用,它需要通过 CALL 语句来调用。
  • FUNCTION:是一个可以返回单个值的子程序。它可以在 SQL 语句中使用,就像一个表达式。

如果有学过C/C++的,这两个类似于预处理和函数的区别。预处理可以替换源代码(并根据替换的代码给出返回值,也可以没有返回值),函数是调用并给出返回值。

打好基础——数据库——SQL语句。

SQL语句的分类

SQL语句主要可以分为四类:

  1. 数据查询语言 (DQL, Data Query Language): 这类语句主要用于查询和检索数据库中的数据。最常见的DQL语句是SELECT
  2. 数据定义语言 (DDL, Data Definition Language): DDL语句用于定义或修改数据库的结构。这些语句包括CREATE(创建新表或数据库)、ALTER(修改现有数据库结构)、DROP(删除表或数据库)等。
  3. 数据操控语言 (DML, Data Manipulation Language): 这类语句主要用于修改数据库中的数据。包括INSERT(插入新数据)、UPDATE(更新数据)、DELETE(删除数据)等。
  4. 数据控制语言 (DCL, Data Control Language): DCL语句用于控制不同用户和角色对数据库的访问和操作权限。这些语句包括GRANT(赋予权限)、REVOKE(撤销权限)等。

除此之外,还有一类叫做事务控制语言 (TCL, Transaction Control Language) 的语句,主要用于处理数据库事务,例如COMMITROLLBACK等。

数据定义语言 (DDL, Data Definition Language)

数据定义语言用于定义和管理数据库中的表和其他结构对象。有以下三种:

  1. CREATE,创建
  2. ALTER,修改
  3. DROP,删除