让你的 SQL 从优秀走向卓越:第 3 部分
神译局是36氪旗下编译团队,关注科技、商业、职场、生活等领域,重点介绍国外的新技术、新观点、新风向。
编者按:做开发的多少都要懂点SQL。但是只会点皮毛跟精通的差别还是很大的。有很多事情如果用SQL处理的话,会让你的后端开发工作事半功倍。那有哪些堪称神奇的SQL技巧呢?Taylor Brownlow用4篇系列文章总结了四个方面的SQL使用技巧,相信你会用得上的。此为第三部分,原文发表在Medium上,标题是:Take Your SQL from Good to Great: Part 3。
这是最有价值的SQL技巧系列文章的第 3 部分。本系列包括:
- 第 3 部分:不一样的JOIN
- 第 4 部分:Window函数
我还漏掉了哪些被低估的 SQL 技巧呢?请告诉我!
卑微的JOIN
在SQL的所有组件里面,JOIN 几乎是最不讨人喜欢的那个。它们不像Window函数那么华丽,也不像 CTE 那么具有变革性。但是,在使用和转换关系数据的时候,JOIN却可以说是我们最强大的工具。
那么,在介绍我最喜欢的JOIN使用方式之前,我会先提醒一下自己,为什么我们应该把更多的关注放在长期被忽视的JOIN身上。
1.优化的能力
SQL 的查询不会按照写入(或读取)的顺序进行。事实上,SELECT语句往往是执行查询的时候最后完成的步骤之一。
查询执行的时候首先执行的往往就有 FROM/JOIN 子句,这意味着如果我们想提高查询性能的话(谁不想啊),我们应该先看看我们的老朋友,JOIN。
2. 创造性地解决问题
我欣赏 JOIN 还有一个原因,不过这个原因跟技术关系不大,那就是JOIN让我们可以更有创意地去思考我们的数据分析。JOIN就像我们那些乐高积木上的螺柱,让我们得以搭建起更大的东西,比一个个数据表组成的数据库可以做出来的东西要大得多。
这种强大和灵活性是我们至今仍使用关系数据库的主要原因之一,尽管后者已有近 50 年的历史。
默认的JOIN
尽管可以使用的 JOIN 类型多如牛毛,但大多数人默认使用的还是 LEFT JOIN。之所以这样有些理由是很说得过去的。我们的阅读顺序是从左到右,从提到的第二张表添加列对我们来说更容易概念化,而且它的计算成本往往也比较低。
但是在某些情况下,把一串 LEFT JOIN 链接到一起可能会更加费力,计算成本也比较高,高到你难以承受。对于这些场景,我们可以看看更容易被忽视的某些别的类型的JOIN:
常见的 JOIN 类型。
你用得还不够多的 JOIN 功能
1. 用INNER JOIN 而不是把NULLS过滤掉
我经常看到大家会做这样的事情:
SELECT ...
FROM X
LEFT JOIN Y
ON X.KEY = Y.KEY
WHERE Y.KEY IS NOT NULL
这其实就是要找表X 和 Y 都有的记录(也叫做 INNER JOIN)。
SELECT ...
FROM X
INNER JOIN Y
ON X.KEY = Y.KEY
好处:这种用法的好处是恰到好处。不过说得更具体一点,这种用法更高效,因为你不需要那么多的 WHERE 语句,输入更快更容易,其他人理解也更容易。这本身算不上一个技巧,而是为了提醒你有 INNER JOIN 这个东西,而且它可以为你处理很多的过滤。
例子:
如果我想找出Spotify每天流媒体播放量最多的前 10 名歌曲,我可以:
- 找出播放次数最多的 10 首歌曲
- 用 INNER JOIN 将其连接回我们的每日流媒体数据:
INNER JOIN例子。
在这个例子当中,上面这个版本的查询(用top_tracks INNER JOIN daily_streams )用BigQuery 处理 27 MB 需要 0.9 秒。
用LEFT JOIN 选项 ( daily_streams LEFT JOIN top_tracks …WHERE …)处理27MB的数据需要 1.9 秒。
虽说这两种查询速度都很快,但如果数据量更大的话你就能看出区别了。
2. Range JOIN
我们一想到连接时,往往只考虑相等的情况,比方说 key == key。但其实我们往往需要更精细的逻辑把两张表合并在一起。
为此,我们可以进行range join,或者其实这是根据不等式进行的连接。比方说:
SELECT ...
FROM X
LEFT JOIN Y
ON X.KEY >=Y.RANGE_START AND X.KEY <= Y.RANGE_END
好处:如果需要根据一些时间逻辑来连接两张表的话,用range join真的很棒。想想看,如果要你找出用户组买了第一件产品之后做X的实例,或者找出在免费试用窗口期间发生的所有动作。如果没有range join的话,想做到这些是很困难的,如果说不是完全不可能的话。
例子:
我们不妨继续拿Spotify的数据来举例,假设我们要在每日热门曲目中找出刚发行不到30天的那些歌曲。
Range JOIN示例(
放大的SQL代码
从这个示例可以看出,只需要给 INNER JOIN 多添加一点条件,我们就可以扩展第一个例子,从而确保只挑选出发布不到 30 天的热门歌曲数据。
警告:不是每一种SQL语言都支持Join ON不等式。所以最好是在尝试之前先检查一下是否支持,否则你可能会得到了一个说法含糊的错误。如果发现不支持的话就换一种 SQL 语言。
3. Lateral JOIN连接
我用上Lateral Join的时间相对较晚,但不得不说,我喜欢到目前为止自己所看到的东西。我花了一段时间才弄清楚了这玩意儿到底是什么东西,但基本上要想理解lateral join有两个关键:
- 这种连接可让我们在 FROM 语句之后访问列。
- 其操作跟 A FOR EACH 操作符比较类似,因为这是针对查询的每一行进行数值计算。
好处:在 SQL 中具备执行 FOR EACH 这种操作的能力其实是非常方便的,尤其是当你需要针对基表的每一行生成新的行时。
此外,lateral join可以让你在同一个查询里面自行构建你的查询,从而简化了查询的逻辑。这里还有这里是两个这种逻辑合并的例子。
例子:
还是拿Spotify作为例子,假设我们想找出Spotify十大艺术家各自的5大金曲。
为此,我们可以:
- 找出十大歌星
- 对于每一位艺术家,用 LATERAL JOIN 找出各自的5大金曲:
点击
放大后的SQL代码
你也可以用窗口函数来做到这一点,这正是本系列的下一个主题!
警告:不是所有的SQL语言都支持LATERAL JOIN的概念。比方说,BigQuery通过 UNNEST 使用隐式的LATERAL JOIN。在尝试之前,先看看所使用工具的帮助文档!
译者:boxi。
本文来自翻译, 如若转载请注明出处。