SQL 是声明式语言吗?

142 天前
 jingwei8340885

我们在学习 SQL 时,常常会听到这样的说法:SQL 是一种声明式语言。你只需要告诉它要做什么,而不需要告诉它怎么做,它会自己找到实现方法。也就是说,你要只用它描述任务目标,而不需要说明计算过程,这和常规的过程式语言有本质的差别。显然,这种程序语言听起来要易学好用得多。

真有这么好?


看一个例子,我们用 SQL 来查询员工中销售部女员工的数量,这是写出来的 SQL:

SELECT COUNT(*) FROM employee WHERE department='Sales' AND gender='Female'

看起来是这样,我们不需要关心具体的计算过程(遍历员工表中每一条记录,碰到符合条件的则计数加 1 ,不符合条件者略过,最后看计数),只要说清要查询的目标就可以了。

再举一例,按部门统计 30 岁以上员工的平均工资:

SELECT department,AVERAGE(salary) FROM employee WHERE age>30 GROUP BY departmen

看起来也不错,在这里我们确实不必关心到底如何分组和计算平均。

尽管 SQL 仍然是一种语法严格的语言,我们经过一定的学习才能写出正确的语句,但如果能不关心计算过程,那还是会省很多事的。


我们再看一个例子:找出销售额贡献度在前一半的大客户。如果设计一下计算过程大概是这样:

1.计算所有客户的总销售额

2.把客户按销售额倒排序,大的在前小的在后

3.按这次序从 0 累加每个客户的销售额,超过总销售额的一半时停止,则已经遍历过后客户则是目标客户

那么,用 SQL 写出来是什么样的呢?这是我能想出来的最简单写法了:

SELECT customer, amount, cum_amount
FROM ( SELECT customer,amount,SUM(amount) OVER (ORDER BY amount DESC) cum_amount FROM ordersummary )
WHERE cum_amount < (SELECT SUM(amount) FROM ordersummary)/2

仔细看一下这句 SQL ,它基本上就是在描述上述过程,有个子查询计算总销售额,再有个子查询按销售额倒排序,利用窗口函数计算出排序后列表的每一行的累计销售额,主查询再过滤出累计销售额小于总销售额一半的客户。和上述过程不同的只是书写次序,SQL 把开始计算总销售额写在了后面。还有一个微小的逻辑差异,SQL 的有序和分步运算不好,要把所有的累计销售额都计算出来再找出排在前面的。

这句 SQL 妥妥地是在描述这样的一个过程。说好的只要描述任务目标而不必关心计算过程呢?


再看简单一些的例子:查询销售额最大的 10 名客户。

某些 SQL 写出来是这样:

SELECT TOP 10 customer FROM ordersummary ORDER BY amount DESC

如果用某著名数据库来做,还得用子查询:

SELECT customer
FROM ( SELECT rownumber rn,customer FROM ordersummary ORDER BY amount DESC )
WHERE rn<=10

这两个 SQL 都明白无误地告诉我们计算过程:按销售额倒排序之后取前面 10 个。而且在这个著名数据库的语法中,还要人为造一个序号,也就是需更明确地告诉数据库应该如何算。


如果再找个数百行的 SQL (存储过程)来看,则可以更清楚地看到 SQL 照样也在老老实实地描述计算过程,而且不同的计算过程还会带来截然不同的计算性能甚至计算结果。


其实,任何程序设计语言都可以在某种程度说成是声明性语言:即只需要关心目标而不必关心过程。

如用 Java 写程序,你只要关心变量如何变化,而不必关心 CPU 中寄存器的动作,但用汇编语言就要关心了;同样,而用汇编语言时,你要关心寄存器的取值了,但却不必关心 CPU 里面与非门是如何动作的;

用 SQL 写代码时一般不用再关心变量、循环的具体动作,它就没有变量这些概念,但它有表、字段这些概念及相关的计算方法,你一样要关心这个层面的过程。从这个意义上讲,SQL 和其它程序设计语言在描述问题的方法上只是抽象层次不同,对于过程的说明并没有任何本质的不同。


为什么有这么多人会觉得 SQL 是所谓的声明式语言呢?这是因为 SQL 的设计时刻意弱化了“过程性”的特征,它为了让语句更像英语,把基本运算放进一条语句的各个子句。当计算任务涉及的环节都是 SQL 抽象层次内的基本运算,那就可以书写成一条语句中,貌似向 SQL 描述任务目标就行。

而常规程序语言通常不会把多个基本运算设计到一条语句的子句或函数参数中,而且提倡由程序员组合,这样人就会觉得需要描述过程,不具有“声明式”的特征了。

但是,一条语句的子句结构再复杂也是有限的,当任务复杂到超过这个结构的范围时,必须使用嵌套子查询或中间结果才能描述时,SQL 这个所谓“声明式”假象就会露馅了,还是要老老实实描述过程思路了。回想前面的例子就可以清楚地看出这一点。


SQL 在面对一些基本查询任务时确实时比 Java 这些高级语言易学好用,但并不是因为它比 Java 有更强的“声明式”特征,而是因为它在结构化数据计算方面的抽象程度比 Java 更高。

SQL 刻意弱化了“过程性”特征,比如没有中间变量这些东西,这导致它描述过程的能力非常弱,面对复杂的任务会难度陡增,甚至于有些任务根本无法只用 SQL 实现,因为缺乏过程能力的 SQL 就不是图灵完备的。所以,数据库厂商后来又只好再补充发明了存储过程以及 CTE 语法。

如果我们发明一种对数据计算有较高抽象程度的语言,同时保留“过程式”的特征,你会发现它会比 SQL 更易学好用,特别是面对复杂的业务逻辑时候。嗯,这就是 esProc SPL 。

SPL 资料

1541 次点击
所在节点    分享创造
10 条回复
Mistwave
142 天前
SQL 是声明式语言吗?
kyuuseiryuu
142 天前
HTML,CSS 都是声明式编程语言。
agagega
142 天前
有点强词夺理了。

C 语言算是非常典型的过程式语言吧,也是通常理解的「底层」语言。它对程序的运行环境有很多假定,比如浮点数的运算要符合 IEEE-754 标准,比如变量可以取地址,地址可以解引用,函数可以调用和返回等等。但 C 语言看起来比汇编更「高级」也更「已读」,就是因为它对程序语义的假定比汇编更少,编译器可以对 C 程序作优化,但对汇编能做的优化则极为罕见。

类似地,不可变的函数式语言拥有比 C 语言更少的假定,这使得编译器可以不对程序是如何计算的做出假设,甚至可以启用多线程进行计算。SQL 也类似,它的理论基础是关系代数而不是 C 语言抽象的机器环境,具体如何执行一个查询,优化器可以自行做决策。描述取大于几个的满足某某条件的数据,这是人类的要求,不是 SQL 本身造成的约束,你让 ChatGPT 帮你写 Java 程序去读,也得描述好这几个条件。

写 HTML 和 CSS 的时候,也需要想这个画面最后渲染出来是什么样子。难道这代表 HTML/CSS 也是过程式语言吗?
dacapoday
142 天前
关系代数
kaiveyoung
142 天前
广告贴?
Exception615
142 天前
省流:给 SPL 打广告的。哪里都能看到这个广告,说实话有点生理上反感了
OliverDD
142 天前
SQL 的普及是市场、技术的选择,代表关系模型干爆了层次模型。之后关系模型融合了对象模型发展出对象关系型数据库。再后来就是 NoSQL ,但没有能撼动 SQL 的地位。

说实话,你给 SPL 打广告可以直接正面打,刻意把 SQL 称为“非声明语言”因为它很难实现复杂需求...

建议了解下关系代数,看看 SQL 的理论基础再来抨击它有什么弊端
adoal
142 天前
在点进来之前我真的以为是初学者在提问。一点进来,还没往下翻页,就意识到是润乾了。
littlewing
142 天前
建议发到推广
XhivaW
141 天前
这东西广告帖越来越多了。。。 现在 v2 上居然都看到好几个。。。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1001384

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX