MySQL与标准SQL的区别

我们试图使MySQL Server遵循ANSI SQL标准和ODBC SQL标准,但MySQL Server在某些情况下执行不同的操作:

MySQL和标准SQL特权系统之间有一些区别。例如,在MySQL中,删除表时不会自动撤销表的特权。您必须显式发出REVOKE来撤销表的特权。

CASTCAST()函数不支持强制转换为REAL或BIGINT。

SELECT INTO TABLE 语法差异

MySQL服务器不支持SELECT ... INTO TABLE Sybase数据库SQL扩展。相反,MySQL服务器支持INSERT INTO ... SELECT标准SQL语法,这基本上是一样的。例如:

sql 复制代码
INSERT INTO tbl_temp2 (fld_id)
    SELECT tbl_temp1.fld_order_id
    FROM tbl_temp1 WHERE tbl_temp1.fld_order_id > 100;

或者,您可以使用SELECT ... INTO OUTFILE或CREATE TABLE ... SELECT。

您可以将SELECT ... INTO与用户定义的变量一起使用。同样的语法也可以在使用游标和局部变量的存储过程中使用。

UPDATE 语法差异

如果在表达式中访问要更新的表中的列,UPDATE将使用该列的当前值。以下语句中的第二个赋值将col2设置为当前(更新)的col1值,而不是原始的col1值。结果是col1和col2具有相同的值。这种行为与标准SQL不同。

sql 复制代码
UPDATE t1 SET col1 = col1 + 1, col2 = col1;
外键约束差异

外键约束的MySQL实现在以下关键方面不同于SQL标准:

1、如果父表中有多行具有相同的引用键值,InnoDB会执行外键检查,就好像其他具有相同键值的父行不存在一样。例如,如果您定义了RESTRICT类型约束,并且有一个子行具有多个父行,InnoDB不允许删除任何父行。这在以下示例中显示:

sql 复制代码
mysql> CREATE TABLE parent (
    ->     id INT,
    ->     INDEX (id)
    -> ) ENGINE=InnoDB;
Query OK, 0 rows affected (0.04 sec)

mysql> CREATE TABLE child (
    ->     id INT,
    ->     parent_id INT,
    ->     INDEX par_ind (parent_id),
    ->     FOREIGN KEY (parent_id)
    ->         REFERENCES parent(id)
    ->         ON DELETE RESTRICT
    -> ) ENGINE=InnoDB;
Query OK, 0 rows affected (0.02 sec)

mysql> INSERT INTO parent (id) 
    ->     VALUES ROW(1), ROW(2), ROW(3), ROW(1);
Query OK, 4 rows affected (0.01 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> INSERT INTO child (id,parent_id) 
    ->     VALUES ROW(1,1), ROW(2,2), ROW(3,3);
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> DELETE FROM parent WHERE id=1;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key
constraint fails (`test`.`child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY
(`parent_id`) REFERENCES `parent` (`id`) ON DELETE RESTRICT)

2、如果ON UPDATE CASCADE或ON UPDATE SET NULL递归以更新它之前在同一级联期间更新过的同一表,则其行为类似于RESTRICT。这意味着您不能使用自引用ON UPDATE CASCADE或ON UPDATE SET NULL操作。这是为了防止级联更新导致无限循环。另一方面,自引用ON DELETE SET NULL是可能的,因为自引用也是可能的ON DELETE CASCADE。级联操作的嵌套深度不得超过15级。

3、在插入、删除或更新多行的SQL语句中,逐行检查外键约束(如唯一约束)。在执行外键检查时,InnoDB会在它必须检查的子记录或父记录上设置共享行级锁。MySQL立即检查外键约束;检查不会延迟到事务提交。根据SQL标准,默认行为应该是延迟检查。即只有在处理完整个SQL语句后才检查约束。这意味着无法使用外键删除引用自身的行。

4、没有存储引擎(包括InnoDB)可以识别或强制执行referential-integrity约束定义中使用的MATCH子句。使用显式MATCH子句没有指定的效果,它会导致忽略ON DELETE和ON UPDATE子句。应避免指定MATCH。

SQL标准中的MATCH子句控制复合(多列)外键中的NULL值在与引用表中的主键进行比较时如何处理。MySQL本质上实现了MATCH SIMPLE定义的语义学,它允许外键全部或部分NULL。在这种情况下,可以插入包含此类外键的(子表)行,即使它没有拟合引用(父)表中的任何行。(可以使用触发器实现其他语义学。)

5、引用非UNIQUE键的FOREIGN KEY约束不是标准SQL而是现在已弃用的InnoDB扩展,必须通过设置restrict_fk_on_non_standard_key启用。在MySQL的未来版本中可能删除对使用非标准键的支持,不建议使用他们。

根据SQL标准,NDB存储引擎需要在作为外键引用的任何列上显式唯一键(或主键)。

6、对于不支持外键的存储引擎(如MyISAM),MySQL Server会解析并忽略外键规范。

7、MySQL解析但忽略"内联REFERENCES规范"(在SQL标准中定义),其中引用定义为列规范的一部分。MySQL仅在指定为单独的FOREIGN KEY规范的一部分时接受REFERENCES子句。定义列以使用REFERENCES tbl_name(col_name)子句没有实际效果,仅作为备忘录或注释,告诉您当前定义的列在另一个表中详情可见。使用此语法时必须注意:

a、MySQL不执行任何类型的检查来确保col_name确实存在于tbl_name中(甚至tbl_name本身存在)。

b、MySQL不会对tbl_name执行任何类型的操作,例如删除行以响应对您正在定义的表中的行执行的操作;换句话说,这种语法不会产生任何ON DELETE或ON UPDATE行为。(尽管您可以编写ON DELETE或ON UPDATE子句作为REFERENCES的一部分,但它也会被忽略。)

c、此语法创建一个列;它不创建任何类型的索引或键。

您可以将这样创建的列用作连接列,如下所示:

sql 复制代码
CREATE TABLE person (
    id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
    name CHAR(60) NOT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE shirt (
    id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
    style ENUM('t-shirt', 'polo', 'dress') NOT NULL,
    color ENUM('red', 'blue', 'orange', 'white', 'black') NOT NULL,
    owner SMALLINT UNSIGNED NOT NULL REFERENCES person(id),
    PRIMARY KEY (id)
);

INSERT INTO person VALUES (NULL, 'Antonio Paz');

SELECT @last := LAST_INSERT_ID();

INSERT INTO shirt VALUES
    ROW(NULL, 'polo', 'blue', @last),
    ROW(NULL, 'dress', 'white', @last),
    ROW(NULL, 't-shirt', 'blue', @last);

INSERT INTO person VALUES (NULL, 'Lilliana Angelovska');

SELECT @last := LAST_INSERT_ID();

INSERT INTO shirt VALUES
    ROW(NULL, 'dress', 'orange', @last),
    ROW(NULL, 'polo', 'red', @last),
    ROW(NULL, 'dress', 'blue', @last),
    ROW(NULL, 't-shirt', 'white', @last);

SELECT * FROM person;
+----+---------------------+
| id | name                |
+----+---------------------+
|  1 | Antonio Paz         |
|  2 | Lilliana Angelovska |
+----+---------------------+

SELECT * FROM shirt;
+----+---------+--------+-------+
| id | style   | color  | owner |
+----+---------+--------+-------+
|  1 | polo    | blue   |     1 |
|  2 | dress   | white  |     1 |
|  3 | t-shirt | blue   |     1 |
|  4 | dress   | orange |     2 |
|  5 | polo    | red    |     2 |
|  6 | dress   | blue   |     2 |
|  7 | t-shirt | white  |     2 |
+----+---------+--------+-------+

SELECT s.* FROM person p INNER JOIN shirt s
   ON s.owner = p.id
WHERE p.name LIKE 'Lilliana%'
   AND s.color <> 'white';

+----+-------+--------+-------+
| id | style | color  | owner |
+----+-------+--------+-------+
|  4 | dress | orange |     2 |
|  5 | polo  | red    |     2 |
|  6 | dress | blue   |     2 |
+----+-------+--------+-------+

以这种方式使用时,REFERENCES不会显示在SHOW CREATE TABLE或DESCRIBE的输出中:

sql 复制代码
mysql> SHOW CREATE TABLE shirt\G
*************************** 1. row ***************************
Table: shirt
Create Table: CREATE TABLE `shirt` (
`id` smallint(5) unsigned NOT NULL auto_increment,
`style` enum('t-shirt','polo','dress') NOT NULL,
`color` enum('red','blue','orange','white','black') NOT NULL,
`owner` smallint(5) unsigned NOT NULL,
PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
'--'作为SQL注解的开头

标准SQL使用C语法/* this is a comment */用于注释,MySQL服务器也支持这种语法。MySQL还支持对这种语法的扩展,使MySQL特定的SQL能够嵌入到注释中。

MySQL服务器也使用#作为开始注释字符。这是不标准的。

标准SQL还使用"--"作为开始注释序列。MySQL服务器支持--注释样式的变体;--开始注释序列被接受,但必须后跟空格或换行符等空格字符。该空格旨在防止使用如下结构生成的SQL查询出现问题,这些结构会更新余额以反映费用:

sql 复制代码
UPDATE account SET balance=balance-charge
WHERE account_id=user_id

考虑一下当charge具有负值时会发生什么,例如-1,这可能是将金额记入账户的情况。在这种情况下,生成的语句如下所示:

sql 复制代码
UPDATE account SET balance=balance--1
WHERE account_id=5752;

balance--1是有效的标准SQL,但是--被解释为注释的开始,并且表达式的一部分被丢弃。结果是一个与预期含义完全不同的语句:

sql 复制代码
UPDATE account SET balance=balance
WHERE account_id=5752;

该语句的值根本不会发生任何变化。为了防止这种情况发生,MySQL需要在--后面加上一个空格字符,以便在MySQL服务器中将其识别为开始注释序列,以便始终可以安全使用balance--1之类的表达式。

相关推荐
('-')9 分钟前
《从根上理解MySQL是怎样运行的》第三章学习笔记
笔记·学习·mysql
数据知道12 分钟前
FastAPI项目:构建打字速度测试网站(MySQL版本)
数据库·python·mysql·fastapi·python项目
姓蔡小朋友36 分钟前
redis GEO数据结构、实现附近商铺功能
数据结构·数据库·redis
冉冰学姐44 分钟前
SSM农贸市场摊位管理系统c22ux(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm 框架·农贸市场·摊位管理系统
面向星辰1 小时前
SQL LIKE 相似信息查找语句
数据库·sql
数据库学啊1 小时前
时序数据库选型
数据库·时序数据库
TDengine (老段)1 小时前
强杀服务、重启系统及断电对 TDengine 影响
运维·服务器·数据库·物联网·时序数据库·tdengine·涛思数据
数据库学啊1 小时前
时序数据库怎么选
数据库·时序数据库
baivfhpwxf20231 小时前
SQL Server 创建一个删除分表的作业,每月执行一次,删除表的逻辑放到存储过程里
数据库
清静诗意3 小时前
独立 IoT 客户端绕过 Django 生命周期导致数据库断链:诊断与修复
python·mysql·django·生命周期