type
status
date
slug
summary
tags
category
icon
password
sql练习场文档查询sql简介什么是sql关系数据库SELECT查询语句约束查询查询结果的过滤和排序DISTINCTGROUP BYLIMIT 和 OFFSET使用 JOIN 进行多表查询数据库规范化使用 JOIN 进行多表查询外连接关于 NULL 的简短说明使用表达式进行查询聚合查询常见聚合函数分组聚合函数查询的执行顺序查询执行顺序1. FROM and JOIN2. WHERE3. GROUP BY4. HAVING5. SELECT6. DISTINCT7. ORDER BY8. LIMIT / OFFSET总结插入行-Inserting rows插入新数据-Inserting new data更新行-Updating rows注意事项-Taking extra care删除行-Deleting rows注意事项-Taking extra care创建表-Creating tables表数据类型-Table data types表约束-Table constraints举例更改表-Altering tables添加列-Adding columns删除列-Removing columns重命名表-Renaming the table其他更改表的方法-Other changes删除表-Dropping tables参考翻译
sql练习场
鱼皮的sql练习网站,一个字爽
文档查询
sql简介
什么是sql
SQL(即结构化查询语言)是一种旨在允许技术和非技术用户查询、操作和转换关系数据库中的数据的语言。由于其简单性,SQL 数据库为数百万网站和移动应用程序提供安全且可扩展的存储
有许多流行的 SQL 数据库,包括 SQLite、MySQL、Postgres、Oracle 和 Microsoft SQL Server。它们都支持通用 SQL 语言标准,但每种实现在其支持的附加功能和存储类型方面可能有所不同。
关系数据库
学习 SQL 语法之前,了解关系数据库的实际情况有一个模型非常重要。关系数据库表示相关(二维)表的集合。每个表都类似于 Excel 电子表格,具有固定数量的命名列(表的属性或属性)和任意数量的数据行
例如,如果机动车辆管理局有一个数据库,您可能会找到一个表,其中包含该州人们驾驶的所有已知车辆。
例如,该表可能需要存储每辆车的型号名称、类型、车轮数量和车门数量。
表:车辆
ID | 品牌/型号 | 轮子 | 门 | 类型 |
1 | 福特福克斯 | 4 | 4 | 轿车 |
2 | 特斯拉跑车 | 4 | 2 | 运动的 |
3 | 川卡西忍者 | 2 | 0 | 摩托车 |
4 | 迈凯伦一级方程式赛车 | 4 | 0 | 种族 |
5 | 特斯拉S | 4 | 4 | 轿车 |
在这样的数据库中,您可能会找到其他相关表,其中包含诸如该州所有注册驾驶员的列表、可以授予的驾驶执照类型,甚至每个驾驶员的驾驶违规行为等信息。
通过学习 SQL,目标是学习如何回答有关此数据的具体问题,例如 “道路上哪些类型的车辆少于四轮?” ,或者“特斯拉生产了多少款汽车?” ,帮助我们做出更好的决策。
SELECT查询语句
要从 SQL 数据库检索数据,我们需要编写
SELECT
语句,这些语句通常通俗地称为查询。查询本身只是一个语句,它声明我们正在查找什么数据,在数据库中的何处找到它,以及(可选)如何在返回它之前对其进行转换。但它有一个特定的语法,这就是我们将在下面的练习中学习的内容。正如我们在简介中提到的,您可以将 SQL 中的表视为实体的类型(即狗),并将该表中的每一行视为该类型的特定实例(即哈巴狗、比格犬、不同颜色的哈巴狗等)。这意味着这些列将代表该实体的所有实例共享的公共属性(即毛皮颜色、尾巴长度等)。
给定一个数据表,我们可以编写的最基本的查询是选择表中所有行(实例)的几列(属性)。
选择特定列的查询
该查询的结果将是一组二维行和列,实际上是表的副本,但仅包含我们请求的列。
如果我们想要从表中检索所有数据列,则可以使用星号 (
*
) 简写来代替单独列出所有列名。选择所有列的查询
这个查询特别有用,因为它是一种通过一次转储所有数据来检查表的简单方法。
约束查询
现在我们知道如何从表中选择特定的数据列,但是如果您有一个包含一亿行数据的表,那么读取所有行将是低效的,甚至可能是不可能的。
为了过滤返回的某些结果,我们需要
WHERE
在查询中使用子句。该子句通过检查特定列值以确定是否应包含在结果中来应用于每行数据。选择带有约束的查询
AND
可以通过连接多个或OR
逻辑关键字(即 num_wheels >= 4 AND Doors <= 2)来构建更复杂的子句。以下是一些可用于数值数据(即整数或浮点)的有用运算符:Operator | Condition | 示例 |
=、!=、< <=、>、>= | 标准数字运算符 | col_name != 4 |
BETWEEN … AND … | 数字在两个值的范围内(含) | col_name BETWEEN 1.5 AND 10.5 |
NOT BETWEEN … AND … | 数字不在两个值的范围内(含) | col_name NOT BETWEEN 1 AND 10 |
IN (…) | 列表中存在数字 | col_name IN (2, 4, 6) |
NOT IN (…) | 列表中不存在号码 | col_name NOT IN (1, 3, 5) |
除了使结果更易于管理和理解之外,编写子句来约束返回的行集还可以使查询运行得更快,因为减少了返回的不必要的数据。
您现在可能已经注意到,SQL 并不要求您将关键字全部大写,但作为一种约定,它可以帮助人们将 SQL 关键字与列名和表名区分开,并使查询更易于阅读。
当编写
WHERE
包含文本数据的列的子句时,SQL 支持许多有用的运算符来执行不区分大小写的字符串比较和通配符模式匹配等操作。下面我们展示了一些常见的文本数据特定运算符:Operator | Condition | 示例 |
= | 区分大小写的精确字符串比较(注意单个等于) | col_name = "abc" |
!= 或 <> | 区分大小写的精确字符串不等式比较 | col_name != "abcd" |
LIKE | 不区分大小写的精确字符串比较 | col_name LIKE “ABC” |
NOT LIKE | 不区分大小写的精确字符串不等式比较 | col_name NOT LIKE “ABCD” |
% | 在字符串中的任何位置使用以匹配零个或多个字符的序列(仅适用于 LIKE 或 NOT LIKE) | col_name LIKE "%AT%"(匹配“AT”、“ATTIC”、“CAT ”甚至“BATS”) |
_ | 在字符串中的任何位置使用来匹配单个字符(仅适用于 LIKE 或 NOT LIKE) | col_name LIKE "AN_"(匹配“AND”,但不匹配“AN”) |
IN (…) | 字符串存在于列表中 | col_name IN(“A”,“B”,“C”) |
NOT IN(...) | 列表中不存在字符串 | col_name NOT IN ("D", "E", "F") |
所有字符串都必须加引号,以便查询解析器可以区分字符串中的单词和 SQL 关键字。
查询结果的过滤和排序
DISTINCT
尽管数据库中的数据可能是唯一的,但任何特定查询的结果可能不是唯一的 - 以我们的电影表为例,许多不同的电影可以在同一年发布。在这种情况下,SQL 提供了一种便捷的方法,通过使用关键字来丢弃具有重复列值的行
DISTINCT
。选择具有唯一结果的查询
由于
DISTINCT
关键字会盲目删除重复行,因此我们将在下面中学习如何使用分组和子句基于特定列丢弃重复行GROUP BY
。GROUP BY
与我们在过去几课中整齐排列的表不同,实际数据库中的大多数数据都没有按特定的列顺序添加。因此,当表的大小增加到数千甚至数百万行时,可能很难通读和理解查询结果。
为了帮助解决此问题,SQL 提供了一种使用子句按给定列按升序或降序对结果进行排序的方法
ORDER BY
。选择具有排序结果的查询
指定子句后
ORDER BY
,每行都会根据指定列的值按字母数字顺序排序。在某些数据库中,您还可以指定排序规则以更好地对包含国际文本的数据进行排序ASC 升序
DESC 降序
LIMIT
和 OFFSET
与子句一起使用的另一个子句
ORDER BY
是LIMIT
andOFFSET
子句,它们是一种有用的优化,可以向数据库指示您关心的结果的子集。将
LIMIT
减少要返回的行数,并且可选OFFSET
将指定从何处开始计算行数。选择行数有限的查询
使用 JOIN 进行多表查询
数据库规范化
数据库规范化非常有用,因为它可以最大限度地减少任何单个表中的重复数据,并允许数据库中的数据彼此独立地增长(即,汽车发动机的类型可以独立于每种类型的汽车而增长)。作为权衡,查询变得稍微复杂一些,因为它们必须能够从数据库的不同部分查找数据,并且在处理许多大型表时可能会出现性能问题。
为了回答有关规范化数据库中数据跨越多个表的实体的问题,我们需要学习如何编写一个查询,该查询可以组合所有数据并准确提取我们需要的信息。
使用 JOIN 进行多表查询
共享有关单个实体的信息的表需要有一个主键,用于在数据库中唯一标识该实体。一种常见的主键类型是自动递增整数(因为它们节省空间),但它也可以是字符串、散列值,只要它是唯一的即可。
JOIN
在查询中使用该子句,我们可以使用此唯一键组合两个单独表中的行数据。我们将介绍的第一个连接是INNER JOIN
.在多个表上使用 INNER JOIN 选择查询
该
INNER JOIN
过程匹配第一个表和第二个表中具有相同键(由约束定义ON
)的行,以使用两个表中的组合列创建结果行。连接表后,将应用我们之前学到的其他子句。您可能会看到查询中的
INNER JOIN
简单地写为JOIN
. 这两者是等效的,我们将这些联接称为内联接,因为一旦您开始使用其他类型的联接,它们将使查询更易于阅读外连接
如果两个表的数据不对称,这种情况在不同阶段输入数据时很容易发生,那么我们就必须使用 ,
LEFT JOIN
或RIGHT JOIN
来FULL JOIN
确保您需要的数据不会被遗漏在结果中就像INNER JOIN这三个新连接必须指定要连接数据的列。
将表 A 联接到表 B 时,LEFT JOIN只包含 A 中的行,无论是否在 B 中找到匹配的行。情况RIGHT JOIN相同,但相反,无论在 A 中是否找到匹配的行,都保留 B 中的行。最后,FULL JOIN只是意味着保留两个表中的行,无论另一个表中是否存在匹配的行。
您可能会看到这些连接写为
LEFT OUTER JOIN
、RIGHT OUTER JOIN
或FULL OUTER JOIN
,但OUTER
实际上保留该关键字是为了兼容 SQL-92,并且这些查询分别相当于 LEFT JOIN
、RIGHT JOIN
、 和FULL JOIN
关于 NULL 的简短说明
NULL
减少数据库中值的可能性总是好的,因为在构造查询、约束(某些函数对空值的行为不同)以及处理结果时需要特别注意它们数据库中值的替代方法
NULL
是具有数据类型适当的默认值,例如数字数据为 0,文本数据为空字符串等。但是如果您的数据库需要存储不完整的数据,那么NULL
如果默认值是合适的,则值可以是合适的会扭曲以后的分析(例如,在对数值数据取平均值时)有时,也无法避免
NULL
值,正如上面中在外连接两个具有不对称数据的表时所看到的那样。在这些情况下,您可以使用or约束测试列中子句中的NULL
值。WHEREIS NULLIS NOT NULL
使用表达式进行查询
除了使用 SQL 查询和引用原始列数据之外,您还可以使用表达式在查询中的列值上编写更复杂的逻辑。这些表达式可以使用数学和字符串函数以及基本算术来在执行查询时转换值
使用表达式可以节省时间和对结果数据进行额外的后处理,但也会使查询更难以阅读,因此我们建议在查询部分使用表达式时,还为它们指定一个描述
SELECT
别名使用AS
关键字除了表达式之外,常规列甚至表也可以有别名,以便更容易在输出中引用它们,并作为简化更复杂查询的一部分。
聚合查询
SQL 还支持使用聚合表达式(或函数),它允许您汇总有关一组数据行的信息
如果没有指定的分组,每个聚合函数将在整个结果行集上运行并返回单个值。与普通表达式一样,为聚合函数指定别名可确保结果更易于阅读和处理。
常见聚合函数
以下是我们将在示例中使用的一些常见聚合函数:
Function 功能 | Description 描述 |
COUNT(*), COUNT(column) | 一个常用函数,用于在未指定列名的情况下计算组中的行数。否则,计算组中指定列中具有非 NULL 值的行数。 |
MIN(column) | 查找组中所有行的指定列中的最小数值。 |
MAX(column) | 查找组中所有行的指定列中的最大数值。 |
AVG(column) | 查找指定列中组中所有行的平均数值。 |
SUM(column) | 查找组中行的指定列中所有数值的总和。 |
分组聚合函数
除了聚合所有行之外,您还可以将聚合函数应用于该组中的各个数据组
GROUP BY
子句的工作原理是对指定列中具有相同值的行进行分组我们的查询变得相当复杂,但我们几乎已经介绍了
SELECT
查询的所有重要部分。您可能已经注意到的一件事是,如果 GROUP BY
子句在 WHERE
子句(过滤要分组的行)之后执行,那么我们到底如何过滤分组行?幸运的是,SQL 允许我们通过添加一个额外的
HAVING
子句来做到这一点,该子句专门与 GROUP BY
子句一起使用,以允许我们从结果集中过滤分组行。HAVING
子句约束的编写方式与 WHERE
子句约束相同,并应用于分组行在我们的示例中,这可能看起来不是一个特别有用的构造,但如果您想象数据具有数百万行具有不同的属性,则通常需要能够应用附加约束才能快速理解数据
如果您不使用“GROUP BY”子句,一个简单的“WHERE”子句就足够了。
查询的执行顺序
现在我们已经了解了select查询的所有部分,现在我们可以讨论它们如何在完整查询的上下文中组合在一起
每个查询都从在数据库中查找我们需要的数据开始,然后将该数据过滤为可以尽快处理和理解的内容。
由于查询的每个部分都是按顺序执行的,因此了解执行顺序非常重要,这样您才能知道在哪里可以访问哪些结果。
查询执行顺序
1. FROM
and JOIN
首先执行
FROM
子句和后续的 JOIN
以确定正在查询的总工作数据集。这包括该子句中的子查询,并且可能会导致在幕后创建临时表,其中包含要连接的表的所有列和行。
2. WHERE
一旦我们有了完整的工作数据集,第一遍
WHERE
约束将应用于各个行,不满足约束的行将被丢弃。每个约束只能直接从 FROM
子句中请求的表访问列。在大多数数据库中,查询的 SELECT
部分中的别名无法访问,因为它们可能包含依赖于尚未执行的查询部分的表达式。3. GROUP BY
应用
WHERE
约束后的剩余行将根据 GROUP BY
子句中指定的列中的公共值进行分组。分组的结果是,行数与该列中的唯一值一样多。隐含地,这意味着您应该只在查询中有聚合函数时才需要使用它。4. HAVING
如果查询有
GROUP BY
子句,则 HAVING
子句中的约束将应用于分组行,丢弃不满足约束的分组行。与 WHERE
子句一样,在大多数数据库中也无法通过此步骤访问别名。5. SELECT
最终计算查询的
SELECT
部分中的任何表达式。6. DISTINCT
在其余行中,标记为
DISTINCT
的列中具有重复值的行将被丢弃。7. ORDER BY
如果
ORDER BY
子句指定了顺序,则行将按指定数据按升序或降序排序。由于查询的 SELECT
部分中的所有表达式都已计算,因此您可以在此子句中引用别名。8. LIMIT
/ OFFSET
最后,超出
LIMIT
和 OFFSET
指定范围的行将被丢弃,留下从查询返回的最终行集。总结
并非每个查询都需要具备我们上面列出的所有部分,但 SQL 如此灵活的部分原因是它允许开发人员和数据分析师快速操作数据,而无需编写额外的代码,而这一切只需使用上述子句即可。
插入行-Inserting rows
我们之前将数据库中的表描述为行和列的二维集合,其中列是属性,行是表中实体的实例。在 SQL 中,数据库模式描述了每个表的结构以及表的每列可以包含的数据类型。
例如,在我们的“电影”表中,“年份”列中的值必须是整数,“标题”列中的值必须是字符串。
这种固定的结构使得数据库在存储数百万甚至数十亿行的情况下仍然高效且一致。
插入新数据-Inserting new data
当向数据库插入数据时,我们需要使用
INSERT
语句,该语句声明要写入哪个表、要填充的数据列以及要插入的一行或多行数据。一般来说,插入的每一行数据都应包含表中每个相应列的值。您只需按顺序列出即可一次插入多行。
在某些情况下,如果数据不完整并且表包含支持默认值的列,则可以通过显式指定来插入仅包含您拥有的数据列的行。
在这些情况下,值的数量需要与指定的列数匹配。
尽管这是一个更冗长的语句,但以这种方式插入值具有向前兼容的好处。例如,如果您向表中添加一个具有默认值的新列,则无需更改硬编码的
INSERT
语句来适应该更改。此外,您可以对要插入的值使用数学和字符串表达式。
这对于确保插入的所有数据都以某种方式格式化很有用。
更新行-Updating rows
除了添加新数据之外,一项常见任务是更新现有数据,这可以使用
UPDATE
语句来完成。与 INSERT
语句类似,您必须准确指定要更新的表、列和行。此外,您要更新的数据必须与表架构中列的数据类型匹配。该语句的工作原理是采用多个列/值对,并将这些更改应用于满足
WHERE
子句中的约束的每一行。注意事项-Taking extra care
大多数使用 SQL 的人在更新数据时都会犯错误。无论是更新生产数据库中错误的行集,还是意外遗漏
WHERE
子句(这会导致更新应用于所有行),在构造 UPDATE
一个有用的提示是始终首先编写约束并在
SELECT
查询中测试它,以确保更新正确的行,然后才编写要更新的列/值对。删除行-Deleting rows
当需要从数据库中的表中删除数据时,可以使用
DELETE
语句,该语句描述了要操作的表,并通过 WHERE
如果您决定省略
WHERE
约束,则所有行都将被删除,这是完全清除表(如果有意的话)的快速而简单的方法。注意事项-Taking extra care
与上面的
UPDATE
语句类似,建议您首先在 SELECT
查询中运行约束,以确保删除正确的行。如果没有适当的备份或测试数据库,就很容易不可撤销地删除数据,因此请始终读取 DELETE
语句两次并执行一次。创建表-Creating tables
当您有新的实体和关系要存储在数据库中时,您可以使用
CREATE TABLE
语句创建新的数据库表。新表的结构由其表模式定义,该表模式定义了一系列列。每列都有一个名称、该列中允许的数据类型、对插入值的可选表约束以及可选的默认值。
如果已经存在同名的表,SQL 实现通常会抛出错误,因此要抑制错误并跳过创建表(如果存在),可以使用
IF NOT EXISTS
子句。表数据类型-Table data types
不同的数据库支持不同的数据类型,但常见的类型支持数字、字符串和其他杂项,例如日期、布尔值,甚至二进制数据。以下是您可能在实际代码中使用的一些示例。
Data type 数据类型 | Description 描述 |
INTEGER , BOOLEAN | 整数数据类型可以存储整个整数值,例如数字的计数或年龄。在一些实现中,布尔值仅表示为 0 或 1 的整数值。 |
FLOAT , DOUBLE , REAL | 浮点数据类型可以存储更精确的数值数据,例如测量值或小数值。根据该值所需的浮点精度,可以使用不同的类型。 |
CHARACTER(num_chars) , VARCHAR(num_chars) , TEXT | 基于文本的数据类型可以存储各种语言环境中的字符串和文本。各种类型之间的区别通常相当于使用这些列时数据库的底层效率。CHARACTER 和 VARCHAR(可变字符)类型都指定了它们可以存储的最大字符数(较长的值可能会被截断),因此可以更有效地存储和查询大表。 |
DATE , DATETIME | SQL 还可以存储日期和时间戳以跟踪时间序列和事件数据。它们使用起来可能很棘手,尤其是在跨时区操作数据时。 |
BLOB | 最后,SQL 可以将二进制数据直接存储在数据库中的 blob 中。这些值通常对数据库来说是不透明的,因此您通常必须将它们与正确的元数据一起存储以重新查询它们。 |
表约束-Table constraints
在本文中,我们不会太深入地研究表约束,但每一列都可以有额外的表约束,这些约束限制了可以插入到该列中的值。
这不是一个完整的列表,但会显示一些您可能会发现有用的常见约束。
Constraint 约束 | Description 描述 |
PRIMARY KEY | 这意味着该列中的值是唯一的,并且每个值都可用于标识该表中的一行。 |
AUTOINCREMENT | 对于整数值,这意味着该值会自动填充并随着每行插入而递增。并非所有数据库都支持。 |
UNIQUE | 这意味着该列中的值必须是唯一的,因此您不能在此列中插入与表中另一行具有相同值的另一行。与“主键PRIMARY KEY ”的不同之处在于它不必是表中行的键。 |
NOT NULL | 这意味着插入的值不能为“NULL”。 |
CHECK (expression) | 这允许您运行更复杂的表达式来测试插入的值是否有效。例如,您可以检查值是否为正数,或大于特定大小,或以特定前缀开头等。 |
FOREIGN KEY | 这是一致性检查,可确保该列中的每个值对应于另一个表中列中的另一个值。例如,如果有两个表,一个按 ID 列出所有员工,另一个列出他们的工资信息,则“FOREIGN KEY”可以确保工资表中的每一行都对应于主员工列表中的有效员工。 |
举例
更改表-Altering tables
当您的数据随时间发生变化时,SQL 为您提供了一种更新相应表和数据库架构的方法,即使用
ALTER TABLE
语句添加、删除或修改列和表约束。添加列-Adding columns
添加新列的语法与在
CREATE TABLE
语句中创建新行的语法类似。您需要指定列的数据类型以及任何潜在的表约束以及要应用于现有行和新行的默认值。在某些数据库(例如 MySQL)中,您甚至可以使用
FIRST
或 AFTER
子句指定在何处插入新列,但这不是标准功能。删除列-Removing columns
删除列就像指定要删除的列一样简单,但是,某些数据库(包括 SQLite)不支持此功能。相反,您可能必须创建一个新表并将数据迁移过来
重命名表-Renaming the table
如果您需要重命名表本身,也可以使用语句的
RENAME TO
子句来完成此操作。其他更改表的方法-Other changes
每个数据库实现都支持不同的更改表的方法,因此最好在继续之前查阅数据库文档:MySQL、Postgres、SQLite、Microsoft SQL Server
删除表-Dropping tables
在极少数情况下,您可能希望删除整个表,包括其所有数据和元数据,为此,您可以使用
DROP TABLE
语句,该语句与 DELETE
不同语句,因为它还从数据库中完全删除表模式。与
CREATE TABLE
语句类似,如果指定的表不存在,数据库可能会抛出错误,要抑制该错误,可以使用 IF EXISTS
子句。此外,如果您有另一个表依赖于要删除的表中的列(例如,具有
FOREIGN KEY
依赖项),那么您必须首先更新所有依赖表以删除依赖行,或者完全删除这些表