《SQL必知必会》读后随笔
提示:结束 SQL 语句
多条SQL语句必须以分号(;)分隔。多数DBMS不需要在单条SQL语句后加分号,但也有DBMS可能必须在单条SQL语句后加上分号。
当然,如果愿意可以总是加上分号。事实上,即使不一定需要,加上分号也肯定没有坏处
提示:SQL 语句和大小写
请注意,SQL语句不区分大小写,因此 SELECT 与 select 是相同的。 同样,写成Select 也没有关系。许多SQL开发人员喜欢对SQL关键 字使用大写,而对列名和表名使用小写,这样做使代码更易于阅读和调试。不过,一定要认识到虽然SQL是不区分大小写的,但是表名、列名和值可能有所不同(这有赖于具体的DBMS及其如何配置)。
注意:第 0 行
第一个被检索的行是第0行,而不是第1行。因此,LIMIT 1 OFFSET 1 会检索第 2行,而不是第1行。
提示:MySQL 和 MariaDB 捷径
MySQL和MariaDB支持简化版的LIMIT 4 OFFSET 3语句,即LIMIT 3,4。 使用这个语法,逗号之前的值对应OFFSET,逗号之后的值对应LIMIT
说明:并非所有的SQL 实现都一样
我加入这一节只有一个原因,就是要说明,SQL虽然通常都有相当一致的实现,但你不能想当然地认为它总是这样。非常基本的语句往往是容易移植的,但较复杂的语句就不同了。当你针对某个问题寻找SQL解决方案时,一定要记住这一点
注意:ORDER BY 子句的位置
在指定一条 ORDER BY 子句时,应该保证它是 SELECT 语句中最后一 条子句。如果它不是最后的子句,将会出现错误消息。
提示:通过非选择列进行排序
通常,ORDER BY 子句中使用的列将是为显示而选择的列。但是,实 际上并不一定要这样,用非检索的列排序数据是完全合法的
警告:在多个列上降序排序
如果想在多个列上进行降序排序,必须对每一列指定 DESC 关键字
提示:区分大小写和排序顺序
在对文本性数据进行排序时,A 与 a 相同吗?a 位于 B 之前,还是 Z 之后?这些问题不是理论问题,其答案取决于数据库的设置方式。
在字典(dictionary)排序顺序中,A 被视为与 a 相同,这是大多数数据库管理系统的默认行为。
但是,许多DBMS允许数据库管理员在需要时改变这种行为(如果你的数据库包含大量外语字符,可能必须这样做)。
这里的关键问题是,如果确实需要改变这种排序顺序,用简单的 ORDER BY 子句可能做不到。你必须请求数据库管理员的帮助。
提示:在 WHERE 子句中使用圆括号
任何时候使用具有 AND 和 OR 操作符的 WHERE 子句,都应该使用圆括号明确地分组操作符。不要过分依赖默认求值顺序,即使它确实如你希望的那样。使用圆括号没有什么坏处,它能消除歧义
说明:Access 通配符
如果使用的是 Microsoft Access,需要使用 * 而不是 %。
说明:区分大小写
根据 DBMS的不同及其配置,搜索可以是区分大小写的。如果区分大小写,则'fish%'与 Fish bean bag toy 就不匹配。
说明:请注意后面所跟的空格
包括 Access在内的许多 DBMS都用空格来填补字段的内容。例如,如果某列有50个字符,而存储的文本为 Fish bean bag toy(17个字 符),则为填满该列需要在文本后附加33个空格。
这样做一般对数据及其使用没有影响,但是可能对上述SQL语句有负面影响。子句WHERE prod_name LIKE 'F%y'只匹配以 F 开头、以 y 结尾的 prod_name。
如果值后面跟空格,则不是以 y 结尾,所以 Fish bean bag toy 就不会检索出来。简单的解决办法是给搜索模式再增加一个%号:'F%y%' 还匹配 y 之后的字符(或空格)。更好的解决办法是用函数去掉空格
注意:请注意 NULL
通配符%看起来像是可以匹配任何东西,但有个例外,这就是 NULL。 子句 WHERE prod_name LIKE '%'不会匹配产品名称为 NULL 的行
说明:DB2 通配符
DB2不支持通配符_。
说明:Access 通配符
如果使用的是 Microsoft Access,需要使用 ? 而不是 _
说明:并不总是支持集合
与前面描述的通配符不一样,并不是所有 DBMS都支持用来创建集合的[]。只有微软的 Access 和 SQL Server 支持集合
说明:使用通配符的技巧
不要过度使用通配符。如果其他操作符能达到相同的目的,应该使用其他操作符。
在确实需要使用通配符时,也尽量不要把它们用在搜索模式的开始处。把通配符置于开始处,搜索起来是最慢的。
仔细注意通配符的位置。如果放错地方,可能不会返回想要的数据。
说明:是+还是||?
Access和 SQL Server使用 + 号。
DB2、Oracle、PostgreSQL、SQLite和 Open Office Base使用 ||
说明:TRIM 函数
大多数 DBMS都支持 RTRIM()(正如刚才所见,它去掉字符串右边的空格)、LTRIM()(去掉字符串左边的空格)以及 TRIM()(去掉字符串左右两边的空格)
注意:别名
别名的名字既可以是一个单词,也可以是一个字符串。如果是后者,字符串应该括在引号中。虽然这种做法是合法的,但不建议这么去做。多单词的名字可读性高,不过会给客户端应用带来各种问题。
因此, 别名最常见的使用是将多个单词的列名重命名为一个单词的名字
说明:导出列
别名有时也称为导出列(derived column),不管怎么叫,它们所代表的是相同的东西
提示:如何测试计算
SELECT 语句为测试、检验函数和计算提供了很好的方法。虽然 SELECT 通常用于从表中检索数据,但是省略了 FROM 子句后就是简单地访问和处理表达式,
例如
SELECT 3 * 2;将返回6
SELECT Trim(' abc '); 将返回 abc
SELECT Now();使用Now()函数返回当前日期和时间
现在你明白了,可以根据需要使用 SELECT 语句进行检验
提示:是否应该使用函数?
现在,你面临是否应该使用函数的选择。决定权在你,使用或是不使用也没有对错之分。如果你决定使用函数,应该保证做好代码注释, 以便以后你(或其他人)能确切地知道所编写的SQL代码的含义。
聚集函数(aggregate function)
对某些行运行的函数,计算并返回一个值
注意:只用于单个列
AVG()只能用来确定特定数值列的平均值,而且列名必须作为函数参 数给出。为了获得多个列的平均值,必须使用多个 AVG()函数
说明:NULL 值
AVG()函数忽略列值为 NULL 的行。
说明:NULL 值
如果指定列名,则 COUNT()函数会忽略指定列的值为空的行,但如果 COUNT()函数中用的是星号(*),则不忽略
提示:对非数值数据使用 MAX()
虽然 MAX()一般用来找出最大的数值或日期值,但许多(并非所有) DBMS 允许将它用来返回任意列中的最大值,包括返回文本列中的最大值。
在用于文本数据时,MAX()返回按该列排序后的最后一行。
说明:NULL 值
MAX()函数忽略列值为 NULL 的行
提示:对非数值数据使用 MIN()
虽然 MIN()一般用来找出最小的数值或日期值,但许多(并非所有) DBMS 允许将它用来返回任意列中的最小值,包括返回文本列中的最 小值。在用于文本数据时,MIN()返回该列排序后最前面的行
说明:NULL 值
MIN()函数忽略列值为 NULL 的行
提示:在多个列上进行计算
如本例所示,利用标准的算术操作符,所有聚集函数都可用来执行多 个列上的计算
说明:NULL 值
SUM()函数忽略列值为 NULL 的行
注意:DISTINCT 不能用于 COUNT(*)
如果指定列名,则 DISTINCT 只能用于 COUNT()。DISTINCT 不能用于 COUNT(*)。类似地,DISTINCT 必须使用列名,不能用于计算或表达式
提示:将 DISTINCT 用于 MIN()和 MAX()
虽然 DISTINCT 从技术上可用于 MIN()和 MAX(),但这样做实际上没有价值。一个列中的最小值和最大值不管是否只考虑不同值,结果都是相同的
说明:其他聚集参数
除了这里介绍的 DISTINCT 和 ALL 参数,有的DBMS还支持其他参数, 如支持对查询结果的子集进行计算的 TOP 和 TOP PERCENT。
注意:取别名
在指定别名以包含某个聚集函数的结果时,不应该使用表中实际的列名。虽然这样做也算合法,但许多 SQL实现不支持,可能会产生模糊的错误消息
注意:在使用 GROUP BY 子句前,需要知道一些重要的规定
(1)GROUP BY 子句可以包含任意数目的列,因而可以对分组进行嵌套, 更细致地进行数据分组。
(2)如果在 GROUP BY 子句中嵌套了分组,数据将在后指定的分组上进 行汇总。换句话说,在建立分组时,指定的所有列都一起计算(所以 不能从个别的列取回数据)。
(3)GROUP BY 子句中列出的每一列都必须是检索列或有效的表达式(但不能是聚集函数)。如果在 SELECT 中使用表达式,则必须在 GROUP BY 子句中指定相同的表达式。不能使用别名。
(4)大多数SQL实现不允许 GROUP BY 列带有长度可变的数据类型(如文本或备注型字段)。
(5)除聚集计算语句外,SELECT 语句中的每一列都必须在 GROUP BY 子句中给出。
(6)如果分组列中包含具有 NULL 值的行,则 NULL 将作为一个分组返回。 如果列中有多行 NULL 值,它们将分为一组。
(7)GROUP BY 子句必须出现在 WHERE 子句之后,ORDER BY 子句之前
说明:HAVING 和 WHERE 的差别
这里有另一种理解方法,WHERE 在数据分组前进行过滤,HAVING 在数 据分组后进行过滤。
这是一个重要的区别,WHERE 排除的行不包括在分组中。这可能会改变计算值,从而影响 HAVING 子句中基于这些值过滤掉的分组
说明:使用 HAVING 和 WHERE
HAVING 与 WHERE 非常类似,如果不指定 GROUP BY,则大多数 DBMS 会同等对待它们。
不过,你自己要能区分这一点。使用 HAVING 时应该结合 GROUP BY 子句,而 WHERE 子句用于标准的行级过滤。
提示:不要忘记 ORDER BY
一般在使用 GROUP BY 子句时,应该也给出 ORDER BY 子句。这是保证数据正确排序的唯一方法。千万不要仅依赖 GROUP BY 排序数据
注意:只能是单列
作为子查询的 SELECT 语句只能查询单个列。企图检索多个列将返回错误
注意:完全限定列名
你已经看到了为什么要使用完全限定列名,没有具体指定就会返回错误结果,因为 DBMS会误解你的意思。有时候,由于出现冲突列名而 导致的歧义性,会引起 DBMS抛出错误信息。
例如,WHERE 或 ORDER BY 子句指定的某个列名可能会出现在多个表中。好的做法是,如果在 SELECT 语句中操作多个表,就应使用完全限定列名来避免歧义
警告:完全限定列名
就像前一课提到的,在引用的列可能出现歧义时,必须使用完全限定 列名(用一个句点分隔表名和列名)。如果引用一个没有用表名限制的 具有歧义的列名,大多数 DBMS会返回错误
注意:不要忘了 WHERE 子句
要保证所有联结都有 WHERE 子句,否则 DBMS将返回比想要的数据多得多的数据。同理,要保证 WHERE 子句的正确性。不正确的过滤条件 会导致 DBMS返回不正确的数据
提示:叉联结
有时,返回笛卡儿积的联结,也称叉联结(cross join)
注意:性能考虑
DBMS 在运行时关联指定的每个表,以处理联结。这种处理可能非常耗费资源,因此应该注意,不要联结不必要的表。联结的表越多,性能下降越厉害。
注意:Oracle 中没有 AS
Oracle不支持 AS 关键字。要在 Oracle中使用别名,可以不用 AS,简单地指定列名即可(因此,应该是 Customers C,而不是 Customers AS C)
提示:用自联结而不用子查询
自联结通常作为外部语句,用来替代从相同表中检索数据的使用子查 询语句。虽然终的结果是相同的,但许多 DBMS处理联结远比处理 子查询快得多。应该试一下两种方法,以确定哪一种的性能更好
提示:外联结的类型
要记住,总是有两种基本的外联结形式:左外联结和右外联结。它们 之间的唯一差别是所关联的表的顺序。换句话说,调整 FROM 或 WHERE 子句中表的顺序,左外联结可以转换为右外联结。因此,这两种外联 结可以互换使用,哪个方便就用哪个
提示:组合查询和多个 WHERE 条件
多数情况下,组合相同表的两个查询所完成的工作与具有多个 WHERE 子句条件的一个查询所完成的工作相同。
换句话说,任何具有多个 WHERE 子句的 SELECT 语句都可以作为一个组合查询,在下面可以看到这一点
提示:UNION 与 WHERE
这一课一开始我们说过,UNION 几乎总是完成与多个 WHERE 条件相同 的工作。UNION ALL 为 UNION 的一种形式,它完成 WHERE 子句完成 不了的工作。
如果确实需要每个条件的匹配行全部出现(包括重复行), 就必须使用 UNION ALL,而不是 WHERE。
说明:其他类型的 UNION
某些 DBMS还支持另外两种 UNION:EXCEPT(有时称为 MINUS)可用 来检索只在第一个表中存在而在第二个表中不存在的行;而 INTERSECT 可用来检索两个表中都存在的行。实际上,这些 UNION 很 少使用,因为相同的结果可利用联结得到。
提示:插入多行
INSERT 通常只插入一行。要插入多行,必须执行多个 INSERT 语句。 INSERT SELECT是个例外,它可以用一条INSERT插入多行,不管SELECT 语句返回多少行,都将被 INSERT 插入。
说明:INSERT SELECT 与 SELECT INTO
它们之间的一个重要差别是前者导出数据,而后者导入数据
提示:更快的删除
如果想从表中删除所有行,不要使用 DELETE。可使用 TRUNCATE TABLE 语句,它完成相同的工作,而速度更快(因为不记录数据的变动)
注意:性能问题
因为视图不包含数据,所以每次使用视图时,都必须处理查询执行时需要的所有检索。如果你用多个联结和过滤创建了复杂的视图或者嵌 套了视图,性能可能会下降得很厉害。
因此,在部署使用了大量视图的应用前,应该进行测试。
说明:视图重命名
删除视图,可以使用 DROP 语句,其语法为 DROP VIEW viewname;。
覆盖(或更新)视图,必须先删除它,然后再重新创建
- 点赞
- 收藏
- 关注作者
评论(0)