SQL(Structured Query Language)是一种用于管理关系数据库的标准语言。为了确保不同数据库管理系统之间的兼容性和可移植性,ISO/IEC和ANSI制定了SQL标准。这些标准定义了SQL语言的核心功能以及一些可选的功能扩展。比较重要的SQL标准有SQL-92、SQL-99、SQL-2011等。
尽管SQL有标准,但是市面上的数据库采用的SQL语法仍存在一些差异。熟悉不同数据库SQL语法差异有助于我们更好更快地使用DM8,可以写出兼容性健壮性更好的SQL。本文记录了一些基础SQL语法需要注意的地方,主要例句均通过MySQL 9.0和DM8测试。
单行注释:
--: PostgreSQL 等支持以两个连字符开始的单行注释。
#: MySQL 和 MariaDB 支持以井号开始的单行注释。
注意:在MySQL中如果要使用--做注释,--后必须加入半角空格,否则不被视为注释。
多行注释:
/* ... /: 这种格式被广泛支持于MySQL, PostgreSQL, SQL Server, Oracle, SQLite 等多个DBMS中,用于包裹一段多行的注释内容。
DM8中支持 -- 做单行注释,也支持//做单行注释,支持/... */做多行注释。DM8中不支持#开始注释。
分号 ( ; ) : 在大多数SQL实现中,如MySQL, PostgreSQL, SQL Server, Oracle等,使用分号作为SQL语句的结束标志。
斜杠加换行 (/后跟回车): 在Oracle的SQL*Plus命令行工具中,除了分号外,还可以使用斜杠加换行来表示SQL语句或PL/SQL块的结束。
例如:
SELECT 1 FROM dual;
/
这里,/紧跟在查询语句之后,并且在其后按下回车键,这会告诉SQL*Plus你已经完成了一条或多条命令的输入,可以开始执行这些命令了。
如果是在多行的PL/SQL匿名块中,情况也是类似的:
BEGIN
DBMS_OUTPUT.PUT_LINE('Hello, World!');
END;
/
在这个例子中,/放在END;之后,用来指示整个PL/SQL块的结束。然后你需要按下回车键来提交这个块给SQL*Plus去执行。
DM8中对于上述用法都支持。值得注意的是,大部分数据库(包括DM8),尽管对于单条SQL语句不是必须使用分号来结束(例如单步执行“SELECT 1”没有分号结束符也是可以执行成功),但加上分号肯定没有坏处。所以还是建议单条SQL语句都使用分号结束。
反引号 () 或双引号 (""): 在某些数据库系统中,为了允许保留字作为标识符(例如表名、列名),可以使用特殊字符来包围这些标识符。 MySQL 使用反引号
identifier。 SELECT
EMPLOYEE_IDFROM
EMPLOYEE` ;
PostgreSQL 和 Oracle 可以使用双引号 "identifier" 来引用包含特殊字符或保留字的标识符。
DM8中,使用双引号作为引用的标识符。
select "JOB_ID" from "DMHR"."JOB" ;
很多数据库都支持create database,例如可在MySQL中执行:
CREATE DATABASE SHOP;
USE SHOP;
这样就创建了一个库”SHOP”,并切换工作区到这个库。
DM8的概念有所不同。DM8是一个实例一个库,但是一个库可以包含多个用户,一个用户可以包含多个模式。相对应的此处可以使用crerate schema 语法:
CREATE SCHEMA SHOP;
SET SCHEMA SHOP ;
建表语法中需要注意的是命名规则。例如MySQL中可以执行下列建表语句:
create table 2009sale (id int);
但上述建表语句无法在DM8中执行成功,原因是表名应该以英文字母开头。
关于命名规则,各个数据库可能稍有差异,但是实践中最好遵循下列规则以增强健壮性:只使用半角英文字母、数字、下划线作为数据库、表和列的名称(不得使用-连字符如Product-ID,可以使用下划线如Product_ID),名称应该以英文字母开头,名称不能重复。
在表定义更新时, MySQL一般采用如下写法:
#在表employee中增加/删除列 TENURE
alter table employee add column TENURE int;
alter table employee drop column TENURE ;
而在Oracle和SQL SERVER中不用写COLUMN,Oracle中写法如下:
alter table employee add (TENURE int);
alter table employee drop TENURE;
DM8对于上述语法均支持。
有意思的是,大部分数据库均可以支持SELECT子句不带FROM,例如:
Select 1;
但是Oracle不支持此种写法,Oracle的SELECT子句必须带FROM,可写成:
Select 1 from dual;
不管是MySQL还是DM8上面这两种写法都是支持的。
我构造了一个例子如下:
--表数据
create table TNUM (id int , num int );
insert into TNUM values (1,1),(2,2),(3,3),(4,null),(5,null);
在dm8中查询及结果如下:
--查询
select count(*) from (select * from tnum where num in (2,1,null));--执行结果为2
select count(*) from (select * from tnum where num in (2,1) or num is null);--执行结果为4
select count(*) from (select * from tnum where num not in (2,1,null));--执行结果为0
select count(*) from (select * from tnum where num not in (2,1));--执行结果为1
可以看出,select … in (… , null)其中的结果并不包含(4,null),(5,null)这样的值,可以理解为null并不能在in中做等值比较。如果要查出包含null的值,应该用 is null。
反过来,对于select …not in (2,1),这个执行结果中只包含了(3,3)而不包含(4,null),(5,null),即null值是不确定值,null值不能做等值比较。如果使用select …not in (… , null),因为null值是不确定值,null值不能做等值比较,所以这里not in(…,null)的结果始终为空。
对于同样的例子,在Mysql中的执行结果如何呢:
select count(*) as mananum from (select * from TNUM where num in (2,1,null)) as s1;#执行结果为2
select count(*) as mananum from (select * from TNUM where num in (2,1) or num is null) as s2;#执行结果为4
select count(*) as mananum from (select * from TNUM where num not in (2,1,null)) as s3;#执行结果为0
select count(*) as mananum from (select * from TNUM where num not in (2,1)) as s4;#执行结果为1
可以看出执行结果是和dm8一致的。
不过这里需要注意到MySQL中多了一个强制要求:每个派生表(即子查询)都需要有一个别名。所以如果使用之前不使用别名的查询SQL会报错:
Error Code: 1248. Every derived table must have its own alias。
我们构造一个只有一列的表,包含null值做count:
create table TNUM2 ( num int );
insert into TNUM2 values (1),(2),(3),(null),(null);
select count(*) as s1 , count(num) as s2 from TNUM2;
执行结果是s1的值是5,而s2的值是3。
上述例子在dm8和MySQL中的执行结果一致。
需要注意到:count函数的结果根据参数的不同而不同。Count(*)会得到包含null的数据行数,而count(<列名>)会得到null之外的数据行数。
更进一步:所有的聚合函数,如果以列名为参数,那么在计算之前就已经把NULL排除在外了。因此无论有多少个NULL都会被无视。这与“等价为0”并不相同。例如sum函数、avg函数等等均可以看出是这样的结果。
考虑前面的TNUM2表,做如下查询:
select num,count(num),count(*) from TNUM group by num;
执行结果如下:
上述的结果在DM8和MySQL中没有差别。
从上面的例子可以看出++当聚合键中包含NULL时,也会将NULL作为一组特定的数据。++同时,分组后NULL组中,count(*)会统计包含null的数据行数,而count(<列名>)会得到null之外的数据行数(这个例子里NULL组本身就只有NULL不会有其他值)。
考虑前面的TNUM表查询结果排序:
select * from TNUM order by 2,1 desc;
执行结果:
上述结果在DM8和MySQL中执行结果一致。
考虑另一个查询:
select * from TNUM order by 2 desc, 1 ;
执行结果:
DM8:
而在MySQL中:
上述结果在DM8和MySQL中不一致。
这里面有两点值得注意:1.null参与排序,且默认的排序结果在不同数据库可能不同。这个例子里面,dm8默认排序不管是升序还是降序都将null排在最前面;而MySQL中默认视null为最小值,升序时null排在前面,降序时null排在最后面。
2.dm8中支持使用nulls last或者nulls first显式地指定null排在前面或者后面,而MySQL一般使用if(isnull(<列名>),0,1)或if(isnull(<列名>),1,0)函数。
例如可以在dm8执行下列SQL:
select * from TNUM order by 2 desc nulls last, 1 ;
但是在MySQL的某些版本中不支持nulls first 或者nulls last语法。例如在MySQL 9.0中执行:
select * from TNUM order by num desc nulls first ;
会报错:
Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'nulls last' at line 1
相应的,上述SQL在MySQL中可以使用if(isnull(<列名>),0,1)函数。
select * from TNUM order by if(isnull(num),0,1), num desc ; -- 相当于nulls first
select * from TNUM order by if(isnull(num),1,0), num desc ;-- 相当于nulls last
3.在order by子句中使用列编号尽管在语法上没有问题,但是并不推荐使用,因为使用列编号会使阅读变得困难,同时在SQL-92中指出该排序功能将来会被删除,为了程序的可读性和健壮性,建议order by子句中不要使用列编号。
大部分数据库都支持集合运算,如union / intersect /except。
值得注意的是,MySQL 从版本 8.0.31 开始支持 INTERSECT 和 EXCEPT 集合操作符。在此之前,MySQL 并不直接支持这些 SQL 标准的集合操作符,用户需要通过其他方法来模拟这些操作,例如使用INNER JOIN 或者 IN 子句。
对于Oracle,其不支持except,而是使用minus关键字来实现相同的功能。
对于DM8,既支持union / intersect /except ,也支持minus关键字。
结语:总体来说DM8的兼容性还是非常好的。有的语法Oracle支持而MySQL不支持,或者MySQL支持而Oracle不支持,大部分情况下DM8做到了两种都支持。这是国产数据库为强化兼容性、降低用户使用门槛、降低用户迁移成本做出的巨大努力。
文章
阅读量
获赞