数据库表
拥有以下blog 数据库表来存储我们平台托管的博客:
而且,我们目前托管了两个博客:
在不使用 SQL LATERAL JOIN 的情况下获取我们的报告
我们需要构建一个报告,从blog 表中提取以下数据:
- 博客 ID
- 博客年龄,以年为单位
- 下一个博客周年纪念日
- 距离下一个周年纪念日的剩余天数。
如果您使用的是 PostgreSQL,那么您必须执行以下 SQL 查询:
SELECT
b.id as blog_id,
extract(
YEAR FROM age(now(), b.created_on)
) AS age_in_years,
date(
created_on + (
extract(YEAR FROM age(now(), b.created_on)) + 1
) * interval '1 year'
) AS next_anniversary,
date(
created_on + (
extract(YEAR FROM age(now(), b.created_on)) + 1
) * interval '1 year'
) - date(now()) AS days_to_next_anniversary
FROM blog b
ORDER BY blog_id
如您所见,age_in_years 必须定义 3 次,因为您在计算 next_anniversary 和 days_to_next_anniversary 值时需要它。
而且,这正是 LATERAL JOIN 可以帮助我们的地方。
使用 SQL LATERAL JOIN 获取报告
以下关系数据库系统支持LATERAL JOIN 语法:
- 自 12c 以来的 Oracle
- PostgreSQL 自 9.3 起
- MySQL 自 8.0.14 起
SQL Server 可以使用CROSS APPLY 和OUTER APPLY 模拟LATERAL JOIN。
LATERAL JOIN 允许我们重用 age_in_years 值,并在计算 next_anniversary 和 days_to_next_anniversary 值时进一步传递它。
前面的查询可以改写为使用LATERAL JOIN,如下:
SELECT
b.id as blog_id,
age_in_years,
date(
created_on + (age_in_years + 1) * interval '1 year'
) AS next_anniversary,
date(
created_on + (age_in_years + 1) * interval '1 year'
) - date(now()) AS days_to_next_anniversary
FROM blog b
CROSS JOIN LATERAL (
SELECT
cast(
extract(YEAR FROM age(now(), b.created_on)) AS int
) AS age_in_years
) AS t
ORDER BY blog_id
而且,age_in_years 的值可以计算一次,并重新用于next_anniversary 和days_to_next_anniversary 计算:
| blog_id |
age_in_years |
next_anniversary |
days_to_next_anniversary |
| 1 |
7 |
2021-09-30 |
295 |
| 2 |
3 |
2021-01-22 |
44 |
好多了,对吧?
age_in_years 是针对 blog 表的每条记录计算的。因此,它的工作方式类似于关联子查询,但子查询记录与主表连接,因此,我们可以引用子查询生成的列。