暂无图片
暂无图片
5
暂无图片
暂无图片
暂无图片

Oracle和Mysql在sql上的一些差异

1203

一、说明

平常我用到最多的数据库是Oracle和Mysql,本文将介绍这两个数据库在sql上的一些差异。
测试环境数据库的版本为Oracle 11G,Mysql 8.0。

二、实验

1. 字符类型大小写

Oracle和Mysql最典型的区别在于是否区分字符大小写。Oracle默认区分大小写,Mysql默认不区分大小写。因此这会影响字符串的选择和排序。从下面的例子可以看出‘B’和‘b’在Oracle中不同,在Mysql中相同。
Oracle:

create table stt1(id int, name varchar2(10));
insert into stt1 values (1, 'a');
insert into stt1 values (2, 'b');
insert into stt1 values (3, 'B');
insert into stt1 values (4, 'c');

SQL> select * from stt1 order by name;

        ID NAME
---------- ----------
         3 B
         1 a
         2 b
         4 c
         
SQL> select * from stt1 where name = 'b';

        ID NAME
---------- ----------
         2 b

Mysql:

create table stt1(id int, name varchar(10));
insert into stt1 values (1, 'a'),(2, 'b'),(3, 'B'),(4, 'c');

(root@localhost)[hello]> select * from stt1 order by name;
+------+------+
| id   | name |
+------+------+
|    1 | a    |
|    2 | b    |
|    3 | B    |
|    4 | c    |
+------+------+

(root@localhost)[hello]> select * from stt1 where name = 'b';
+------+------+
| id   | name |
+------+------+
|    2 | b    |
|    3 | B    |
+------+------+

Mysql也可以区分字符大小写,使用的排序字符集是utf8mb4_bin,这样排序和过滤就和Oracle一样了

create table stt2(id int, name varchar(10)) collate utf8mb4_bin;
insert into stt2 values (1, 'a'),(2, 'b'),(3, 'B'),(4, 'c');
            
(root@localhost)[hello]> select * from stt2 order by name;
+------+------+
| id   | name |
+------+------+
|    3 | B    |
|    1 | a    |
|    2 | b    |
|    4 | c    |
+------+------+

(root@localhost)[hello]> select * from stt2 where name = 'b';
+------+------+
| id   | name |
+------+------+
|    2 | b    |
+------+------+

那么问题就来了,Mysql中字符串大小写敏感和不敏感的表关联会出现什么现象呢?

(root@localhost)[hello]> select a.*, b.* from stt1 a, stt2 b where a.name = b.name;
+------+------+------+------+
| id   | name | id   | name |
+------+------+------+------+
|    1 | a    |    1 | a    |
|    2 | b    |    2 | b    |
|    3 | B    |    3 | B    |
|    4 | c    |    4 | c    |
+------+------+------+------+

可以看到两表关联,它们的name完全一样,区分了大小写,即如果表与表之间关联,有一张表的字符区分大小写,那么整体的关联条件都是区分大小写的。接下来复制一张stt1表。看看两张表都不区分大小写关联是个什么情况。

create table stt3 as select * from stt1;
      
(root@localhost)[hello]> select a.*, b.* from stt1 a, stt3 b where a.name = b.name;
+------+------+------+------+
| id   | name | id   | name |
+------+------+------+------+
|    1 | a    |    1 | a    |
|    3 | B    |    2 | b    |
|    2 | b    |    2 | b    |
|    3 | B    |    3 | B    |
|    2 | b    |    3 | B    |
|    4 | c    |    4 | c    |
+------+------+------+------+

2. char与varchar关联

在这两个数据库中都提供了定长char和不定长varchar(在Oracle中是varchar2,为了简便,统一称为varchar)这两个字符类型,那么char跟varchar之间关联会是个什么情况呢?
Oracle:

create table ctt10(id int, name char(10));
create table ctt12(id int, name char(12));
create table vtt10(id int, name varchar2(10));
create table vtt12(id int, name varchar2(12));
insert into ctt10 values(1, '20221230');
insert into ctt12 values(2, '20221230');
insert into vtt10 values(3, '20221230');
insert into vtt12 values(4, '20221230');

Oracle中char(10)与char(12),varchar(10)与varchar(12)关联,即同类型不同字段长度,都能取到结果

SQL> select a.*, b.* from ctt10 a, ctt12 b where a.name = b.name;

        ID NAME               ID NAME
---------- ---------- ---------- ------------
         1 20221230            2 20221230

SQL> select a.*, b.* from vtt10 a, vtt12 b where a.name = b.name;

        ID NAME               ID NAME
---------- ---------- ---------- ------------
         3 20221230            4 20221230

Oracle中char(10)与varchar(10),char(10)与varchar(12)关联,即不同类型,不管字段长度是否一样,都不能取到结果

SQL> select a.*, b.* from ctt10 a, vtt10 b where a.name = b.name;

no rows selected

SQL> select a.*, b.* from ctt10 a, vtt12 b where a.name = b.name;

no rows selected

Mysql:

create table ctt10(id int, name char(10));
create table ctt12(id int, name char(12));
create table vtt10(id int, name varchar(10));
create table vtt12(id int, name varchar(12));
insert into ctt10 values(1, '20221230');
insert into ctt12 values(2, '20221230');
insert into vtt10 values(3, '20221230');
insert into vtt12 values(4, '20221230');

(root@localhost)[hello]> select a.*, b.* from ctt10 a, ctt12 b where a.name = b.name;
(root@localhost)[hello]> select a.*, b.* from vtt10 a, vtt12 b where a.name = b.name;
(root@localhost)[hello]> select a.*, b.* from ctt10 a, vtt10 b where a.name = b.name;
(root@localhost)[hello]> select a.*, b.* from ctt10 a, vtt12 b where a.name = b.name;

在Mysql中,以上4条sql语句都能取到结果,为了节约篇幅,这里将结果省略。可以看出在Mysql中其实不用在意varchar和char之间的关联情况,但在Oracle中需要注意varchar和char之间关联的空格问题。可以采用以下两种写法。

select a.*, b.* from ctt10 a, vtt10 b where trim(a.name) = b.name; -- 去掉char的空格
select a.*, b.* from ctt10 a, vtt10 b where a.name = rpad(b.name, 10, ' '); -- varchar后面填充空格

在Oracle中还有另外一种情况就是在varchar中插入空格会是什么情况?

insert into vtt12 values(5, '20221230 ');

SQL> select a.*, b.* from vtt12 a, vtt12 b where a.name = b.name;     

        ID NAME                 ID NAME
---------- ------------ ---------- ------------
         4 20221230              4 20221230
         5 20221230              5 20221230 

可以看到‘20221230 ’和‘20221230’能关联上。但使用以下sql又能明显看出这两个值的不同。

SQL>  select id, name || 'xx' from vtt12;

        ID NAME||'XX'
---------- --------------
         4 20221230xx
         5 20221230 xx

3. 隐式转换

在sql中,隐式转换总是从字符串转数字。
Oracle:

create table tra1(id int, name varchar2(10));
insert into tra1 values(1, '5abc');
insert into tra1 values(2, 'def5');
insert into tra1 values(3, '5');

SQL> select * from tra1 where id = '3';

        ID NAME
---------- ----------
         3 5

SQL> select * from tra1 where id = '3abc';
select * from tra1 where id = '3abc'
                              *
ERROR at line 1:
ORA-01722: invalid number

SQL> select * from tra1 where name = 5;
select * from tra1 where name = 5
                         *
ERROR at line 1:
ORA-01722: invalid number

SQL> select * from tra1 where name = '5';

        ID NAME
---------- ----------
         3 5

Mysql:

create table tra1(id int, name varchar(10));
insert into tra1 values(1, '5abc');
insert into tra1 values(2, 'def5');
insert into tra1 values(3, '5');

(root@localhost)[hello]> select * from tra1 where id = '3';
+------+------+
| id   | name |
+------+------+
|    3 | 5    |
+------+------+

(root@localhost)[hello]> select * from tra1 where id = '3abc';
+------+------+
| id   | name |
+------+------+
|    3 | 5    |
+------+------+

(root@localhost)[hello]> select * from tra1 where name = 5;
+------+------+
| id   | name |
+------+------+
|    1 | 5abc |
|    3 | 5    |
+------+------+

(root@localhost)[hello]> select * from tra1 where name = '5';
+------+------+
| id   | name |
+------+------+
|    3 | 5    |
+------+------+

可以对比看出在Oracle中字符串转数字,碰到不能转的,例如‘5abc’就直接报错。但是在Mysql中,‘5abc’在隐式转换成数字的时候,会转成5,即字符串开头的数字部分。

4. update语句

在Mysql中执行update语句

(root@localhost)[hello]> select * from stt1;
+------+------+
| id   | name |
+------+------+
|    1 | a    |
|    2 | b    |
|    3 | B    |
|    4 | c    |
+------+------+

我想将id为1的这条数据的id改成5,并且name改成‘A’,sql语句如下

(root@localhost)[hello]> update stt1 set id=5 and name='A' where id =1;
Query OK, 0 rows affected (0.01 sec)
Rows matched: 1  Changed: 0  Warnings: 0

(root@localhost)[hello]> select * from stt1;
+------+------+
| id   | name |
+------+------+
|    1 | a    |
|    2 | b    |
|    3 | B    |
|    4 | c    |
+------+------+

可以看到匹配上了,但是数据没改。细心的小伙伴发现是什么问题了吗?之前我碰到一个业务老师拿这个问题问我,一下子把我问住了,大脑卡了很久都没想明白是什么一回事。
其实正常的update语句应该是这样的,update stt1 set id=5, name=‘A’ where id =1; 更改两个值应该是用逗号隔开而不是用and。用and最后呈现出来的sql语句其实等价于update stt1 set id=(5 and name=‘A’) where id =1; (5 and name=‘A’)刚好就是1,所以id的值就没有变。
这个语句在Oracle中执行会直接语法报错。

5. 生成自增的行

自增的行在数据库中作用挺大,很多地方都用得到。在Oracle中生成自增行可以采用connect by,例如

SQL> select rownum rn from dual connect by rownum < 10;

        RN
----------
         1
         2
         3
         4
         5
         6
         7
         8
         9

在Mysql中可以用递归函数

(root@localhost)[hello]> with recursive cte(n) as (
    ->     select 1
    ->     union all
    ->     select n + 1 from cte where n < 9)
    -> select n rn from cte;
+------+
| rn   |
+------+
|    1 |
|    2 |
|    3 |
|    4 |
|    5 |
|    6 |
|    7 |
|    8 |
|    9 |
+------+

三、总结

工作中经常会交叉使用这两种数据库,这两个数据库在一些sql细节上面有着不少的差异,需要我们不断测试和积累。
今天是2022年最后的一个工作日,这一年有着太多不可思议,好的坏的也都过去了。感恩还有一个健康的身体,还有一个稳定的工作。有幸能参与墨天轮举办的2022年度优秀原创作者评选,加上本文,这一年一共写了12篇文章,正好一个月一篇。即使工作再忙,也希望自己能不断的写下去。期待大家给我投票,最后提前祝大家元旦快乐。

最后修改时间:2023-01-03 10:16:41
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
1人已赞赏
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论