北京 [ 更换 ]
热门城市
北京上海广州深圳成都杭州南京武汉天津西安重庆青岛沈阳长沙大连厦门无锡福州济南宁波昆明苏州郑州长春合肥南昌哈尔滨常州烟台南宁温州石家庄太原珠海南通扬州贵阳东莞徐州大庆佛山威海洛阳淮安呼和浩特镇江潍坊桂林中山临沂咸阳包头嘉兴惠州泉州三亚赣州九江金华泰安榆林许昌新乡舟山慈溪南阳聊城海口东营淄博漳州保定沧州丹东宜兴绍兴唐山湖州揭阳江阴营口衡阳郴州鄂尔多斯泰州义乌汕头宜昌大同鞍山湘潭盐城马鞍山襄樊长治日照常熟安庆吉林乌鲁木齐兰州秦皇岛肇庆西宁介休滨州台州廊坊邢台株洲德阳绵阳双流平顶山龙岩银川芜湖晋江连云港张家港锦州岳阳长沙县济宁邯郸江门齐齐哈尔昆山柳州绍兴县运城齐河衢州太仓张家口湛江眉山常德盘锦枣庄资阳宜宾赤峰余姚清远蚌埠宁德德州宝鸡牡丹江阜阳莆田诸暨黄石吉安延安拉萨海宁通辽黄山长乐安阳增城桐乡上虞辽阳遵义韶关泸州南平滁州温岭南充景德镇抚顺乌海荆门阳江曲靖邵阳宿迁荆州焦作丹阳丽水延吉茂名梅州渭南葫芦岛娄底滕州上饶富阳内江三明淮南孝感溧阳乐山临汾攀枝花阳泉长葛汉中四平六盘水安顺新余晋城自贡三门峡本溪防城港铁岭随州广安广元天水遂宁萍乡西双版纳绥化鹤壁湘西松原阜新酒泉张家界黔西南保山昭通河池来宾玉溪梧州鹰潭钦州云浮佳木斯克拉玛依呼伦贝尔贺州通化朝阳百色毕节贵港丽江安康德宏朔州伊犁文山楚雄嘉峪关凉山雅安西藏四川广东河北山西辽宁黑龙江江苏浙江安徽福建江西山东河南湖北湖南海南贵州云南陕西甘肃青海台湾内蒙古广西宁夏香港澳门
培训资讯网 - 为兴趣爱好者提供专业的职业培训资讯知识

SQL为什么动不动就N百行以K计?

行以

发明SQL的初衷之一显然是为了降低人们实施数据查询计算的难度。SQL中用了不少类英语的词汇和语法,这是希望非技术人员也能掌握。确实,简单的SQL可以当作英语阅读,即使没有程序设计经验的人也能运用。

然而,面对稍稍复杂的查询计算需求,SQL就会显得力不从心,经常写出几百行有多层嵌套的语句。这种SQL,不要说非技术人员难以完成,即使对于专业程序员也不是件容易的事,常常成为很多软件企业应聘考试的重头戏。三行五行的SQL仅存在教科书和培训班,现实中用于报表查询的SQL通常是以“K”计的。

SQL困难的分析探讨

这是为什么呢?我们通过一个很简单的例子来考察SQL在计算方面的缺点。

设有一个由三个字段构成的销售业绩表(为了简化问题,省去日期信息):

sales_amount

销售业绩表

sales

销售员姓名,假定无重名

product

销售的产品

amount

该销售员在该产品上的销售额

现在我们想知道出空调和电视销售额都在前10名的销售员名单。

这个问题并不难,人们会很自然地设计出如下计算过程:

1. 按空调销售额排序,找出前10名;

2. 按电视销售额排序,找出前10名;

3. 对1、2的结果取交集,得到答案;

我们现在来用SQL做。

1. 找出空调销售额前10名,还算简单:

select top 10 sales from sales_amount where product="AC" order by amount desc
复制代码

2. 找出电视销售额前10名。动作一样:

select top 10 sales from sales_amount where product="TV" order by amount desc
复制代码

3. 求1、2的交集。这有点麻烦,SQL不支持步骤化,上两步的计算结果无法保存,只能再重抄一遍了:

select * from
    ( select top 10 sales from sales_amount where product="AC" order by amount desc )
intersect
    ( select top 10 sales from sales_amount where product="TV" order by amount desc )
复制代码

一个只三步的简单计算用SQL要写成这样,而日常计算中多达十几步的比比皆是,这显然超出来许多人的可接受能力。

我们知道了SQL的第一个重要缺点:不支持步骤化。把复杂的计算分步可以在很大程度地降低问题的难度,反过来,把多步计算汇成一步则很大程度地提高了问题的难度。

可以想象,如果老师要求小学生做应用题时只能列一个算式完成,小朋友们会多么苦恼(当然,不乏一些聪明孩子搞得定)。

SQL查询不能分步,但用SQL写出的存储过程可以分步,那么用存储过程是否可以方便地解决这个问题呢?

暂先不管使用存储过程的技术环境有多麻烦和数据库的差异性造成的不兼容,我们只从理论上来看用分步SQL是否能让这个计算更简单捷些。

1. 计算空调销售额前10名。语句还是那样,但我们需要把结果存起来供第3步用,而SQL中只能用表存储集合数据,这样我们要建一个临时表:

create temporary table x1 as
    select top 10 sales from sales_amount where product="AC" order by amount desc
复制代码

2. 计算电视销售额前10名。类似地

create temporary table x2 as
    select top 10 sales from sales_amount where product="TV" order by amount desc
复制代码

3. 求交集,前面麻烦了,这步就简单些

select * from x1 intersect x2
复制代码

分步后思路变清晰了,但临时表的使用仍然繁琐。在批量结构化数据计算中,作为中间结果的临时集合是相当普遍的,如果都建立临时表来存储,运算效率低,代码也不直观。

而且,SQL不允许某个字段取值是集合(即临时表),这样,有些计算即使容忍了繁琐也做不到。

如果我们把问题改为计算所有产品销售额都在前10名的销售员,试想一下应当如何计算,延用上述的思路很容易想到:

1. 将数据按产品分组,将每组排序,取出前10名;

2. 将所有的前10名取交集;

由于我们事先不知道会有多个产品,这样需要把分组结果也存储在一个临时表中,而这个表有个字段要存储对应的分组成员,这是SQL不支持的,办法就行不通了。

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

select sales
from ( select sales,
     from ( select sales,
                   rank() over (partition by product order by amount desc ) ranking
            from sales_amount)
     where ranking <=10 )
group by sales
having count(*)=(select count(distinct product) from sales_amount)
复制代码

这样的SQL,有多少人会写呢?

况且,窗口函数在有些数据库中还不支持。那么,就只能用存储过程写循环依次计算每个产品的前10名,与上一次结果做交集。这个过程比用高级语言编写程序并不简单多少,而且仍然要面对临时表的繁琐。

现在,我们知道了SQL的第二个重要缺点:集合化不彻底。虽然SQL有集合概念,但并未把集合作为一种基础数据类型提供,这使得大量集合运算在思维和书写时都需要绕路。

我们在上面的计算中使用了关键字top,事实上关系代数理论中没有这个东西(它可以被别的计算组合出来),这不是SQL的标准写法。

我们来看一下没有top时找前10名会有多困难?

大体思路是这样:找出比自己大的成员个数作为是名次,然后取出名次不超过10的成员,写出的SQL如下:

select sales
from ( select A.sales sales, A.product product,
             (select count(*)+1 from sales_amount
              where A.product=product AND A.amount<=amount) ranking
       from sales_amount A )
where product="AC" AND ranking<=10
复制代码

select sales
from ( select A.sales sales, A.product product, count(*)+1 ranking
       from sales_amount A, sales_amount 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
复制代码

这样的SQL语句,专业程序员写出来也未必容易吧!而仅仅是计算了一个前10名。

退一步讲,即使有top,那也只是使取出前一部分轻松了。如果我们把问题改成取第6至10名,或者找比下一名销售额超过10%的销售员,困难仍然存在。

造成这个现象的原因就是SQL的第三个重要缺点:缺乏有序支持。SQL继承了数学上的无序集合,这直接导致与次序有关的计算相当困难,而可想而知,与次序有关的计算会有多么普遍(诸如比上月、比去年同期、前20%、排名等)。

SQL2003标准中增加的窗口函数提供了一些与次序有关的计算能力,这使得上述某些问题可以有较简单的解法,在一定程度上缓解SQL的这个问题。但窗口函数的使用经常伴随着子查询,而不能让用户直接使用次序访问集合成员,还是会有许多有序运算难以解决。

我们现在想关注一下上面计算出来的“好”销售员的性别比例,即男女各有多少。一般情况下,销售员的性别信息会记在花名册上而不是业绩表上,简化如下:

employee

员工表

name

员工姓名,假定无重名

gender

员工性别

我们已经计算出“好”销售员的名单,比较自然的想法,是用名单到花名册时找出其性别,再计一下数。但在SQL中要跨表获得信息需要用表间连接,这样,接着最初的结果,SQL就会写成:

select employee.gender,count(*)
from employee,
    ( ( select top 10 sales from sales_amount where product="AC" order by amount desc )
    intersect
    ( select top 10 sales from sales_amount where product="TV" order by amount desc ) ) A
where A.sales=employee.name
group by employee.gender
复制代码

仅仅多了一个关联表就会导致如此繁琐,而现实中信息跨表存储的情况相当多,且经常有多层。比如销售员有所在部门,部门有经理,现在我们想知道“好”销售员归哪些经理管,那就要有三个表连接了,想把这个计算中的where和group写清楚实在不是个轻松的活儿了。

这就是我们要说的SQL的第四个重要困难:缺乏对象引用机制,关系代数中对象之间的关系完全靠相同的外键值来维持,这不仅在寻找时效率很低,而且无法将外键指向的记录成员直接当作本记录的属性对待,试想,上面的句子可否被写成这样:

select sales.gender,count(*)
from (…) // …是前面计算“好”销售员的SQL
group by sales.gender
复制代码

显然,这个句子不仅更清晰,同时计算效率也会更高(没有连接计算)。

我们通过一个简单的例子分析了SQL的四个重要困难,这也是SQL难写或要写得很长的主要原因。基于一种计算体系解决业务问题的过程,也就是将业务问题的解法翻译成形式化计算语法的过程(类似小学生解应用题,将题目翻译成形式化的四则运算)。SQL的上述困难会造成问题解法翻译的极大障碍,极端情况就会发生这样一种怪现象:将问题解法形式化成计算语法的难度要远远大于解决问题本身

再打个程序员易于理解的比方,用SQL做数据计算,类似于用汇编语言完成四则运算。我们很容易写出3+5*7这样的算式,但如果用汇编语言(以X86为例),就要写成

    mov ax,3
    mov bx,5
    mul bx,7
    add ax,bx
复制代码

这样的代码无论书写还是阅读都远不如3+5*7了(要是碰到小数就更要命了)。虽然对于熟练的程序员也算不了太大的麻烦,但对于大多数人而言,这种写法还是过于晦涩难懂了,从这个意义上讲,FORTRAN确实是个伟大的发明。

为了理解方便,我们举的例子还是非常简单的任务。现实中的任务要远远比这些例子复杂,过程中会面临诸多大大小小的困难。这个问题多写几行,那个问题多写几行,一个稍复杂的任务写出几百行多层嵌套的SQL也就不奇怪了。而且这个几百行常常是一个语句,由于工程上的原因,SQL又很难调试,这又进一步加剧了复杂查询分析的难度。

更多例子

我们再举几个例子来分别说明这几个方面的问题。

为了让例子中的SQL尽量简捷,这里大量使用了窗口函数,故而采用了对窗口函数支持较好的ORACLE数据库语法,采用其它数据库的语法编写这些SQL一般将会更复杂。 这些问题本身应该也算不上很复杂,都是在日常数据分析中经常会出现的,但已经很难为SQL了。

计算不分步

把复杂的计算分步可以在很大程度地降低问题的难度,反过来,把多步计算汇成一步完成则会提高问题的复杂度。

任务1 销售部的人数,其中北京籍人数,再其中女员工人数?

销售部的人数

select count(*) from employee where department="sales"
复制代码

其中北京籍的人数

select count(*) from employee where department="sales" and native_place="Beijing"
复制代码

再其中的女员工人数

select count (*) from employee
where department="sales" and native_place="Beijing" and gender="female"
复制代码

常规想法:选出销售部人员计数,再在其中找出其中北京籍人员计数,然后再递进地找出女员工计数。每次查询都基于上次已有的结果,不仅书写简单而且效率更高。

但是,SQL的计算不分步,回答下一个问题时无法引用前面的成果,只能把相应的查询条件再抄一遍。

任务2 每个部门挑选一对男女员工组成游戏小组

with A as
       (select name, department,
              row_number() over (partition by department order by 1) seq
        from employee where gender=‘male’)
     B as
        (select name, department,
              row_number() over(partition by department order by 1) seq
        from employee where gender=‘female’)
select name, department from A
where department in ( select distinct department from B ) and seq=1
union all
select name, department from B
where department in (select distinct department from A ) and seq=1
复制代码

计算不分步有时不仅造成书写麻烦和计算低效,甚至可能导致思路严重变形。

这个任务的直观想法:针对每个部门循环,如果该部门有男女员工则各取一名添进结果集中。但SQL不支持这种逐步完成结果集的写法(要用存储过程才能实现此方案),这时必须转变思路为:从每个部门中选出男员工,从每个部门选出女员工,对两个结果集分别选出部门出现在另一个结果集的成员,最后再做并集。

好在还有with子句和窗口函数,否则这个SQL语句简直无法看了。

集合无序

有序计算在批量数据计算中非常普遍(取前3名/第3名、比上期等),但SQL延用了数学上的无序集合概念,有序计算无法直接进行,只能调整思路变换方法。

任务3 公司中年龄居中的员工

select name, birthday
from (select name, birthday, row_number() over (order by birthday) ranking
      from employee )
where ranking=(select floor((count(*)+1)/2) from employee)
复制代码

中位数是个常见的计算,本来只要很简单地在排序后的集合中取出位置居中的成员。但SQL的无序集合机制不提供直接用位置访问成员的机制,必须人为造出一个序号字段,再用条件查询方法将其选出,导致必须采用子查询才能完成。

任务4 某支股票最长连续涨了多少交易日

select max (consecutive_day)
from (select count(*) (consecutive_day
      from (select sum(rise_mark) over(order by trade_date) days_no_gain
            from (select trade_date,
                         case when
                              closing_price>lag(closing_price) over(order by trade_date)
                         then 0 else 1 END rise_mark
                from stock_price) )
     group by days_no_gain)
复制代码

无序的集合也会导致思路变形。

常规的计算连涨日数思路:设定一初始为0的临时变量记录连涨日期,然后和上一日比较,如果未涨则将其清0,涨了再加1,循环结束看该值出现的最大值。

使用SQL时无法描述此过程,需要转换思路,计算从初始日期到当日的累计不涨日数,不涨日数相同者即是连续上涨的交易日,针对其分组即可拆出连续上涨的区间,再求其最大计数。这句SQL读懂已经不易,写出来则更困难了。

集合化不彻底

毫无疑问,集合是批量数据计算的基础。SQL虽然有集合概念,但只限于描述简单的结果集,没有将集合作为一种基本的数据类型以扩大其应用范围。

任务5 公司中与其他人生日相同的员工

select * from employee
where to_char (birthday, ‘MMDD’) in
    ( select to_char(birthday, "MMDD") from employee
      group by to_char(birthday, "MMDD")
      having count(*)>1 )
复制代码

分组的本意是将源集合分拆成的多个子集合,其返回值也应当是这些子集。但SQL无法表示这种“由集合构成的集合”,因而强迫进行下一步针对这些子集的汇总计算而形成常规的结果集。

相关知识

相关内容

725人参加!这场培训倡议“争做齐鲁未来教育家”!

近日,齐鲁名师名校长名班主任建设工程(2022-2025)师德涵养主题培训暨课题开题指导活动在曲阜市举办。培训班以弘扬教育家精神为主题,组织开展了专题讲座引领、浸润式师德涵养现场教学、研修课题开题与指导等培养活动。全国知名教育学家、中国教育···

欢迎台生报名!300个免费名额,直通全球最大AI训练营!

全球最大的AI培训班来了!中国高校人工智能人才国际培养计划昨日在北京大学启动!未来一个多月内将在全国重点计算机高校中筛选100名老师300名学生参加2018年培养计划图灵奖得主John E. Hopcroft深度学习发明人Geoffrey ···

第三期中证隰县基层干部乡村振兴能力培训班在杭举办

中国日报11月15日北京电 2021年11月8日-13日,“第三期中证·隰县基层干部乡村振兴能力培训班”在浙江大学华家池校区开班,本期培训班由中国证券业协会、中国扶贫基金会、隰县人民政府联合举办,此次培训有来自隰县的68名基层干部参加,中国···

京蒙苏豫涉外法律服务专题培训班圆满结束

由北京市司法局、内蒙古自治区司法厅、江苏省司法厅、河南省司法厅及四省区市律师协会共同主办,内蒙古自治区司法厅和律师协会承办的“京蒙苏豫涉外法律服务研讨会暨专题培训班”于11月2日圆满完成所有课程,顺利结束。来自北京、江苏、河南和内蒙古的12···

给校外培训“立规矩” 让“野机构”无处遁形——教育界别省政协委员建言加强校外培训机构管理

连续几日,北京知名舞蹈培训机构——天鹅湖畔少儿芭蕾一夜之间关闭全市门店上了热搜,与之相关的退费难、卷钱跑路等话题也不绝于耳。记者发现,类似的现象,在全国比比皆是。“这位家长,请了解一下我们古筝培训班,今日报名一律5折;街舞团一次缴一年费用,···

京蒙苏豫涉外法律服务专题培训班在满洲里市开班

11月1日,由北京、内蒙古、江苏、河南司法厅(局)及四地律师协会主办的京蒙苏豫涉外法律服务专题培训班在内蒙古自治区满洲里市正式开班。本次培训班是贯彻习近平总书记关于加强涉外法治建设相关指示的重要实践,是响应和落实司法部党组对广大律师提出的“···

第三批北京市优秀社区社会工作专业人才培训试点推进会暨京台社区社会工作专题培训会在京台两地连线举办

2022年7月20日上午,由北京市台办、市社工委市民政局主办的“第三批北京市优秀社区社会工作专业人才培训试点推进会暨京台社区社会工作专题培训会”在京台两地连线开启,台湾地区社会工作专家及北京优秀社会工作者200余人参加。▲张霄林分享参加培···

【预告】就在明天!西山区2023年第二期线上家庭教育培训

西山区2023年第二期线上家庭教育培训家庭教育在未成年人成长发展中具有不可替代的重要作用是促进未成年人健康成长、推进社会主义和谐社会建设的重要基础为在家庭中大力倡导和谐理念、培育和谐精神,提高家长的思想道德素质和科学教育子女的能力,在全社会···

北京冬奥会圆满成功 澳门各界倍感自豪

来源:央视新闻20日晚上,不少澳门市民相约一起收看了北京冬奥会闭幕式的现场直播,共同回味本届冬奥赛事的难忘瞬间。澳门体育教师协会会长 老杰龙:这次冬奥会的成功举办再一次展示了我们国家的伟大和强大,在我们的运动员里面也有很多的突破,例如这一次···

校外培训行业迎来强监管,“教育 区块链”新模式试图解决行业痼疾

华夏时报(www.chinatimes.net.cn)记者 王永菲 冉学东 北京报道近两个月,以校外培训为主要业务的教辅行业迎来密集监管。从诸如高思、跟谁学、学而思等多个头部教育机构被顶格罚款的行政处罚,到教育部成立校外教育培训监管司,校外···

建场地、做培训、打造IP,室内滑雪紧握冬奥接力棒

近年来,室内滑雪市场发展迅速。截至2020年年底,中国室内滑雪场数量为36家,位居全球第一。后冬奥时代,室内滑雪该如何大显身手?2020年室内滑雪场数量超2013年7倍2月20日,北京冬奥会正式闭幕,冰雪运动市场仍在快速发展。据文旅部相关调···

全国公安机关领导干部学习宣传贯彻党的二十大精神政治培训班成功举办

深入学习宣传贯彻党的二十大精神,是当前和今后一个时期全国公安机关的首要政治任务。新时代新征程,如何切实把广大公安民警的思想和行动统一到党的二十大精神上来?如何进一步加强公安机关领导干部队伍政治建设和忠诚教育?公安部党委统筹谋划、孜孜不倦。1···

枫叶教育亏损超31亿;北京首批线上学科培训非营利牌照仍在民政审批中|一周教育要闻

实习记者|陈振芳本周,教育机构方面,“学而思转型的非营利机构未能通过审批。”言论在社交媒体发酵,北京首批线上学科培训非营利牌照仍在民政审批中,尚未获得许可证;“早教第一股”美吉姆重庆两加盟店陷跑路传闻,美吉姆称,预计两周内能够正常上课;苹果···

京保扶贫协作结硕果,北京三年帮河北阜平培训1483名教师

在京保扶贫协作推进中,北京不断加大对保定阜平县的教育资金投入和支持力度,全方位推动阜平教育质量提升。3年来,利用京冀扶贫协作资金,北京已帮助阜平县培训了1483名中小学及幼儿园教师。据了解,阜平职教中心与北京市物业管理行业协会5家会员企业,···

最新!校外培训七大违规行为将被重罚,一图解读

“双减”改革实施两年以来,擅自举办校外培训机构、隐形变异开展校外培训等问题仍不同程度存在,个别机构“卷款跑路”问题仍零星发生,人民群众合法权益仍不时受到损害,为此,教育部组织成立了调研组,先后赴北京、天津、上海、山东、江苏、江西、浙江等实地···

夏宝龙在北京师范大学亲切看望来京学习的香港中小学校长和教师

夏宝龙在北京师范大学亲切看望来京学习的香港中小学校长和教师 主办方供图中新网北京5月18日电(记者 高凯)5月16日上午,国务院港澳事务办公室主任、党组书记夏宝龙来到北京师范大学亲切看望参加“香港中小学校长领导研习班及新入职教师内地学习团联···

白银市举办推进国家公共文化服务体系示范区创新发展暨公共文化高质量发展培训班

为了给全市推进国家公共文化服务体系示范区创新发展工作搭建一个交流学习、开阔视野的高质量平台,7月17日,白银市举办推进国家公共文化服务体系示范区创新发展暨公共文化高质量发展培训班。本次培训班结合白银市推进国家公共文化服务体系示范区创新发展暨···

义务教育阶段线下学科类培训机构压减率超九成

义务教育阶段线下学科类培训机构压减率超九成教育是国之大计、党之大计,教育兴则国家兴,教育强则国家强。“我国有2.9亿在校学生,要坚持把教育这个关乎千家万户和中华民族未来的大事办好。”今年的政府工作报告提出,要促进教育公平与质量提升,继续做好···

非学科类培训行业性增长:门店增加、课程上新、业绩翻红

21世纪经济报道记者王峰北京报道 教育上市公司陆续发布半年报,青少年非学科类校外培训率先复苏,取得行业性增长。21世纪经济报道梳理发现,有的公司营收显著增长,有的公司利润扭亏为盈;有的公司招收人数回暖,有的公司开始扩张教学中心。新东方、好未···

努力当好乡村振兴“领头雁”——全国村党组织书记和村委会主任视频培训班侧记

新华社北京5月15日电 题:努力当好乡村振兴“领头雁”——全国村党组织书记和村委会主任视频培训班侧记新华社记者冯家顺、丁小溪“培训内容丰富实用,抓党建促乡村振兴方向更明、办法更多”“提升领导能力,打造群众信得过的‘铁班子’”“学习先进典型,···