背景
- 经常看到 SQL-92,SQL-03 的词汇,但是不了解其中的差异,比如 SQL-03 比 SQL-92 多了些什么?
- 知道不同的数据库有不同的方言,但是不了解哪些是 SQL 标准中定义的大家都遵守的。
- 开发一个类 SQL 语言时,选择支持哪些语法好呢?
本文从 ANSI SQL 切入,来调研一下 ANSI SQL 标准(下面简称为 SQL 标准)的演变,以及不同数据库对 SQL 标准的支持程度。
历程
通过 SQL Wikipedia 上了解到:
由于不同的数据库产商的 SQL 语法不兼容,因此 1986 年开始,ANSI 和 ISO 标准化组织采纳了 SQL 语言标准。并且在随后的年份陆续公布了新的标准。
具体年份和名称如下:
年份 | 名称 | 备注 |
---|---|---|
1986 | SQL-86 | 第一版 |
1989 | SQL-89 | 小修改 |
1992 | SQL-92 | 大修改 |
1999 | SQL:1999 | 加入嵌套表(nested table[4]), 加入正则表达式, 递归查询,触发器,过程和控制流,数组类型和一些结构化类型 |
2003 | SQL:2003 | 加入 XML 相关特征,窗口函数,标准化序列(standardized sequences),自动生成值的列 |
2006 | SQL:2006 | 支持导入,存储和操作 XML 数据,加入 XQuery |
2008 | SQL:2008 | 支持 cursor 定义外的 ORDER BY?, 增加 INSTEAD OF 触发器,增加 TRUNCATE,增加 FETCH |
2011 | SQL:2011 | 增加 temporal data,增强 window functions 和 FETCH |
2016 | SQL:2016 | 增加列特征匹配(row pattern matching), 多态表函数(polymorphic table functions), JSON |
标准公开出来后,不同的产商的实现情况也是不兼容的,即不完全遵守标准。主要是日期类型,时间类型,字符串拼接,NULL 和大小写敏感。
只有 PostgreSQL 和 Mimer SQL 是紧跟标准的。
变化
请找一个充足的时间,打开 Modern SQL: Slides,从头到尾看一遍。
或者打开 Modern SQL in Open Source and Commercial Databases,下载下来看。
这里做一下读书笔记
LATERAL
来自 SQL:1999
依旧拿 PostgreSQL 初体验中的示例数据举例,现在有两张表 cities
和 weather
如下:
mydb=# select * from cities; name | location ---------------+----------- San Francisco | (-194,53) (1 row)
mydb=# select * from weather; city | temp_lo | temp_hi | prcp | date ---------------+---------+---------+------+------------ San Francisco | 46 | 50 | 0.25 | 1994-11-27 San Francisco | 43 | 57 | 0 | 1994-11-29 Hayward | 37 | 54 | | 1994-11-29 (3 rows)
JOIN LATERAL
现在两张表要做一个 JOIN
在 SQL:1999 之前,只能这么写:
SELECT * FROM cities JOIN ( SELECT * FROM weather ) inline_view ON inline_view.city = cities.name
执行结果
name | location | city | temp_lo | temp_hi | prcp | date ---------------+-----------+---------------+---------+---------+------+------------ San Francisco | (-194,53) | San Francisco | 43 | 57 | 0 | 1994-11-29 San Francisco | (-194,53) | San Francisco | 46 | 50 | 0.25 | 1994-11-27 (2 rows)
在 SQL:1999 之后,可以这样写:
SELECT * FROM cities JOIN lateral( SELECT * FROM weather WHERE weather.city = cities.name ) inline_view ON true
结果
name | location | city | temp_lo | temp_hi | prcp | date ---------------+-----------+---------------+---------+---------+------+------------ San Francisco | (-194,53) | San Francisco | 43 | 57 | 0 | 1994-11-29 San Francisco | (-194,53) | San Francisco | 46 | 50 | 0.25 | 1994-11-27 (2 rows)
注意两点:
- 在
inline_view
中,引用了外部的 cities - JOIN 使用了 join lateral 关键字,而不仅仅是 join
好奇一下内部的执行过程是不是一样的,使用 explain 来看一下
EXPLAIN SELECT * FROM cities JOIN ( SELECT * FROM weather ) inline_view ON inline_view.city = cities.name;
结果
QUERY PLAN ------------------------------------------------------------------------ Hash Join (cost=18.10..55.28 rows=648 width=388) Hash Cond: ((cities.name)::text = (weather.city)::text) -> Seq Scan on cities (cost=0.00..13.60 rows=360 width=194) -> Hash (cost=13.60..13.60 rows=360 width=194) -> Seq Scan on weather (cost=0.00..13.60 rows=360 width=194) (5 rows)
EXPLAIN SELECT * FROM cities JOIN lateral( SELECT * FROM weather WHERE weather.city = cities.name ) inline_view ON true;
结果
QUERY PLAN ------------------------------------------------------------------------ Hash Join (cost=18.10..55.28 rows=648 width=388) Hash Cond: ((cities.name)::text = (weather.city)::text) -> Seq Scan on cities (cost=0.00..13.60 rows=360 width=194) -> Hash (cost=13.60..13.60 rows=360 width=194) -> Seq Scan on weather (cost=0.00..13.60 rows=360 width=194) (5 rows)
这两个的实际执行过程是一样的。
CROSS JOIN LATERAL
使用 cross join lateral 也可以实际同样的效果,同时可以不加末尾的 on true
SELECT * FROM cities CROSS JOIN lateral( SELECT * FROM weather WHERE weather.city = cities.name ) inline_view;
结果
name | location | city | temp_lo | temp_hi | prcp | date ---------------+-----------+---------------+---------+---------+------+------------ San Francisco | (-194,53) | San Francisco | 43 | 57 | 0 | 1994-11-29 San Francisco | (-194,53) | San Francisco | 46 | 50 | 0.25 | 1994-11-27 (2 rows)
内部执行过程仍然是相同的
EXPLAIN SELECT * FROM cities CROSS JOIN lateral( SELECT * FROM weather WHERE weather.city = cities.name ) inline_view;
结果
QUERY PLAN ------------------------------------------------------------------------ Hash Join (cost=18.10..55.28 rows=648 width=388) Hash Cond: ((cities.name)::text = (weather.city)::text) -> Seq Scan on cities (cost=0.00..13.60 rows=360 width=194) -> Hash (cost=13.60..13.60 rows=360 width=194) -> Seq Scan on weather (cost=0.00..13.60 rows=360 width=194) (5 rows)
意义
这种写法有什么意义呢?比原来的写法的优越性在哪里呢?
一方面是 LATERAL 和 table functions 可以联合使用。这个目前是 PostgreSQL 中特有的,本文略过。
LATERAL & LIMIT
另一方面是 可以在 inline view 中加入 LIMIT 操作,比如:
SELECT * FROM cities CROSS JOIN lateral( SELECT * FROM weather WHERE weather.city = cities.name ORDER BY temp_lo LIMIT 1 ) inline_view;
结果
name | location | city | temp_lo | temp_hi | prcp | date ---------------+-----------+---------------+---------+---------+------+------------ San Francisco | (-194,53) | San Francisco | 43 | 57 | 0 | 1994-11-29 (1 row)
如果不使用 LATERAL 的话,是无法实现只取某一列的前 LIMIT 行的。
LATERAL & Multi-Source Top-N
场景:用户找出每个分类下最新的 10 条新闻。
SELECT n.* FROM news n JOIN subscriptions s ON n.topic = s.topic WHERE s.user = ? ORDER BY n.created DESC LIMIT 10
这样会把每个 subscriptions 下的所有的 news 都找出来,但是是没有必要的。
使用 LATERAL 的写法如下:
SELECT n.* FROM subscriptions s JOIN LATERAL( SELECT * FROM news n WHERE n.topic = s.topic ORDER BY n.created DESC LIMIT 10 ) top_news ON true WHERE s.user_id = ? ORDER BY n.created DESC LIMIT 10;
这样可以做到只取每个 news 下的前 10 条新闻.
小结
- SQL 中的"FOR EACH"
- 适合与 OUTER JOINS 联用
- 适合取子查询中的 Top-N
- 可以与 table functions 作 join
支持的数据库
- DB2 LUW 9.1+
- Oracle 12c+
- PostgreSQL 9.3+
- SQL Server 2005 部分支持
- MySQL 不支持
- SQLite 不支持
WITH (Common Table Expressions)
来自 SQL:1999
WITH 是解决什么问题的呢?
背景
OK,先来看一个在 SQL:1999 之前的场景:
当遇到多层嵌套的 SQL 时,我们会这样写。
SELECT ... FROM (SELECT ... FROM t1 JOIN (SELECT ... FROM ... ) a ON (...) ) b JOIN (SELECT ... FROM ... ) c ON (...)
下面的 SQL 是为了示例而示例:
SELECT * FROM ( SELECT * FROM cities ) a JOIN ( SELECT * FROM weather ) b ON a.name = b.city;
结果
name | location | city | temp_lo | temp_hi | prcp | date ---------------+-----------+---------------+---------+---------+------+------------ San Francisco | (-194,53) | San Francisco | 43 | 57 | 0 | 1994-11-29 San Francisco | (-194,53) | San Francisco | 46 | 50 | 0.25 | 1994-11-27 (2 rows)
当去理解这样的 SQL 语句时,需要先理解最里面的 SELECT,再理解次里面的 SELECT,一层一层的向外看,最后再理解最外层的 SELECT。在 SQL92 时,就是这样来做的。
如果有了 WITH CTEs,就可以用更易理解的方式来写 SQL 了。
语法
一个 CTE
WITH a (c1, c2, c3) AS (SELECT c1, c2, c3 FROM ...) SELECT ...
两个 CTEs
WITH a (c1, c2, c3) AS (SELECT c1, c2, c3 FROM ...), b (c4, ...) AS (SELECT c4, ... FROM t1 JOIN a ON(...) ) SELECT ...
- 多个 CTE 之间由逗号连接,只有第一个 CTE 前有 WITH 关键字,后面的 CTE 不需要 WITH 关键字。
- 在第二个 CTE 中可以引用第一个 CTE,即在后面的 CTE 可以引用前面的 CTE。
- 最后一个 CTE 后没有逗号,表明后面跟着的是查询语句。
这样的话,看 SQL 时就可以从上往下来看了。
将上面的示例重写一下:
WITH a (name, location) AS ( SELECT name, location FROM cities ), b (city, temp_lo, temp_hi, prcp, date) AS ( SELECT city, temp_lo, temp_hi, prcp, date FROM weather ) SELECT * FROM a JOIN b ON a.name = b.city;
结果如下:
name | location | city | temp_lo | temp_hi | prcp | date ---------------+-----------+---------------+---------+---------+------+------------ San Francisco | (-194,53) | San Francisco | 43 | 57 | 0 | 1994-11-29 San Francisco | (-194,53) | San Francisco | 46 | 50 | 0.25 | 1994-11-27 (2 rows)
小结
- WITH 是 SQL 中的"私有方法 private methods"
- WITH 视图可以多次被引用
- WITH 通过定义多个 CTE,来实现嵌套
- 可以用 SELECT 的地方,就可以用 WITH,如
INSERT INTO tbl WITH ... SELECT
坑
下推(push down)
需要注意的是,在 PostgreSQL 中,WITH 视图更像是一个物化视图,没有把查询中的过滤条件下推到视图中。
在数据量大时,可能会有性能问题。使用时请用 EXPLAIN 来仔细检查一下。
但是在 PostgreSQL 中使用 inline 视图时,会有下推的操作的。
上推(push up)
PostgreSQL 9.1+ 中,INSERT, UPDATE 和 DELETE 中也支持 WITH。
如将指定行从一个表移动到另一个表
WITH deleted_rows AS( DELETE FROM source_tbl RETURNING * ) INSERT INTO destination_tbl SELECT * FROM deleted_rows
支持的数据库
- DB2 LUW 7+
- Oracle 9iR2+
- PostgreSQL 8.4+
- SQL Server 2005+
- SQLite 3.8.3+
- MySQL 8.0+
WITH RECURSIVE (Common Table Expressions)
来自 SQL:1999
从一个例子来看起。
生成 1 到 3 的数字
WITH RECURSIVE cte (n) AS (SELECT 1 UNION ALL SELECT n+1 FROM cte WHERE n < 3) SELECT * FROM cte;
结果
n --- 1 2 3 (3 rows)
这个是怎么实现的呢?
首先其中的 SELECT 中有两项,由 UNION (ALL)分开: 初始项和递归项,见 SELECT 初始项 UNION ALL 递归项
初始时,由初始项产生临时表
随时,临时表做为递归项的输入表,再生产新的临时表
直到新的临时表为空时结束
这样,就形成了一个循环。
来看更多的例子
生成 2 到 4 的数字
WITH RECURSIVE cte (n) AS (SELECT 2 UNION ALL SELECT n + 1 FROM cte WHERE n < 4) SELECT * FROM cte;
结果
n --- 2 3 4 (3 rows)
生成 1,3,5 的数字
WITH RECURSIVE cte (n) AS (SELECT 1 UNION ALL SELECT n + 2 FROM cte WHERE n < 5) SELECT * FROM cte;
结果
n --- 1 3 5 (3 rows)
阶乘
再看一个求阶乘的例子
WITH RECURSIVE factorial(F,n) AS ( SELECT 1 F, 4 n UNION ALL SELECT F*n F, n-1 n FROM factorial WHERE n > 1) SELECT * FROM factorial;
结果
f | n ----+--- 1 | 4 4 | 3 12 | 2 24 | 1 (4 rows)
即 4 的阶乘为 24。
求树的子节点
构造一颗树,每一个节点有自己编号 id
, 和其父节点的编号 parent_id
建表
CREATE TABLE t ( id NUMERIC NOT NULL, parent_id NUMERIC, PRIMARY KEY (id) )
构造数据
INSERT INTO t VALUES (1, null); INSERT INTO t VALUES (2, 1); INSERT INTO t VALUES (3, 1); INSERT INTO t VALUES (4, 1); INSERT INTO t VALUES (5, 1); INSERT INTO t VALUES (6, 2); INSERT INTO t VALUES (7, 3); INSERT INTO t VALUES (8, 3); INSERT INTO t VALUES (9, 4); INSERT INTO t VALUES (10, 5); INSERT INTO t VALUES (11, 6); INSERT INTO t VALUES (12, 8); INSERT INTO t VALUES (13, 8); INSERT INTO t VALUES (14, 8); INSERT INTO t VALUES (15, 8); INSERT INTO t VALUES (16, 10);
查看一下表中的数据
mydb=# select * from t; id | parent_id ----+----------- 1 | 2 | 1 3 | 1 4 | 1 5 | 1 6 | 2 7 | 3 8 | 3 9 | 4 10 | 5 11 | 6 12 | 8 13 | 8 14 | 8 15 | 8 16 | 10 (16 rows)
现在来找到节点 3 其及所有子节点。
传统写法
可以使用多个 LEFT JOIN 来写成,同时自己的结果再进行二次处理。
SELECT d0.*, d1.*, d2.* FROM t AS d0 LEFT JOIN t AS d1 ON (d1.parent_id = d0.id) LEFT JOIN t AS d2 ON (d2.parent_id = d1.id) WHERE d0.id = 3
结果如下:
id | parent_id | id | parent_id | id | parent_id ----+-----------+----+-----------+----+----------- 3 | 1 | 8 | 3 | 12 | 8 3 | 1 | 8 | 3 | 13 | 8 3 | 1 | 8 | 3 | 14 | 8 3 | 1 | 8 | 3 | 15 | 8 3 | 1 | 7 | 3 | | (5 rows)
WITH RECURSIVE CTEs 写法
WITH RECURSIVE subtree (id, parent_id) AS ( SELECT id, parent_id FROM t WHERE id = 3 UNION ALL SELECT t.id, t.parent_id FROM subtree LEFT JOIN t ON (t.parent_id = subtree.id) WHERE t.id <= 20 ) SELECT * FROM subtree;
结果
id | parent_id ----+----------- 3 | 1 7 | 3 8 | 3 12 | 8 13 | 8 14 | 8 15 | 8 (7 rows)
小结
- SQL 中的 while 循环
- 列生成器
- 图处理器
- 可以将数据传递到下一个迭代中
- 需要有终止条件,否则容易死循环
支持的数据库
- DB2 LUV 7+
- Oracle 11.gR2+
- PostgreSQL 8.4+
- SQL Server 2005+
- SQLite 3.8.3+
- MySQL 不支持
OVER/PARTITION BY
来自 SQL:2003
先从一个场景来说起,如何实现部分收入百分比呢?
构造数据
CREATE TABLE empsalary( depname varchar, empno bigint, salary int, enroll_date date ); INSERT INTO empsalary VALUES('develop',10, 5200, '2007/08/01'); INSERT INTO empsalary VALUES('sales', 1, 5000, '2006/10/01'); INSERT INTO empsalary VALUES('personnel', 5, 3500, '2007/12/10'); INSERT INTO empsalary VALUES('sales', 4, 4800, '2007/08/08'); INSERT INTO empsalary VALUES('sales', 6, 5500, '2007/01/02'); INSERT INTO empsalary VALUES('personnel', 2, 3900, '2006/12/23'); INSERT INTO empsalary VALUES('develop', 7, 4200, '2008/01/01'); INSERT INTO empsalary VALUES('develop', 9, 4500, '2008/01/01'); INSERT INTO empsalary VALUES('sales', 3, 4800, '2007/08/01'); INSERT INTO empsalary VALUES('develop', 8, 6000, '2006/10/01'); INSERT INTO empsalary VALUES('develop', 11, 5200, '2007/08/15');
SQL:2003 之前解法
在 SQL:2003 之前
WITH total_salary_by_department AS (SELECT depname, SUM(salary) total FROM empsalary GROUP BY depname) SELECT ts.depname, empno, salary, 100 * salary/ts.total "% of dep" FROM empsalary JOIN total_salary_by_department ts ON (empsalary.depname = ts.depname) ORDER BY depname, "% of dep" desc;
结果
develop | 8 | 6000 | 23 develop | 10 | 5200 | 20 develop | 11 | 5200 | 20 develop | 9 | 4500 | 17 develop | 7 | 4200 | 16 personnel | 2 | 3900 | 52 personnel | 5 | 3500 | 47 sales | 6 | 5500 | 27 sales | 1 | 5000 | 24 sales | 3 | 4800 | 23 sales | 4 | 4800 | 23
如果只看某一个部门的呢?
WITH total_salary_by_department AS (SELECT depname, SUM(salary) total FROM empsalary GROUP BY depname) SELECT ts.depname, empno, salary, 100 * salary/ts.total "% of dep" FROM empsalary JOIN total_salary_by_department ts ON (empsalary.depname = ts.depname) WHERE empsalary.depname = 'develop' ORDER BY depname, "% of dep" desc;
结果
develop | 8 | 6000 | 23 develop | 10 | 5200 | 20 develop | 11 | 5200 | 20 develop | 9 | 4500 | 17 develop | 7 | 4200 | 16
好吧,这个又是上面提到的 PostgreSQL 的性能问题。
在 SQL:2003 以前,想实现 Aggregation 的话,只有通过 DISTINCT 和 GROUP BY 这两种方法。如果不想选项被合并的话,那就只有 GROUP BY 一种方法。
SQL:2003
在 SQL:2003 中,引入了 OVER(PARTITION BY)语法,来解法此类问题。
如上面的 SQL,可以写为:
SELECT depname, empno, salary, 100 * salary / SUM(salary) OVER(PARTITION BY depname) "% of depname" FROM empsalary ORDER BY depname, "% of depname" DESC;
结果
develop | 8 | 6000 | 23 develop | 10 | 5200 | 20 develop | 11 | 5200 | 20 develop | 9 | 4500 | 17 develop | 7 | 4200 | 16 personnel | 2 | 3900 | 52 personnel | 5 | 3500 | 47 sales | 6 | 5500 | 27 sales | 1 | 5000 | 24 sales | 3 | 4800 | 23 sales | 4 | 4800 | 23
这里先选择了 salary 列,然后选择对 salary 列做 SUM,同时通过 PARTIITON BY 关键字指定了 SUM 的窗口是针对 dep 列。
小结
- OVER 可以与任何聚合函数搭配作用
- 略
- 略
- OVER (PARTITION BY X)可以实现类似 GROUP BY 的效果
OVER/ORDER BY
收支场景:
有一张表 transactions 记录着自己的收支情况。现在需要显示收支的明细和对应的余额。
构造数据
CREATE TABLE transactions( txid int, value numeric ); INSERT INTO transactions VALUES(1, +10); INSERT INTO transactions VALUES(2, +20); INSERT INTO transactions VALUES(3, -10); INSERT INTO transactions VALUES(4, +50); INSERT INTO transactions VALUES(5, -30); INSERT INTO transactions VALUES(6, -20);
查看如下
mydb=# select * from transactions; 1 | 10 2 | 20 3 | -10 4 | 50 5 | -30 6 | -20
SQL:2003 之前
SELECT txid, value, (SELECT SUM(value) FROM transactions tx2 WHERE tx2.txid <= tx1.txid) balance FROM transactions tx1 ORDER BY txid;
结果
1 | 10 | 10 2 | 20 | 30 3 | -10 | 20 4 | 50 | 70 5 | -30 | 40 6 | -20 | 20
SQL:2003
SELECT txid, value, SUM(value) OVER(ORDER BY txid ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) balance FROM transactions tx1 ORDER BY txid;
结果
1 | 10 | 10 2 | 20 | 30 3 | -10 | 20 4 | 50 | 70 5 | -30 | 40 6 | -20 | 20
新的方法
ROW_NUMBER
排序方法
RANK
DENSE_RANK
PERCENT_RANK
CUME_DIST
支持的数据库
- DB2 LUW: 7+
- Oracle: 8i+
- PostgreSQL: 8.4+
- SQL Server: 2005+
- MySQL: 不支持
- SQLite: 不支持
OVER/LAG
SQL:2008
场景:显示与前一项的差值。和上面的场景正好相反。
SQL:2008 前
显示 ROW_NUMBER
SELECT *, ROW_NUMBER() OVER (ORDER BY txid) rn FROM transactions; 1 | 10 | 1 2 | 20 | 2 3 | -10 | 3 4 | 50 | 4 5 | -30 | 5 6 | -20 | 6
然后使用上面提到的 SQL:2003 的 WITH 方法,来求差值。
WITH numbered_data AS ( SELECT *, ROW_NUMBER() OVER (ORDER BY txid) rn FROM transactions ) SELECT cur.txid, cur.value, cur.value - prev.value FROM numbered_data cur LEFT JOIN numbered_data prev ON (cur.rn - 1 = prev.rn);
结果
1 | 10 | 2 | 20 | 10 3 | -10 | -30 4 | 50 | 60 5 | -30 | -80 6 | -20 | 10
SQL:2008
SELECT *, value - LAG(value) OVER (ORDER BY txid) FROM transactions;
结果:
1 | 10 | 2 | 20 | 10 3 | -10 | -30 4 | 50 | 60 5 | -30 | -80 6 | -20 | 10
其它
同样的还有:
LEAD / LAG FIRST_VALUE / LAST_VALUE MIN_VALUE(col, n) FROM FIRST/LAST RESPECT/IGNORE NULLS
支持的数据库
- DB2 LUW 9.5+
- Oracle: 11gR2+
- PostgreSQL 8.4+
- SQL Server 2012+
- MySQL: 不支持
FETCH FIRST
SQL:2008
场景:找到选定行的前有限列
SQL:2008 之前
SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY txid) rn FROM transactions) numbered_data WHERE rn <= 3;
结果
1 | 10 | 1 2 | 20 | 2 3 | -10 | 3
当然也可以使用非标准的语法,如 LIMIT
, TOP
。
SQL:2008 之后
SELECT * FROM transactions ORDER BY txid FETCH FIRST 3 ROWS ONLY;
结果
1 | 10 2 | 20 3 | -10
支持的数据库
- DB2 LUW 7+
- MySQL 3.19.3+
- Oracle 12c
- PostgreSQL 8.4+
- SQL Server 2012+
- SQLite 2.1.0+
OFFSET
SQL: 2011
这个很常见了,但是只是在 SQL:2011 中才引入。
支持的数据库
- DB2 LUW 9.7+
- MySQL 4.0.6+
- Oracle 12c+
- PostgreSQL 6.5+
- SQL Server 2012+
- SQLite 2.1.0+
AS OF
场景:记录数据记录的产生时间
SQL:2011 中
CREATE TABLE t( ..., start_ts TIMESTAMP(9) GENERATED ALWAYS AS ROW START, end_ts TIMESTAMP(9) GENERATED ALWAYS AS ROW END, PERIOD FOR SYSTEM TIME (start_ts, end_ts) ) WITH SYSTEM VERSIONING
写入
INSERT ... (ID, DATA) VALUES (1, 'X')
SQL:2011
其中的 start_ts
和 end_ts
会自动修改,对应用透明。
支持的数据库
- DB2 LUW 10.1+
- Oracle 10gR1+
- MySQL 不支持
- PostgreSQL 不支持
- SQL Server 不支持
- SQLite 不支持
WITHOUT OVERLAPS
场景:每行日志有起止时间,要求约束起止时间不重叠。
SQL:2011
SQL:2011 中引入了 temporal 类型和 bi-temporal 类型,如:
PRIMARY KEY (id, period WITHOUT OVERLAPS)
支持的数据库
- DB2 LUW 10.1+
- PostgreSQL 9.2+
- MySQL 不支持
- Oracle 不支持
- SQL Server 不支持
- SQLite 不支持
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于