7、SQL的派生表就像表的变量
前文,我们已经了解到SQL是一种声明性语言,因此不会有变量。(虽然在一些SQL语句中可能会存在)但你可以写这样的变量。那些野兽一般的表被称为派生表。
派生表只不过是包含在括号里的子查询。
-- A derived table
FROM (SELECT * FROM author)
需要注意的是,一些SQL方言要求派生表有一个关联的名字(也被称为别名)。
-- A derived table with an alias
FROM (SELECT * FROM author) a
当你想规避由SQL子句逻辑排序造成的问题时,你会发现派生表可以用帅呆了来形容。例如,如果你想在SELECT和WHERE子句中重用一个列表达式,只写(Oracle方言):
-- Get authors' first and last names, and their age in days
SELECT first_name, last_name, age
FROM (
SELECT first_name, last_name, current_date - date_of_birth age
FROM author
)
-- If the age is greater than 10000 days
WHERE age > 10000
注意,一些数据库和SQL:1999标准里已将派生表带到下一级别,,引入公共表表达式。这将允许你在单一的SQL SELECT中重复使用相同的派生表。上面的查询将转化为类似这样的:
WITH a AS (
SELECT first_name, last_name, current_date - date_of_birth age
FROM author
)
SELECT *
FROM a
WHERE age > 10000
很明显,对广泛重用的常见SQL子查询,你也可以灌输具体"a"到一个独立视图中。想要了解更多就看这里。
从中我们学到了什么呢?
再温习一遍,SQL主要是关于表引用,而不是列。好好利用这些表引用。不要害怕写派生表或其他复杂的表引用。
8、SQL GROUP BY转换之前的表引用
让我们重新考虑我们之前的FROM语句:
FROM a, b
现在,让我们来应用一个GROUP BY语句到上述组合表引用
GROUP BY A.x, A.y, B.z
这会产生一个只有其余三个列(!)的新的表引用。让我们再消化一遍。如果你用了GROUP BY,那么你在所有后续逻辑条款-包括选择中减少可用列的数量。这就是为什么你只可以从SELECT语句中的GROUP BY语句引用列语法的原因。
请注意,其他列仍然可能可作为聚合函数的参数:
SELECT A.x, A.y, SUM(A.z)
FROM A
GROUP BY A.x, A.y
值得注意并很不幸的是,MySQL不遵守这一标准,只会造成混乱。不要陷入MySQL的把戏。GROUP BY转换表引用,因此你可以只引用列也引用GROUPBY语句。
从中我们学到了什么呢?
GROUP BY,在表引用上操作,将它们转换成一个新表。
9、SQL SELECT在关系代数中被称为投影
当它在关系代数中使用时,我个人比较喜欢用"投影"一词中。一旦你生成你的表引用,过滤,转换它,你可以一步将它投影到另一个表中。SELECT语句就像一个投影机。表函数利用行值表达式将之前构造的表引用的每个记录转化为最终结果。
在SELECT语句中,你终于可以在列上操作,创建复杂的列表达式作为记录/行的一部分。
有很多关于可用的表达式,函数性质等的特殊规则。最重要的是,你应该记住这些:
1、你只能使用从“output”表引用产生的列引用
2、如果你有GROUP BY语句,你只可能从该语句或聚合函数引用列
3、当你没有GROUP BY语句时,你可以用窗口函数替代聚合函数
4、如果你没有GROUP BY语句,你就不能将聚合函数与非聚合函数结合起来
5、这有一些关于在聚合函数包装常规函数的规则,反之亦然
6、还有…
嗯,这有很多复杂的规则。他们可以填补另一个教程。例如,之所以你不能将聚合函数与非聚合函数结合起来投影到没有GROUP BY的SELECT语句中是因为:
1、凭直觉,没有任何意义。
2、对一个SQL初学者来说,直觉还是毫无帮助的,语法规则则可以。SQL:1999年引入了分组集,SQL:2003引入了空分组集GROUP BY()。每当存在没有显式GROUP BY语句的聚合函数时就会应用隐式的空分组集(规则2)。因此,最初关于逻辑顺序的那个规则就不完全正确了,SELECT的投影也会影响前面的逻辑结果,但语法语句GROUP BY却不受影响。
是不是有点迷糊?其实我也是,让我们看一些简单的吧。
从中我们学到了什么呢?
在SQL语句中,SELECT语句可能是最复杂的语句之一,即使它看起来是那么的简单。所有其他语句只不过是从这个到另一个表引用的的输送管道。通过将它们完全转化,后期地对它们应用一些规则,SELECT语句完完全全地搅乱了这些表引用的美。
为了了解SQL,重要的是要理解其他的一切,都要尝试在SELECT之前解决。即便SELECT在语法顺序中排第一的语句,也应该将它放到最后。
10.相对简单一点的SQL DISTINCT,UNION,ORDER BY,和OFFSET
看完复杂的SELECT之后,我们看回一些简单的东西。
集合运算(DISTINCT和UNION)
排序操作(ORDER BY,OFFSET..FETCH)
集合运算
集合运算在除了表其实没有其他东西的“集”上操作。嗯,差不多是这样,从概念上讲,它们还是很容易理解的
DISTINCT投影后删除重复项。
UNION求并集,删除重复项。
UNION ALL求并集,保留重复项。
EXCEPT求差集(在第一个子查询结果中删除第二个子查询中也含有的记录删除),删除重复项。
INTERSECT求交集(保留所有子查询都含有的记录),删除重复项。
所有这些删除重复项通常是没有意义的,很多时候,当你想要连接子查询时,你应该使用UNION ALL。
排序操作
排序不是一个关系特征,它是SQL仅有的特征。在你的SQL语句中,它被应用在语法排序和逻辑排序之后。保证可以通过索引访问记录的唯一可靠方法是使用ORDER BY a和OFFSET..FETCH。所有其他的排序总是任意的或随机的,即使它看起来像是可再现的。
OFFSET..FETCH是唯一的语法变体。其他变体包括MySQL'和PostgreSQL的LIMIT..OFFSET,或者SQL Server和Sybase的TOP..START AT(这里)。
让我们开始应用吧
跟其他每个语言一样,要掌握SQL语言需要大量的实践。上述10个简单的步骤将让你每天编写SQL时更有意义。另一方面,你也可以从常见的错误中学习到更多。下面的两篇文章列出许多Java(和其他)开发者写SQL时常见的错误:
· 10 Common Mistakes Java Developers Make when Writing SQL
· 10 More Common Mistakes Java Developers Make when Writing SQL