因为工作中需要同时面向MySQL、Oracle和SQLServer三种数据库进行开发,所以,大概从去年国庆节开始,我开始使用一个开源的数据库管理工具——DBeaver。

使用这个工具的初衷,是因为我不想在同一台电脑上安装三个客户端工具,尤其是Oracle和SQLServer这种令人恐惧的、动辄需要重装系统的应用程序。我不想再使用类似Navicat这样的软件,因为它的画风像是上个世纪VB6.0的产品一样,同理,我不喜欢用PL/SQL,因为我每次都要瞪大眼睛,在它狭窄而拥挤的画面上找表、找视图,更有甚者,有时要去找触发器、找存储过程。直到我同事给我发了一个几十M的文档,我突然间意识到,这货居然还要安装Oracle的客户端,配置数据库连接要手动去改配置文件,我一点都不喜欢PL/SQL。

除了这三种经典的关系型数据库,我们还会用Memcache和Redis这样的内存数据库,Mongodb这样的非关系型数据库,所以,我希望有一个统一的入口来管理这些连接,毕竟我身边的同事会使用三种以上的工具,譬如SqlyogPL/SQLSQLServer等来处理这些工作,恰好DBeaver可以满足我80%的工作需要。目前,DBeaver企业版支持关系型数据库和非关系型数据库,而社区版仅支持关系型数据库。

可最近在写Oracle环境的触发器(存储过程和触发器都是万恶之源)时,我发现DBeaver和PL/SQL在面对同一段SQL脚本时,居然因为一点点语法上的差异而不兼容,这让我内心深处不由得想对Oracle吐槽一番。这是一个什么样的SQL脚本呢?我们一起来看下面的例子:

CREATE OR REPLACE TRIGGER "TRI_SYNC_ITEM_VALUE"BEFORE DELETE ON "or_line"FOR EACH ROW
DECLAREv_item_value NUMBER(18,6);
BEGINSELECT ITEM_VALUE INTO v_item_value FROM "order_info" WHERE ORDER_GID = :OLD.ORDER_GID;v_item_value := v_item_value - :OLD.PACKAGE_COUNT * NVL(to_number(:OLD.OL_UDF7),0);IF v_item_value < 0 THENv_item_value:= 0;END IF;UPDATE "order_info" SET ITEM_VALUE = v_item_value WHERE ORDER_GID = :OLD.ORDER_GID;
END "TRI_SYNC_ITEM_VALUE";INSERT INTO "sys_upgrade_history"(UPGRADE_TYPE,VERSION_NO,UPDATE_DATE,REMARK) VALUES('版本更新','10005',SYSDATE,'Normal');

这是实际业务中编写的一个简单触发器脚本,我们通常的编写习惯是,在写完触发器或者存储过程以及函数后,会在升级历史中插入一天新纪录,所以,这个脚本实际上由两部分组成。如果这段脚本分两次执行,那么在DBeaver和PL/SQL中效果是一样的。可如果我们希望一次执行整个脚本,根据PL/SQL的规范,一个PL/SQL脚本由如下结构组成:

DECLARE[声明部分]
BEGIN[过程部分]
END;
/

这个时候,我们就要在这两部分脚本间增加一个分隔符——/。可尴尬的是,这种写法在DBeaver中是无法编译执行的,因为它认为**/**是个无效的SQL关键字。我一直疑心这是个Bug,因为Github上曾有人提过类似的Issue,作者回复说,DBeaver并没有完全实现PL/SQL语法的解析,而最近更新的6.0版本中提到:对Oracle环境的存储过程编译进行了强化。博主尝试升级到最新版本,发现这个问题依然存在,哪怕用Ctrl+Enter来执行一样会报错,于是我想从这件事吐槽下某数据库,从哪里说起呢,就从PL/SQL说起吧!

标准与私货

我想一开始学习SQL语法的时候,大家绝对不会想到,看起来和谐而统一的结构化查询语言,其实是貌合神离。为什么这样说呢?因为我真的不知道,一个时间函数居然可以有SYSDATE、NOW()和GETDATE()三种写法,我更不知道,有一天会因为不知道ROWNUM而被面试官鄙视,更不必说每种数据库都会定义一两种不一样的数据类型,这东西号称是有一个标准吗?比如SQL92/99这个标准定义了DML(数据操作语言)、DDL(数据定义语言)、DCL(数据控制语言)和TCL(事务控制语言)四种分类,所以,SQL的定位其实更接近于交互式命令行,它是命令式的查询语言,而非过程式的声明语言。

可在标准化进程缓慢的大背景下,每一家数据库厂商都在往自家产品里夹藏私货,以甲骨文为首的Oracle发展出了PL/SQL、以微软为首的SQLServer发展出了T-SQL。其实,我很能理解这种标准跟不上时代发展需要的阵痛,就像我们的Web领域直到10年前后才提出了HTML5标准,在此之前,我们为不同的浏览器的兼容性煞费苦心,兼容IE8与否甚至成为了评价技术好坏的一个隐性标准,可说句实话,浏览器的Bug难道不应该让浏览器厂商来修复吗?关前端工程师什么事?同样的,数据库间的差异,让我们的脚本失去了可移植性,触发器、存储过程这种严重依赖数据库的东西,一旦更换了数据库,基本等于要重头再写一遍,如今的小程序让Web变成信息孤岛,甚至Chrome正在变成下一个IE,这就是所谓“屠龙少年战胜恶龙,自身亦化为恶龙吗”?

这种不统一带来的弊端就是,我们永远写出可以完美“跨”数据库的SQL,现在跨平台基本成为了大家的共识,因为操作系统间的差异越来越小,以我个人为例,我使用的大多数软件都可以找到对应的Linux版本,这样做的好处是,我可以在无差别地从Windows切换到Linux。可现在,我们必须在MySQL里使用VARCHAR、而在Oracle里使用NVARCHAR,而在SQLServer里又要使用NVARCHAR2,可明明它们都是表示一样的东西啊,类似的还有MediumText和CLOB,是不是起一个不一样的名字会显得与众不同呢?更不必说在DDL中表约束相关的语法存在差别了。我被告知Oracle脚本中表名要用双引号括起来,理由是Oracle区分大小写,加上双引号就可以让它忽略大小写,忽略大小写不应该给Oracle一个设置吗?为什么要让我再写个多余的双引号呢?诸如此类,举不胜举。

SQL是个好DSL吗?

SQL标准定义的SQL,就是一个以集合论为基础的结构化查询语言,它天生适合的场景就是,你在命令行中输入SQL语句,然后它去执行你输入的SQL语句,它就像我们大多数情况下使用的交互式命令行,不然,为什么MySQL要提供命令行版本,主流的数据库管理工具都提供了输入SQL语句的窗口。可我们同样能意识到,SQL的表达能力有限,它无法表达顺序、条件、循环这种基本的程序结构,所以,数据库厂商几乎都对SQL标准进行了扩展,像PL/SQL和T-SQL中都提供了这些语法,进而催生出函数、触发器、存储过程一系列“万恶之源”,可从编程语言的角度来看,SQL算是个好DSL吗?

SQL试图从编程语言中获得“灵感”的思路是正确的,但总给人一种买椟还珠的感觉,譬如使用大量的英文关键字来作为保留关键字,可你很难想象,像GROUP BY和ORDER BY这样的关键字,居然可以保留中间一个甚至多个空格,既然是关键字,为什么不选择一个单词,而选择一个组合词呢?这个世界上用Begin和End的编程语言,我使用过的有Pascal和Basic,但现在我几乎不会再用它们,为什么呢?因为使用花括号({})更符合这个世界的发展趋势,你看Python居然用缩进代替花括号,是打算时刻用游标卡尺写代码吗?

全世界都默认用分号作为一个语句的结束,那么,当多个语句放在一起的时候,直接相互间用分号隔开,编译器或者解释器都能识别,就算不喜欢写分号的JavaScript,最新的标准提案里不还是建议要写吗?可为什么到了PL/SQL这里,明明已经用分号作为结束符了,偏偏还要再用一个/作为分隔符。我们都知道/会被当做是注释的开始,那么如果我在PL/SQL里恰好在End;后写上一句/,你告诉我,这到底代表什么意思?明明像&&、||、^等这样的运算符,都是有固定含义,并且大家所有编程需要都默认了这个原则,可偏偏有人用||来连接字符串,你告诉我,用+不好吗?就像从小到大,÷都会被认为表示一个除法运算,结果突然有一天,有人用这个符号来表示加法运算,你说你是不是有种被当做傻子的感觉。全世界都用=表示赋值运算,结果PL/SQL自作聪明地搞了个:=,我想说,你真的考虑过使用者的体验吗?

你甚至连分页、排序、分组这种事情,都无法在不同的数据库上获得一致的书写体验,读取指定数目的数据库记录,居然要纠结用到底用Limit还是Top,像Select Into这样把指定列存储到指定变量中的操作,居然要求使用者来限制结果集的数目,从函数的角度来看,返回的必然是结果集中的一个元素,只有这样才可以赋值给指定的变量,可问题是存在多条记录的时候,你必须用游标去循环读取,而不能像大多数编程语言一样,直接Map()到一个类型上然后ToList(),可能是我对SQL的要求太高了吧,毕竟它就是个面向过程的语言,OO不OO的没那么重要,可明明你可以抛出异常啊,可以对字符串做截取啊正则啊,可以在控制台里输出日志啊,可以调用各种有的没的的内部函数啊,elsif可能是因为e不发音,就像usr绝对不是拼写错误……

Python的缩进虽然为人所不齿,但它至少和大部分编程语言一样,单独一行的程序语句和由多行程序组成的程序块之间,并不需要明显的分割符号。可MySQL需要用DELIMITER $$这种奇怪的符号,PL/SQL需要用/这种奇怪的符号,SQLServer需要用@这种奇怪的符号,还有大名鼎鼎的虚拟表DUAL。也许这些东西写多了就可以记住,就像我现在可以分清SYSDATE、NOW()和GETDATE(),可它带来的问题是什么呢,大多数的触发器、存储过程、函数都是没有移植性可言的,很多年前,我们讲设计模式,最喜欢觉的例子就是,如果项目发生变动,需要更换数据库,我们要怎么设计能不改动代码,现在看起来,当时还是太天真了,真要换了数据库,估计就是重新做了,敢把全部业务写到数据库里,Web就做一个展示层的项目,有生之年应该是不会换数据库啦!

多元与统一

这个世界的离奇之处在于,人们一边渴望在标准的庇护下幸福生活,又一边渴望可以超脱标准去发展独立的个性,如你我所见,多元与统一,构成了这个世界永恒的旋律,或许是因为那句名言——没有永远的敌人,只有永远的利益。可对比Web的标准化与SQL的标准化,我们却看到了截然不同的场景,虽然Chrome浏览器市场份额的不断提高,加上微软、Mozilla等“浏览器巨头”一起推动,HTML5和CSS4,让大量的工作得到了简化,尤其像WebSocket、Drag&drop、Canvas等API的推出,这带来的好处是什么呢?大家不再去重点关注浏览器的兼容性问题,各种天花乱坠的炫酷特效不再通过JavaScript去控制。一个标准的API + 一个支持降级的profily,基本就可以覆盖到主流的浏览器,就算有小程序这种偏离标准的解决方案,回顾近几年整个前端领域的趋势,可以说,一切都在向着好的方向发展。

可数据库领域发生了什么,依稀记得甲骨文和Google因为Android使用了Java而官司连连,Google不得不推出一种新的基于JVM的语言——Kotlin;依稀记得甲骨文在开源社区的强烈反对下收购了MySQL,社区不得不继续维护MySQL的开源分支——MariaDB。从这两件事情,我完全提不起对甲骨文这家公司的好感,虽然大家都说Oracle品质卓越,可实际使用下来,经常出问题的Oracle。从LAMP时代开始,MySQL就以其免费、轻量的特点广泛应用在互联网产品中,直至今天有大量的云产品使用着MySQL,而Oracle和SQLServer则被更多地使用在私有部署的场景中。虽然,我承认把数据掌握在自己手里会放心些,可当你没有能力去维护这些东西时,付出的时间和精力远远要比这多。甲骨文收购了那么多公司的产品,时至今日,对整个行业的标准化有什么推动呢?Oracle数据库依然难装、难用,PL/SQL同样难用得要命,可我们这世界一直都很奇怪,最流行的偏偏未必是最好的,据说Oracle的代码写得非常差,开发人员表示不会在为它继续开发新功能。

可能有时候,我们完全说不出来,一件东西是好还是坏,就像JavaScript能在前端开发流行,是因为没有其它的选择,你说这门语言没有缺点吗?当然有,JavaScript里各种“骚操作”和“黑科技”,甚至吐槽三天三夜都说不完。同样,还有Python这门语言,大家都觉得它的解释器慢腾腾的,动态语言遇上大型项目简直就是火葬场,还有神来之笔—— 通过缩进来代替花括号。我最终还是在PL/SQL里执行了我的脚本,只要我在使用DBeaver 的时候,人肉地区分/前后的SQL语句就可以了。果然,我骨子里还是一个不喜欢写SQL脚本的人,因为我认为这么别扭的东西简直不能称之为脚本,你看看Lua,再看看Python,有哪一门脚本语言有SQL脚本这样别扭呢?数据库对我而言,就是一个存取数据的“潘多拉魔盒”,索引啊,触发器啊,数据库任务啊,执行计划啊,存储过程啊,难道不属于暴露了太多细节给用户吗?我天天用这个数据库,我每天用哪些表,我每天用哪些字段,你作为一个成熟的数据库了,居然不能自己去解决这些问题,我对你很失望啊,请记住,程序员比任何人都喜欢偷懒。

由DBeaver与PL/SQL引发的数据库吐槽相关推荐

  1. PL/SQL连接oracle数据库

    PL/SQL连接oracle数据库 1.简单介绍 在不安装oracle数据库的情况下使用pl/sql连接远程oracle数据库. 2.详细步骤: a)      安装PL/SQL.依据自己的操作系统安 ...

  2. windows 客户端的Navicat PL/SQL 连接Oracle 数据库

    PL/SQL 连接Oracle 数据库 1.下载instantclient_11_2文件 官网可以下载 2.本地的监听文件 D:\Program Files\instantclient_11_2\ne ...

  3. oracle sql 导入mysql数据库备份_使用PL/SQL连接oracle数据库,并将数据进行导出备份和导入恢复...

    使用PL/SQL连接oracle数据库,并将数据进行导出备份和导入恢复 这种操作百度一搜一大片,今天整理以前做的项目时自己备份了一下数据库,试着将数据进行导出备份和导入恢复了一下:下面是操作过程: 1 ...

  4. PL/SQL登录Oracle数据库提示“无监听程序”解决办法

    PL/SQL登录Oracle数据库提示"ORA-12541:TNS:无监听程序" 解决办法: 1.打开Oracle Net Manager 监听下的listener主机地址改成主机 ...

  5. oracle数据库报错12154,PL/SQL登录Oracle数据库报错ORA-12154:TNS:无法解析指定的连接标识符解决方法...

    本篇文章小编给大家分享一下PL/SQL登录Oracle数据库报错ORA-12154:TNS:无法解析指定的连接标识符解决方法,小编觉得挺不错的,现在分享给大家供大家参考,有需要的小伙伴们可以来看看. ...

  6. oracle 作业调度里pl/sql怎么写,PL/SQL实现Oracle数据库任务调度

    PL/SQL实现Oracle数据库任务调度 PL/SQL实现Oracle数据库任务调度 正在看的ORACLE教程是:PL/SQL实现Oracle数据库任务调度.摘要:本文主要就数据库恢复与系统任务的调 ...

  7. pl/sql连接Oracle数据库的方式

    第一种:直连方式(适用于比较紧急情况下) 打开pl/sql后会出现如下图所示的登陆框,其中用户名和口令分别对应着账号.密码. 采用直连方式时,在数据库这个位置可填写类似"19.34.48.1 ...

  8. pl sql mysql 版本_老版本PL/SQL Developer操作数据库导致ORA-00600[17113]

    在巡检某运营商的计费库时,发现alert日志中发现如下错误 Thu Feb 2 13:54:52 2012 Errors in file /oracle9/app/admin/bill/udump/b ...

  9. oracle数据库12154,PL/SQL登录Oracle数据库报错ORA-12154:TNS:无法解析指定的连接标识符已解决(本地未安装Oracle需要连接服务器上的)...

    ORA-12154:TNS:无法解析指定的连接标识符 ORA-12154:TNS:无监听程序 错误分析一.PL/SQL 客户端登录到数据库,如果配置错误会有以上错误,如下图. 这个错误主要是pl/sq ...

最新文章

  1. unity,standalone下自定义分辨率不起作用的解法
  2. UA MATH566 统计理论5 假设检验简介
  3. python太阳花绘制
  4. android dropbox anr分析,Android如何分析排查ANR
  5. Android TensorFlow Lite 深度学习识别手写数字mnist demo
  6. JS常用的设计模式(2)——简单工厂模式
  7. django 获取环境变量_python – django settings.py os.environ.get(“X”)没有获取正确的值...
  8. 机构报告:大数据分析提升企业决策水平
  9. js excel 矫正
  10. linux下安装配置laravel环境,linux下的laravel安装
  11. marqueeview更改字体颜色_安卓手机上可以编辑字体的便签软件哪个好?
  12. 《嵌入式 – GD32开发实战指南》第8章 PWM输出
  13. 代码与国家地区对照表
  14. flutter 如何判断页面渲染完毕
  15. 计算机二级web知识点,计算机二级WEB考试主要考哪些内容?
  16. spinningup环境搭建
  17. hdu 2197 本原串 思维
  18. dnf内存教学视频教程分析讲解
  19. 英雄传说空之轨迹人物介绍
  20. 2022年湖北潜江助理工程师职称评审申报流程及时间是什么时候?甘建二

热门文章

  1. 因为计算机中丢失d3dx926.dll,win10系统打开程序提示丢失d3dx9 26.dll的解决方法
  2. linux下 不显示光驱,Windows7电脑下不显示光驱盘符的解决方法
  3. WIFI学习一(socket介绍)
  4. /usr/local/go/src/net/cgo_linux.go:12:8: no such package located
  5. Linux安装Intel无线网卡(Ubuntu 16.04)
  6. 计算机开机怎么设置网络连接,电脑怎么设置开机自动连接宽带
  7. 数字逻辑复习(Wust)
  8. 运算放大器电路PCB的设计技巧
  9. @Resource()注解报红
  10. “科林明伦杯”哈尔滨理工大学第十届程序设计竞赛——H.直线【JAVA大数 | Python】