Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >三行五行的 SQL 只存在于教科书和培训班

三行五行的 SQL 只存在于教科书和培训班

原创
作者头像
朱迪
发布于 2024-02-29 08:43:47
发布于 2024-02-29 08:43:47
27800
代码可运行
举报
文章被收录于专栏:数据计算数据计算
运行总次数:0
代码可运行

教科书中 SQL 例句通常都很简单易懂,甚至可以当英语来读,这就给人造成 SQL 简单易学的印象。 但实际上,这种三行五行的 SQL 只存在于教科书和培训班,我们在现实业务中写的 SQL 不会论行,而是以 K 计的,一条 SQL 几百行 N 层嵌套,写出 3K5K 是常事,这种 SQL,完全谈不上简单易学,对专业程序员都是恶梦。 以 K 计本身倒不是大问题,需求真地复杂时,也只能写得长,Python/Java 代码可能会更长。但 SQL 的长和其它语言的长不一样,SQL 的长常常会意味着难写难懂,而且这个难写难懂和任务复杂度不成比例。除了一些最简单情况外,稍复杂些的任务,SQL 的难度就会陡增,对程序员的智商要求很高,所以经常用作应聘考题。

这是为什么呢? 其中一个原因是我们之前讲过的,SQL 像英语而缺乏过程性,要把很多动作搅合在一句中,凭空地增大思维难度。 但是我们会发现,即使 SQL 增加了步骤化的 CTE 语法,面对稍复杂的任务时,仍然会写的非常难懂。 这是因为,SQL 的描述能力还有不少重要的缺失,这导致程序员不能按自然思维写代码,要换着方法绕。 我们通过一个简单的例子来看一下。

简化的销售业绩表 T 有三个字段:sales 销售员,product 产品,amount 销售额。我们想知道空调和电视销售额都在前 10 名的销售员名单。 这个问题并不难,可以很自然地设计出计算过程: 1.按空调销售额排序,找出前 10 名; 2.按电视销售额排序,找出前 10 名; 3.对 1、2 的结果取交集,得到我们想要的

用 CTE 语法后 SQL 可以写成这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
with A as (select top 10 sales from T where product='AC' order by amount desc),
     B as (select top 10 sales from T where product='TV' order by amount desc)
select * from A intersect B
with A as (select top 10 sales from T where product='AC' order by amount desc),
     B as (select top 10 sales from T where product='TV' order by amount desc)
select * from A intersect B

这个句子不太短,但思路还是清晰的。

现在,我们把问题复杂化一点,改为计算所有产品销售额都在前 10 名的销售员,延用上述的思路很容易想到: 1. 列出所有产品; 2. 算出每种产品销售额的前 10 名,分别保存; 3. 针对这些前 10 名取交集; 遗憾开始出现,CTE 语法只能写出确定个数的中间结果。而我们事先不知道总共有多个产品,也就是说 WITH 子句的个数是不确定的,这就写不出来了。 好吧,换一种思路: 1.将数据按产品分组,将每组排序,计算出每组前 10 名; 2.针对这些前 10 名取交集; 这需要把第一步的分组结果保存起来,而这个中间结果是一个表,其中有个字段要存储对应的分组成员的前 10 名,也就是字段的取值将是个集合,SQL 不支持这种数据类型,还是写不出来。

我们可以再转换思路。按产品分组后,计算每个销售员在所有分组的前 10 名中出现的次数,若与产品总数相同,则表示该销售员在所有产品销售额中均在前 10 名内。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
select sales from ( 
    select sales from (
        select sales, rank() over (partition by product order by amount desc ) ranking
        from T ) where ranking <=10 )
group by sales having count(*)=(select count(distinct product) from T)
select sales from ( 
    select sales from (
        select sales, rank() over (partition by product order by amount desc ) ranking
        from T ) where ranking <=10 )
group by sales having count(*)=(select count(distinct product) from T)

在窗口函数支持下,终于能写出来了。但是,这样的思路,绕不绕呢,有多少人想到并写出来呢? 前两种简单的思路无法用 SQL 实现,只能采用第三种迂回的思路。这里的原因在于 SQL 的一个重要缺失:集合化不彻底。 SQL 有集合概念,但并未把集合作为一种基础数据类型提供,不允许字段取值是集合,除了表之外也没有其它集合形式的数据类型,这使得大量集合运算在思维和书写时都非常绕。

我们刚才用了关键字 top,事实上关系代数理论中没有这个东西,这不是 SQL 的标准写法。 没有 top 如何找前 10 名呢? 大体思路是这样:找出比自己大的成员个数作为是名次,然后取出名次不超过 10 的成员

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
select sales from (
    select A.sales sales, A.product product,
        (select count(*)+1 from T
         where A.product=product and A.amount<=amount) ranking
    from T A )where product='AC' and ranking<=10
select sales from (
    select A.sales sales, A.product product,
        (select count(*)+1 from T
         where A.product=product and A.amount<=amount) ranking
    from T A )where product='AC' and ranking<=10

注意,这里的子查询没办法用 CTE 语法分步写,因为它用到了主查询中的信息作为参数。

或可以用连接来写,这样子查询倒是可以用 CTE 语法分步了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
select sales from (
    select A.sales sales, A.product product, count(*)+1 ranking from T A, T B
    where A.sales=B.sales and A.product=B.product and A.amount<=B.amount
    group by A.sales,A.product )
where product='AC' and ranking<=10
select sales from (
    select A.sales sales, A.product product, count(*)+1 ranking from T A, T B
    where A.sales=B.sales and A.product=B.product and A.amount<=B.amount
    group by A.sales,A.product )
where product='AC' and ranking<=10

无论如何,这种东西都太绕了,专业程序员也要想一阵子,仅仅是计算了一个前 10 名。

造成这个现象的原因就是 SQL 的另一个缺失:缺乏有序支持。SQL 继承了数学上的无序集合,与次序有关的计算相当困难,而可想而知,与次序有关的计算会有多么普遍(诸如比上月、比去年同期、前 20%、排名等)。 SQL2003 标准中增加的窗口函数提供了一些与次序有关的计算能力,这在一定程度上缓解 SQL 有序计算的困难,前 10 名可以这样写:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
select sales from ( 
    select sales, rank() over (partition by product order by amount desc ) ranking
    from T )
where ranking <=10
select sales from ( 
    select sales, rank() over (partition by product order by amount desc ) ranking
    from T )
where ranking <=10

还是要用子查询。

窗口函数并没有根本改变 SQL 无序集合的基础,还是会有许多有序运算难以解决。比如我们经常用来举例的,计算一支股票最长连续上涨了多少天:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
select max(ContinuousDays) from (
    select count(*) ContinuousDays from (
        select sum(UpDownTag) over (order by TradeDate) NoRisingDays from (
            select TradeDate,case when Price>lag(price) over ( order by TradeDate) then 0 else 1 end UpDownTag from Stock ))
    group by NoRisingDays )
select max(ContinuousDays) from (
    select count(*) ContinuousDays from (
        select sum(UpDownTag) over (order by TradeDate) NoRisingDays from (
            select TradeDate,case when Price>lag(price) over ( order by TradeDate) then 0 else 1 end UpDownTag from Stock ))
    group by NoRisingDays )

自然思维是这样,按日期排序后开始计数,碰到涨了就加 1,跌了就清 0,看计数器最大计到几。但这个思路写不出 SQL,只能绕成这样多层嵌套的。 这个问题真地是当作应聘考题的,通过率不到 20%。

这么一个简单的例子就能暴露出 SQL 缺失的能力,SQL 缺失的内容还有更多,限于篇幅,这里就不再深入讨论了。 反正结果就是,SQL 实现查询时无法应用自然思路,经常需要绕路迂回,写得又长又难懂。 现实任务要远远比这些例子复杂,过程中会面临诸多大大小小的困难。这个问题绕一下,那个问题多几行,一个稍复杂的任务写出几百行多层嵌套的 SQL 也就不奇怪了,过两月自己也看不懂也不奇怪了。 事实上 SQL 一点也不容易。

SQL 很难写怎么办?用 esProc SPL! esProc SPL 是个 Java 写的开源软件,在这里https://github.com/SPLWare/esProc。 SPL 在 SQL 已有的集合化基础上增加了离散性,从而获得了彻底的集合化和有序能力,上面的例子就 SPL 就可以延用自然思路写出来: 所有产品销售额都在前 10 名的销售员,按产品分组,取每个组的前 10 名再算交集;

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
T.group(product).(~.top(10;-amount)).isect()
T.group(product).(~.top(10;-amount)).isect()

SPL 支持集合的集合,top 也只是常规的聚合计算,有了这些基础,实现自然思路很容易。 一支股票最长连续上涨了多少天,只要按自然思路写就行了

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cnt=0
Stock.sort(TradeDate).max(cnt=if(Price>Price[-1],cnt+1,0))
cnt=0
Stock.sort(TradeDate).max(cnt=if(Price>Price[-1],cnt+1,0))

SPL 有强大的有序计算能力,即使实现和上面 SQL 同样的逻辑也非常轻松:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Stock.sort(TradeDate).group@i(Price<Price[-1]).max(~.len())
Stock.sort(TradeDate).group@i(Price<Price[-1]).max(~.len())

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
三行五行的 SQL 只存在于教科书和培训班
教科书中 SQL 例句通常都很简单易懂,甚至可以当英语来读,这就给人造成 SQL 简单易学的印象。 但实际上,这种三行五行的 SQL 只存在于教科书和培训班,我们在现实业务中写的 SQL 不会论行,而是以 K 计的,一条 SQL 几百行 N 层嵌套,写出 3K5K 是常事,这种 SQL,完全谈不上简单易学,对专业程序员都是恶梦。 以 K 计本身倒不是大问题,需求真地复杂时,也只能写得长,Python/Java 代码可能会更长。但 SQL 的长和其它语言的长不一样,SQL 的长常常会意味着难写难懂,而且这个难写难懂和任务复杂度不成比例。除了一些最简单情况外,稍复杂些的任务,SQL 的难度就会陡增,对程序员的智商要求很高,所以经常用作应聘考题。
朱迪
2024/11/27
760
三行五行的 SQL 只存在于教科书和培训班
SQL 为什么动不动就 N 百行以 K 计
发明 SQL 的初衷之一显然是为了降低人们实施数据查询计算的难度。SQL 中用了不少类英语的词汇和语法,这是希望非技术人员也能掌握。确实,简单的 SQL 可以当作英语阅读,即使没有程序设计经验的人也能运用。
程序猿DD
2021/12/27
5440
SQL 为什么动不动就 N 百行以 K 计
SQL 为什么动不动就 N 百行以 K 计
SQL 中用了不少类英语的词汇和语法,这是希望非技术人员也能掌握。确实,简单的 SQL 可以当作英语阅读,即使没有程序设计经验的人也能运用。
不吃西红柿
2022/10/31
4100
没有数据库也能用 SQL
这种数据很适合用 SQL 做查询,但可惜 SQL 只能用在数据库,要安装个数据库并把这些文件导入,为这么个目标搞的整个应用系统都臃肿很多,实在是划不来。要是有什么技术能直接把这些文件当成数据表用 SQL 查询就好了。
朱迪
2024/10/30
1220
没有数据库也能用 SQL
ORM 技术的终结者
Hibernate,Mybatis 以及新兴的 JOOQ 等 ORM 技术能够方便地将数据库表映射成 Java 对象,并提供自动读写能力。ORM 技术使得用 Java 开发数据库应用变得更为高效。
朱迪
2024/12/13
910
ORM 技术的终结者
面向 Java 程序员的 SQLite 替代品
很多小微型应用程序也需要一些数据处理和计算能力,如果集成一个数据库就显得太沉重了,小巧轻量的 SQLite 是个不错的选择,因而被广泛应用。 不过,SQLite 也有些不方便的地方。SQLite 对外部数据文件及其它数据源的支持力度比较弱又很繁琐;它本身没有存储过程,需要用主程序配合来实现流程,这会导致经常和主程序交换数据(流程走向依赖于数据),效率低且代码麻烦;复杂些的运算用 SQL 也很难写,开发效率较低。
朱迪
2024/11/18
1050
SQL 的困难源于关系代数
在结构化数据计算领域,SQL 现在还是应用最广泛的工作语言,不仅被所有关系数据库采用,许多新进的大数据平台也将实现 SQL 作为目标。
朱迪
2024/03/21
2590
SQLite 的挑战者
很多小微型应用程序也需要一些数据处理和计算能力,如果集成一个数据库就显得太沉重了,这种情况下 SQLite 是一个不错的选择,它架构简单,集成方便,可持久化存储数据,并提供 SQL 实现计算能力。
朱迪
2024/12/04
1170
SQLite 的挑战者
深入MySQL窗口函数:原理和应用
窗口函数(Window Functions)是SQL标准中的一个高级特性,它允许用户在不改变查询结果集行数的情况下,对每一行执行聚合计算或其他复杂的计算。这些计算是基于当前行与结果集中其他行之间的关系进行的。窗口函数特别适用于需要执行跨多行的计算,同时又想保持原始查询结果集的行数不变的场景。
公众号:码到三十五
2024/03/19
2.9K0
深入MySQL窗口函数:原理和应用
一道SQL题的多种解法
自然的想法,寻找每个店铺是否连续三天都有销售额。利用现有的表,构造一个中间表,中间表既有当前日期的销售额,又有当前日期后两天的销售额,然后筛选销售额大于0的店铺名称即可。这种思路可以有(至少)两种实现方式。
数据森麟
2019/09/27
7230
一道SQL题的多种解法
最强总结!数据库开窗函数完全指南!!
开窗函数(Window Functions)是SQL中强大的分析工具,允许我们在不改变结果集行数的情况下进行复杂的聚合和分析操作。本文将系统地介绍开窗函数的用法和实际应用场景。
SQL数据库开发
2024/11/12
5650
最强总结!数据库开窗函数完全指南!!
MySQL数据库,从入门到精通:第十八篇——MySQL 8新特性全解析
MySQL从5.7版本直接跳跃发布了8.0版本,可见这是一个令人兴奋的里程碑版本。MySQL 8版本在功能上做了显著的改进与增强,开发者对MySQL的源代码进行了重构,最突出的一点是多MySQL Optimizer优化器进行了改进。不仅在速度上得到了改善,还为用户带来了更好的性能和更棒的体验。
默 语
2024/11/20
6710
MySQL数据库,从入门到精通:第十八篇——MySQL 8新特性全解析
第18章_MySQL8其它新特性
MySQL从5.7版本直接跳跃发布了8.0版本 ,可见这是一个令人兴奋的里程碑版本。MySQL 8 版本在功能上做了显著的改进与增强,开发者对 MySQL 的源代码进行了重构,最突出的一点是多 MySQL Optimizer 优化器进行了改进。不仅在速度上得到了改善,还为用户带来了更好的性能和更棒的体验。
程序员Leo
2023/08/07
4150
第18章_MySQL8其它新特性
Stream&Kotlin 还能再进化成什么
Java 很长时间都没有提供直接的数据集运算语法,写个简单的 SUM 都要很多行,更不要说分组、排序等复杂运算了。完全同样数据处理功能的代码远比 SQL 长,开发效率低下。 从 Java8 开始,Stream 登场,提供了支持 Lambda 语法的集合运算类。程序员不再为 SUM 就写很多行了,常规的分组、排序都有了现成函数。 数据集上的排序写成这样,比早期 Java 确实方便很多:
朱迪
2024/11/15
830
SQL后计算的利器SPL
现代应用开发中,通常只用SQL实现简单的数据存取动作,而主要的计算过程和业务逻辑直接在应用程序中实现,主要原因在于:
灰小猿
2022/09/02
1.2K0
esProc SPL 相当于有了 Python 加持的 DuckDB
对于桌面数据分析用户,如果说 DuckDB 是轻巧灵活的 "SQL 瑞士军刀",那 esProc SPL 简直就是自带 Python 技能的 "全能工具箱",既保留 SQL 的便捷,又突破 SQL 的天花板。
用户11520468
2025/03/10
1260
一文速学-零成本与数据沟通NL2SQL的概念和实现技术
关于NL2SQL的技术,如果大家最近有关注AI圈的话,或多或少都有所了解。其实很多业务场景下,于用户而言更多的是想要获取到最终数据的呈现效果,关于数据是如何获取得到的学习成本,是尽可能越少越好。众所周知当学习成本越低,那么产品的获客率也越高,当然对于我们技术人员来说,更多的还是研发思维。最终我们开发的服务主要还是为了业务服务,NL2SQL必然是以后数据开发的趋势所在,因此我们数据开发人员来说,暂且不谈掌握这门技术,清楚理念还是十分必要的。
fanstuck
2024/08/26
1.4K0
一文速学-零成本与数据沟通NL2SQL的概念和实现技术
LeetCode数据库题目集合
编写一个 SQL 查询,满足条件:无论 person 是否有地址信息,都需要基于上述两表提供 person 的以下信息:
MiChong
2021/02/24
9550
LeetCode数据库题目集合
这可能是最适合解决 SQL 数据分析痛点的编程语言
数据分析师的日常离不开各种数据操作,过滤、分组、汇总、排序……,面对这些基本需求,SQL 用起来确实得心应手。比如,查个用户分组销售额、筛选一批重要客户,这样的任务用 SQL 写出来就像英语一样简单,迅速搞定:
朱迪
2024/12/16
1210
为什么大数据平台要回归SQL
先说观点:因为还没找到更好的。 接下来说原因,首先来看看大数据平台都在干什么。 原因 结构化数据计算仍是重中之重 大数据平台主要是为了应对海量数据存储和分析的需求,海量数据存储的确不假,除了生产经营产生的结构化数据,还有大量音视频等非结构化数据,这部分数据很大,占用的空间也很多,有时大数据平台 80% 以上都存储着非结构化数据。不过,数据光存储还不行,只有利用起来才能产生价值,这就要进行分析了。 大数据分析要分结构化和非结构化数据两部分讨论。 结构化数据主要是企业生产经营过程中产生的业务数据,可以说是企业的
用户1564362
2022/09/29
9740
为什么大数据平台要回归SQL
相关推荐
三行五行的 SQL 只存在于教科书和培训班
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验