ANSI SQL 与扩展SQL详解
一、ANSI SQL语法
ANSI SQL(American National Standards Institute Structured Query Language),即美国国家标准学会结构化查询语言,是一种标准化的数据库查询语言。它用于管理和操作关系数据库中的数据。ANSI SQL 定义了一组标准的 SQL 语句,以确保在不同的数据库管理系统(DBMS)之间保持一致性和互操作性。下面是 ANSI SQL 语法的详解:
数据定义语言 (DDL)
DDL 用于定义和管理数据库结构,如创建、修改和删除数据库对象。
-
创建数据库:
sqlCREATE DATABASE database_name;
-
删除数据库:
sqlDROP DATABASE database_name;
-
创建表:
sqlCREATE TABLE table_name ( column1 datatype [constraint], column2 datatype [constraint], ... );
-
删除表:
sqlDROP TABLE table_name;
-
修改表:
-
添加列:
sqlALTER TABLE table_name ADD column_name datatype;
-
删除列:
sqlALTER TABLE table_name DROP COLUMN column_name;
-
修改列数据类型:
sqlALTER TABLE table_name ALTER COLUMN column_name datatype;
-
数据操作语言 (DML)
DML 用于查询和修改数据库中的数据。
-
插入数据:
sqlINSERT INTO table_name (column1, column2, ...) VALUES (value1, value2, ...);
-
更新数据:
sqlUPDATE table_name SET column1 = value1, column2 = value2, ... WHERE condition;
-
删除数据:
sqlDELETE FROM table_name WHERE condition;
-
查询数据:
sqlSELECT column1, column2, ... FROM table_name WHERE condition GROUP BY column HAVING condition ORDER BY column LIMIT number;
数据控制语言 (DCL)
DCL 用于授权和撤销对数据库对象的访问权限。
-
授予权限:
sqlGRANT privilege ON object TO user;
-
撤销权限:
sqlREVOKE privilege ON object FROM user;
事务控制语言 (TCL)
TCL 用于管理事务处理,以确保数据的一致性和完整性。
-
开始事务:
sqlBEGIN TRANSACTION;
-
提交事务:
sqlCOMMIT;
-
回滚事务:
sqlROLLBACK;
语法示例
创建表
sql
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
hire_date DATE,
salary DECIMAL(10, 2),
department_id INT,
FOREIGN KEY (department_id) REFERENCES departments(department_id)
);
插入数据
sql
INSERT INTO employees (employee_id, first_name, last_name, hire_date, salary, department_id)
VALUES (1, 'John', 'Doe', '2022-01-01', 50000.00, 101);
查询数据
sql
SELECT first_name, last_name, salary
FROM employees
WHERE department_id = 101
ORDER BY last_name;
更新数据
sql
UPDATE employees
SET salary = salary * 1.10
WHERE department_id = 101;
删除数据
sql
DELETE FROM employees
WHERE employee_id = 1;
授予权限
sql
GRANT SELECT, INSERT ON employees TO user1;
回滚事务
sql
BEGIN TRANSACTION;
UPDATE employees
SET salary = salary * 1.10
WHERE department_id = 101;
ROLLBACK; -- 取消更新操作
ANSI SQL 提供了一套通用的标准语法,用于定义和操作关系数据库。尽管不同的数据库管理系统(DBMS)对 SQL 标准进行了不同程度的扩展,但理解和掌握 ANSI SQL 标准是学习和使用 SQL 的基础。在实际应用中,可以根据特定 DBMS 的文档和功能,进一步了解和使用其特有的 SQL 扩展。
ANSI SQL 多表查询
ANSI SQL 标准定义了多表连接查询的几种常见类型,包括内连接 (INNER JOIN)、左外连接 (LEFT JOIN)、右外连接 (RIGHT JOIN)、全外连接 (FULL JOIN) 、交叉连接 (CROSS JOIN)和自链接(SELF JOIN),下面提供了每种连接类型的语法和示例,并增加图示以帮助理解连接结果。
1. 内连接 (INNER JOIN)
语法
sql
SELECT columns
FROM table1
INNER JOIN table2
ON table1.common_column = table2.common_column;
示例
sql
SELECT employees.name, departments.department_name
FROM employees
INNER JOIN departments
ON employees.department_id = departments.department_id;
图示
employees departments
+----+------+---------+ +----+---------+
| id | name | dept_id | | id | name |
+----+------+---------+ +----+---------+
| 1 | John | 1 | | 1 | HR |
| 2 | Jane | 2 | | 2 | IT |
| 3 | Mike | 1 | | 3 | Finance |
| 4 | Mary | 3 | | 4 | Sales |
+----+------+---------+ +----+---------+
Result:
+------+---------+
| name | dept_name |
+------+---------+
| John | HR |
| Mike | HR |
| Jane | IT |
+------+---------+
2. 左外连接 (LEFT JOIN)
语法
sql
SELECT columns
FROM table1
LEFT JOIN table2
ON table1.common_column = table2.common_column;
示例
sql
SELECT employees.name, departments.department_name
FROM employees
LEFT JOIN departments
ON employees.department_id = departments.department_id;
图示
employees departments
+----+------+---------+ +----+---------+
| id | name | dept_id | | id | name |
+----+------+---------+ +----+---------+
| 1 | John | 1 | | 1 | HR |
| 2 | Jane | 2 | | 2 | IT |
| 3 | Mike | 1 | | 3 | Finance |
| 4 | Mary | 4 | | 4 | Sales |
+----+------+---------+ +----+---------+
Result:
+------+---------+
| name | dept_name |
+------+---------+
| John | HR |
| Mike | HR |
| Jane | IT |
| Mary | NULL |
+------+---------+
3. 右外连接 (RIGHT JOIN)
语法
sql
SELECT columns
FROM table1
RIGHT JOIN table2
ON table1.common_column = table2.common_column;
示例
sql
SELECT employees.name, departments.department_name
FROM employees
RIGHT JOIN departments
ON employees.department_id = departments.department_id;
图示
employees departments
+----+------+---------+ +----+---------+
| id | name | dept_id | | id | name |
+----+------+---------+ +----+---------+
| 1 | John | 1 | | 1 | HR |
| 2 | Jane | 2 | | 2 | IT |
| 3 | Mike | 1 | | 3 | Finance |
| 4 | Mary | 4 | | 4 | Sales |
+----+------+---------+ +----+---------+
Result:
+------+---------+
| name | dept_name |
+------+---------+
| John | HR |
| Mike | HR |
| Jane | IT |
| NULL | Finance |
| NULL | Sales |
+------+---------+
4. 全外连接 (FULL JOIN)
语法
sql
SELECT columns
FROM table1
FULL JOIN table2
ON table1.common_column = table2.common_column;
示例
sql
SELECT employees.name, departments.department_name
FROM employees
FULL JOIN departments
ON employees.department_id = departments.department_id;
图示
employees departments
+----+------+---------+ +----+---------+
| id | name | dept_id | | id | name |
+----+------+---------+ +----+---------+
| 1 | John | 1 | | 1 | HR |
| 2 | Jane | 2 | | 2 | IT |
| 3 | Mike | 1 | | 3 | Finance |
| 4 | Mary | 4 | | 4 | Sales |
+----+------+---------+ +----+---------+
Result:
+------+---------+
| name | dept_name |
+------+---------+
| John | HR |
| Mike | HR |
| Jane | IT |
| NULL | Finance |
| NULL | Sales |
| Mary | NULL |
+------+---------+
5. 交叉连接 (CROSS JOIN)
语法
sql
SELECT columns
FROM table1
CROSS JOIN table2;
示例
sql
SELECT employees.name, departments.department_name
FROM employees
CROSS JOIN departments;
图示
employees departments
+----+------+---------+ +----+---------+
| id | name | dept_id | | id | name |
+----+------+---------+ +----+---------+
| 1 | John | 1 | | 1 | HR |
| 2 | Jane | 2 | | 2 | IT |
| 3 | Mike | 1 | | 3 | Finance |
| 4 | Mary | 4 | | 4 | Sales |
+----+------+---------+ +----+---------+
Result:
+------+---------+
| name | dept_name |
+------+---------+
| John | HR |
| John | IT |
| John | Finance |
| John | Sales |
| Jane | HR |
| Jane | IT |
| Jane | Finance |
| Jane | Sales |
| Mike | HR |
| Mike | IT |
| Mike | Finance |
| Mike | Sales |
| Mary | HR |
| Mary | IT |
| Mary | Finance |
| Mary | Sales |
+------+---------+
6. 自连接 (SELF JOIN)
语法
sql
SELECT a.columns, b.columns
FROM table a
INNER JOIN table b
ON a.common_column = b.common_column;
示例
sql
SELECT e1.name AS Employee, e2.name AS Manager
FROM employees e1
INNER JOIN employees e2
ON e1.manager_id = e2.employee_id;
图示
employees
+----+------+---------+------------+
| id | name | dept_id | manager_id |
+----+------+---------+------------+
| 1 | John | 1 | NULL |
| 2 | Jane | 2 | 1 |
| 3 | Mike | 1 | 1 |
| 4 | Mary | 3 | 2 |
+----+------+---------+------------+
Result:
+----------+---------+
| Employee | Manager |
+----------+---------+
| Jane | John |
| Mike | John |
| Mary | Jane |
+----------+---------+
7、小结
这些示例展示了使用 ANSI SQL 进行多表连接查询的常见类型,并提供了可视化图示来帮助理解每种连接的结果。这些连接类型包括内连接、左外连接、右外连接、全外连接、交叉连接和自连接。通过这些示例和图示,您可以更直观地理解多表连接的功能和用法。
ANSI SQL 的优势
- 标准化: 由于 ANSI SQL 是标准化的,学习和使用它能够在不同的数据库系统之间通用。
- 可移植性: 使用 ANSI SQL 编写的查询可以在支持 ANSI SQL 标准的数据库管理系统之间迁移。
- 广泛支持: 主要的数据库管理系统如 MySQL、PostgreSQL、Oracle、SQL Server 等,都支持 ANSI SQL。
ANSI SQL 版本
ANSI SQL 标准自首次发布以来,经历了多次修订和更新。主要版本包括:
- SQL-86: 第一个标准版本。
- SQL-89: 对 SQL-86 的小幅修订。
- SQL-92: 增加了许多新功能,是影响最大的一个版本。
- SQL:1999: 引入了对象关系模型和扩展的存储过程。
- SQL:2003: 引入了 XML 支持和窗口函数。
- SQL:2006: 提升了对 XML 的支持。
- SQL:2008: 引入了新数据类型和一些小修正。
- SQL:2011: 引入了时间和序列数据类型。
- SQL:2016: 增加了 JSON 支持和多项新功能。
总结
ANSI SQL 是一种强大的数据库查询和操作语言,具有高度的标准化和广泛的支持。它为数据库管理提供了基础,确保了不同数据库系统之间的兼容性和互操作性。通过学习 ANSI SQL,用户能够在各种数据库环境中有效地管理和操作数据。
二、常见数据库在ANSI SQL实现上的差异
以下是 MySQL、PostgreSQL、SQL Server 和 Oracle 在实现 ANSI SQL 标准上的详细差异和扩展,涵盖数据类型、数据定义语言 (DDL)、数据操作语言 (DML)、函数和表达式、事务控制语言 (TCL)、索引和约束、以及语法书写上的差异。
1. 数据类型
MySQL
扩展:
- TINYINT: 小整数 (0 到 255)
- MEDIUMINT: 中整数 (-8388608 到 8388607)
- DATETIME: 日期和时间,精度为秒
- ENUM: 枚举类型
- SET: 集合类型
- BIT: 位字段
PostgreSQL
扩展:
- SERIAL: 自动递增整数
- BIGSERIAL: 大范围的自动递增整数
- TIMESTAMP WITH TIME ZONE: 带有时区的日期和时间
- JSON/JSONB: JSON 数据类型,JSONB 支持二进制存储
- UUID: 全局唯一标识符
- ARRAY: 数组类型
- HSTORE: 键值对存储
SQL Server
扩展:
- TINYINT: 小整数 (0 到 255)
- DATETIME2: 日期和时间,精度更高
- DATETIMEOFFSET: 带有时区的日期和时间
- UNIQUEIDENTIFIER: 全局唯一标识符
- SQL_VARIANT: 任何数据类型的值
- NVARCHAR: 支持 Unicode 的字符串
Oracle
扩展:
- NUMBER: 可变精度的数值
- TIMESTAMP WITH TIME ZONE: 带有时区的时间戳
- TIMESTAMP WITH LOCAL TIME ZONE: 本地时区时间戳
- CLOB: 大文本对象
- BLOB: 二进制大对象
- RAW: 原始二进制数据
2. 数据定义语言 (DDL)
创建表
-
MySQL:
sqlCREATE TABLE example ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) ) ENGINE=InnoDB;
-
PostgreSQL:
sqlCREATE TABLE example ( id SERIAL PRIMARY KEY, name VARCHAR(50) );
-
SQL Server:
sqlCREATE TABLE example ( id INT IDENTITY(1,1) PRIMARY KEY, name NVARCHAR(50) );
-
Oracle:
sqlCREATE TABLE example ( id NUMBER GENERATED BY DEFAULT AS IDENTITY, name VARCHAR2(50) );
扩展:
- MySQL: 支持不同的存储引擎,如 InnoDB 和 MyISAM。
- PostgreSQL: 支持扩展表继承和分区。
- SQL Server: 支持文件组和表分区。
- Oracle: 提供表分区和索引组织表。
3. 数据操作语言 (DML)
插入数据
-
MySQL:
sqlINSERT INTO example (name) VALUES ('John Doe');
-
PostgreSQL:
sqlINSERT INTO example (name) VALUES ('John Doe');
-
SQL Server:
sqlINSERT INTO example (name) VALUES ('John Doe');
-
Oracle:
sqlINSERT INTO example (name) VALUES ('John Doe');
扩展:
- MySQL : 支持
INSERT IGNORE
和REPLACE
语句。 - PostgreSQL : 提供
INSERT ... ON CONFLICT DO NOTHING/UPDATE
。 - SQL Server : 支持
MERGE
语句进行条件插入和更新。 - Oracle : 提供
MERGE INTO
语句进行条件插入和更新。
4. 函数和表达式
字符串函数
-
MySQL:
sqlSELECT CONCAT(first_name, ' ', last_name) FROM example;
-
PostgreSQL:
sqlSELECT CONCAT(first_name, ' ', last_name) FROM example;
-
SQL Server:
sqlSELECT first_name + ' ' + last_name FROM example;
-
Oracle:
sqlSELECT first_name || ' ' || last_name FROM example;
扩展:
- MySQL : 提供
FIND_IN_SET
、FIELD
等函数。 - PostgreSQL : 支持正则表达式匹配和处理函数,如
REGEXP_MATCH
。 - SQL Server : 提供
CHARINDEX
、PATINDEX
等函数。 - Oracle : 提供丰富的字符串处理函数,如
INSTR
、SUBSTR
、REGEXP_INSTR
。
5. 事务控制语言 (TCL)
开始事务
-
MySQL:
sqlSTART TRANSACTION;
-
PostgreSQL:
sqlBEGIN;
-
SQL Server:
sqlBEGIN TRANSACTION;
-
Oracle:
sqlBEGIN;
扩展:
- MySQL : 提供显式锁机制,如
LOCK TABLES
。 - PostgreSQL : 支持
SAVEPOINT
创建事务保存点。 - SQL Server : 提供
SAVE TRANSACTION
和TRY...CATCH
错误处理。 - Oracle : 提供
SAVEPOINT
和高级锁机制。
6. 索引和约束
创建索引
-
MySQL:
sqlCREATE INDEX idx_name ON example (name);
-
PostgreSQL:
sqlCREATE INDEX idx_name ON example (name);
-
SQL Server:
sqlCREATE INDEX idx_name ON example (name);
-
Oracle:
sqlCREATE INDEX idx_name ON example (name);
扩展:
- MySQL: 支持全文索引和空间索引。
- PostgreSQL: 支持 GiST、GIN 等索引类型。
- SQL Server: 支持全文索引和过滤索引。
- Oracle: 支持基于函数的索引和位图索引。
外键约束
-
MySQL:
sqlALTER TABLE example ADD CONSTRAINT fk_example FOREIGN KEY (department_id) REFERENCES departments(department_id);
-
PostgreSQL:
sqlALTER TABLE example ADD CONSTRAINT fk_example FOREIGN KEY (department_id) REFERENCES departments(department_id);
-
SQL Server:
sqlALTER TABLE example ADD CONSTRAINT fk_example FOREIGN KEY (department_id) REFERENCES departments(department_id);
-
Oracle:
sqlALTER TABLE example ADD CONSTRAINT fk_example FOREIGN KEY (department_id) REFERENCES departments(department_id);
7. 其他特性
MySQL
扩展:
- 复制: 主从复制、半同步复制、组复制。
- 分区: 范围分区、列表分区、哈希分区等。
- 插件架构: 支持多种存储引擎。
- 语法差异 :
- 默认
AUTO_INCREMENT
关键字。 - 支持
INSERT IGNORE
和REPLACE
语句。
- 默认
PostgreSQL
扩展:
- 扩展: 支持创建自定义扩展。
- 数据类型: 广泛支持地理数据类型和全文搜索。
- 并行查询: 支持并行查询执行。
- 语法差异 :
- 默认使用
SERIAL
数据类型。 - 支持
RETURNING
子句获取插入/更新的数据。
- 默认使用
SQL Server
扩展:
- CLR: 支持 Common Language Runtime (CLR) 集成。
- 联机索引重建: 允许在线索引重建。
- Always On: 高可用性和灾难恢复解决方案。
- 语法差异 :
- 使用
IDENTITY
关键字。 - 支持
MERGE
语句进行条件插入和更新。
- 使用
Oracle
扩展:
- PL/SQL: 强大的过程化语言支持。
- Flashback: 数据库闪回功能。
- RAC: 真实应用集群,实现高可用性。
- 语法差异 :
- 使用
NUMBER
数据类型。 - 支持
MERGE INTO
语句进行条件插入和更新。
- 使用
总结
各个数据库系统在实现 ANSI SQL 标准时,都进行了广泛的扩展,以满足不同应用
三、常见数据库在SQL语法差异
以下列举了 MySQL、PostgreSQL、SQL Server 和 Oracle 在一些常见 SQL 操作(如分页、字符串连接、日期处理等)上的语法差异和独特的功能实现。
1. 分页查询
MySQL
sql
SELECT * FROM table_name
LIMIT 10 OFFSET 20;
PostgreSQL
sql
SELECT * FROM table_name
LIMIT 10 OFFSET 20;
SQL Server
sql
SELECT * FROM table_name
ORDER BY column_name
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
Oracle
sql
SELECT * FROM (
SELECT t.*, ROWNUM rnum FROM table_name t
WHERE ROWNUM <= 30
) WHERE rnum > 20;
2. 字符串连接
MySQL
sql
SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM table_name;
PostgreSQL
sql
SELECT first_name || ' ' || last_name AS full_name FROM table_name;
SQL Server
sql
SELECT first_name + ' ' + last_name AS full_name FROM table_name;
Oracle
sql
SELECT first_name || ' ' || last_name AS full_name FROM table_name;
3. 日期处理
MySQL
sql
SELECT DATE_ADD(NOW(), INTERVAL 1 DAY) AS tomorrow;
PostgreSQL
sql
SELECT NOW() + INTERVAL '1 day' AS tomorrow;
SQL Server
sql
SELECT DATEADD(day, 1, GETDATE()) AS tomorrow;
Oracle
sql
SELECT SYSDATE + 1 AS tomorrow FROM dual;
4. 自动递增主键
MySQL
sql
CREATE TABLE example (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50)
);
PostgreSQL
sql
CREATE TABLE example (
id SERIAL PRIMARY KEY,
name VARCHAR(50)
);
SQL Server
sql
CREATE TABLE example (
id INT IDENTITY(1,1) PRIMARY KEY,
name NVARCHAR(50)
);
Oracle
sql
CREATE TABLE example (
id NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
name VARCHAR2(50)
);
5. 获取当前日期和时间
MySQL
sql
SELECT NOW() AS current_time;
PostgreSQL
sql
SELECT NOW() AS current_time;
SQL Server
sql
SELECT GETDATE() AS current_time;
Oracle
sql
SELECT SYSDATE AS current_time FROM dual;
6. 子查询中的列别名
MySQL
sql
SELECT sub.name FROM (SELECT name FROM table_name) AS sub;
PostgreSQL
sql
SELECT sub.name FROM (SELECT name FROM table_name) AS sub;
SQL Server
sql
SELECT sub.name FROM (SELECT name FROM table_name) AS sub;
Oracle
sql
SELECT sub.name FROM (SELECT name FROM table_name) sub;
7. 插入或更新 (UPSERT)
MySQL
sql
INSERT INTO table_name (id, name) VALUES (1, 'John Doe')
ON DUPLICATE KEY UPDATE name = VALUES(name);
PostgreSQL
sql
INSERT INTO table_name (id, name) VALUES (1, 'John Doe')
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name;
SQL Server
sql
MERGE INTO table_name AS target
USING (SELECT 1 AS id, 'John Doe' AS name) AS source (id, name)
ON (target.id = source.id)
WHEN MATCHED THEN
UPDATE SET name = source.name
WHEN NOT MATCHED THEN
INSERT (id, name) VALUES (source.id, source.name);
Oracle
sql
MERGE INTO table_name target
USING (SELECT 1 AS id, 'John Doe' AS name FROM dual) source
ON (target.id = source.id)
WHEN MATCHED THEN
UPDATE SET name = source.name
WHEN NOT MATCHED THEN
INSERT (id, name) VALUES (source.id, source.name);
8. 删除重复记录
MySQL
sql
DELETE t1 FROM table_name t1
INNER JOIN table_name t2
WHERE t1.id > t2.id AND t1.name = t2.name;
PostgreSQL
sql
DELETE FROM table_name a
USING table_name b
WHERE a.id > b.id AND a.name = b.name;
SQL Server
sql
WITH CTE AS (
SELECT name,
ROW_NUMBER() OVER (PARTITION BY name ORDER BY id) AS rn
FROM table_name
)
DELETE FROM CTE WHERE rn > 1;
Oracle
sql
DELETE FROM table_name
WHERE ROWID IN (
SELECT rid
FROM (
SELECT ROWID AS rid,
ROW_NUMBER() OVER (PARTITION BY name ORDER BY id) AS rn
FROM table_name
)
WHERE rn > 1
);
9. 条件更新 (CASE WHEN)
MySQL
sql
UPDATE table_name
SET column_name = CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
ELSE result3
END
WHERE some_column = some_value;
PostgreSQL
sql
UPDATE table_name
SET column_name = CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
ELSE result3
END
WHERE some_column = some_value;
SQL Server
sql
UPDATE table_name
SET column_name = CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
ELSE result3
END
WHERE some_column = some_value;
Oracle
sql
UPDATE table_name
SET column_name = CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
ELSE result3
END
WHERE some_column = some_value;
10. 行号 (ROW NUMBER)
MySQL
sql
SELECT *,
@row_num := @row_num + 1 AS row_number
FROM table_name, (SELECT @row_num := 0) AS r;
PostgreSQL
sql
SELECT *,
ROW_NUMBER() OVER (ORDER BY some_column) AS row_number
FROM table_name;
SQL Server
sql
SELECT *,
ROW_NUMBER() OVER (ORDER BY some_column) AS row_number
FROM table_name;
Oracle
sql
SELECT *,
ROW_NUMBER() OVER (ORDER BY some_column) AS row_number
FROM table_name;
总结
以上示例展示了 MySQL、PostgreSQL、SQL Server 和 Oracle 在处理常见 SQL 操作时的语法差异和独特功能。这些差异包括分页查询、字符串连接、日期处理、自动递增主键、获取当前日期和时间、子查询中的列别名、插入或更新操作、删除重复记录、条件更新和行号生成。了解这些差异有助于在不同数据库系统之间进行迁移和优化查询。