PostgreSQL-04-入门篇-连接多张表

news2024/9/19 10:35:05

文章目录

    • 1. 连接
        • 设置样例表
        • PostgreSQL 左连接
        • PostgreSQL 右连接
        • PostgreSQL 全外连接
    • 2. 表别名
        • 简介
        • 表别名的实际应用
          • 1) 对长表名使用表别名,使查询更具可读性
          • 2) 在连接子句中使用表别名
          • 3) 在自连接中使用表别名
    • 3. INNER JOIN 内连接
        • 简介
        • PostgreSQL INNER JOIN 示例
          • 1) 使用 INNER JOIN 连接两个表
          • 2) 使用 INNER JOIN 连接三个表
    • 4. LEFT JOIN 左连接
        • 简介
        • PostgreSQL LEFT JOIN 示例
    • 5. 自连接
        • 简介
        • PostgreSQL 自连接示例
          • 1) 查询分层数据示例
          • 2) 比较同一个表的行
        • 概括
    • 6. 使用FULL OUTER JOIN 进行全外连接
        • 简介
        • PostgreSQL FULL OUTER JOIN 示例
    • 7. 使用CROSS JOIN 进行交叉连接
        • 简介
        • PostgreSQL CROSS JOIN 示例
    • 8. 使用NATURAL JOIN 进行自然连接
        • 简介
        • PostgreSQL NATURAL JOIN 示例
    • 9. 使用 LATERAL JOIN 进行横向连接
        • 数据表
        • 计算博客年龄
        • 使用 LATERAL JOIN 获取报表

1. 连接

设置样例表

假设您有两个名为basket_abasket_b的表,用于存储水果信息:

CREATE TABLE basket_a (
    a INT PRIMARY KEY,
    fruit_a VARCHAR (100) NOT NULL
);

CREATE TABLE basket_b (
    b INT PRIMARY KEY,
    fruit_b VARCHAR (100) NOT NULL
);

INSERT INTO basket_a (a, fruit_a)
VALUES
    (1, 'Apple'),
    (2, 'Orange'),
    (3, 'Banana'),
    (4, 'Cucumber');

INSERT INTO basket_b (b, fruit_b)
VALUES
    (1, 'Orange'),
    (2, 'Apple'),
    (3, 'Watermelon'),
    (4, 'Pear');

桌子上有一些常见的水果,例如appleorange

以下语句通过匹配fruit_afruit_b列中的值来连接第一个表 (basket_a) 和第二个表 (basket_b) :

SELECT
    a,
    fruit_a,
    b,
    fruit_b
FROM
    basket_a
INNER JOIN basket_b
    ON fruit_a = fruit_b;

内连接检查第一个表 (basket_a) 中的每一行。它将fruit_a列中的值与第二个表 (basket_b) 中每行的fruit_b列中的值进行比较。如果这些值相等,则内部联接将创建一个包含两个表中的列的新行,并将新行添加到结果集中。

下面的维恩图说明了内连接:

在这里插入图片描述

PostgreSQL 左连接

以下语句使用左连接子句将basket_a表与basket_b表连接起来。在左连接上下文中,第一个表称为左表,第二个表称为右表。

SELECT
    a,
    fruit_a,
    b,
    fruit_b
FROM
    basket_a
LEFT JOIN basket_b 
   ON fruit_a = fruit_b;

在这里插入图片描述

左连接开始从左表中查询数据。它将fruit_a列中的值与basket_b表中fruit_b列中的值进行比较。

如果这些值相等,则左联接将创建一个包含两个表的列的新行,并将新行添加到结果集中。(请参阅结果集中的第 1 行和第 2 行)。

如果值不相等,左连接还会创建一个新行,其中包含两个表中的列,并将其添加到结果集中。但是,它用NULL填充右表 (basket_b) 的列。(请参阅结果集中的第 3 行和第 4 行)。

下面的维恩图说明了左连接:

在这里插入图片描述

要从左表中查询右表中没有匹配行的行,请使用带WHERE子句的左连接。例如:

SELECT
    a,
    fruit_a,
    b,
    fruit_b
FROM
    basket_a
LEFT JOIN basket_b 
    ON fruit_a = fruit_b
WHERE b IS NULL;

输出是:

在这里插入图片描述

请注意, LEFT JOINLEFT OUTER JOIN相同,因此您可以互换使用它们。

下面的维恩图说明了左连接,它返回左表中与右表中没有匹配行的行:

在这里插入图片描述

PostgreSQL 右连接

右连接是左连接的反转版本。右连接开始从右表中选择数据。它将右表中每行的fruit_b列中的每个值与basket_a表中每行的fruit_a列中的每个值进行比较。

如果这些值相等,则右连接将创建一个新行,其中包含两个表中的列。

如果这些值不相等,右连接还会创建一个新行,其中包含两个表中的列。但是,它用NULL填充左表中的列。

以下语句使用右连接将basket_a表与basket_b表连接起来:

SELECT
    a,
    fruit_a,
    b,
    fruit_b
FROM
    basket_a
RIGHT JOIN basket_b ON fruit_a = fruit_b;

这是输出:

在这里插入图片描述

下面的维恩图说明了右连接:

在这里插入图片描述

同样,您可以通过添加WHERE子句从右表中获取与左表中没有匹配行的行,如下所示:

SELECT
    a,
    fruit_a,
    b,
    fruit_b
FROM
    basket_a
RIGHT JOIN basket_b 
   ON fruit_a = fruit_b
WHERE a IS NULL;

在这里插入图片描述

RIGHT JOINRIGHT OUTER JOIN是相同的,因此您可以互换使用它们。

下面的维恩图说明了右连接,它返回右表中的行,这些行在左表中没有匹配的行:

在这里插入图片描述

PostgreSQL 全外连接

完全外连接或完全连接返回一个结果集,其中包含左表和右表中的所有行,以及两侧的匹配行(如果有)。如果没有匹配,表的列将被填充为NULL

SELECT
    a,
    fruit_a,
    b,
    fruit_b
FROM
    basket_a
FULL OUTER JOIN basket_b 
    ON fruit_a = fruit_b;

输出:

在这里插入图片描述

下面的维恩图说明了完全外连接:

在这里插入图片描述

要返回一个表中的行,而另一个表中没有匹配的行,可以使用带有WHERE子句的完全连接,如下:

SELECT
    a,
    fruit_a,
    b,
    fruit_b
FROM
    basket_a
FULL JOIN basket_b 
   ON fruit_a = fruit_b
WHERE a IS NULL OR b IS NULL;

结果如下:

在这里插入图片描述

下面的维恩图说明了完全外连接,该连接返回一个表中的行,而另一个表中没有对应的行:

在这里插入图片描述

下图显示了我们到目前为止讨论的所有 PostgreSQL 连接的详细语法:

在这里插入图片描述

2. 表别名

简介

表别名在执行查询期间临时为表分配新名称。

下面说明了表别名的语法:

table_name AS alias_name;

在此语法中,为 table_name 分配了一个别名 alias_name。与列别名类似,AS 关键字是可选的。这意味着您可以像这样省略 AS 关键字:

表别名的实际应用

表别名有多种实际应用。

1) 对长表名使用表别名,使查询更具可读性

如果必须使用长表名限定列名,则可以使用表别名来节省一些输入并使查询更具可读性。

例如,当您在查询中使用了以下表达式:

a_very_long_table_name.column_name

您可以为表a_very_long_table_name分配一个别名,如下所示:

a_very_long_table_name AS alias

并使用表别名引用表a_very_long_table_name中的column_name

2) 在连接子句中使用表别名

通常,您经常会使用 join 子句从具有相同列名的多个表中查询数据。
如果您使用来自多个表的相同列名而没有完全限定它们,您将收到错误。
为了避免此错误,您需要使用以下语法限定这些列:
为了使查询更短,您可以使用 FROMINNER JOIN子句中列出的表名的表别名。例如:

SELECT
c.customer_id,
first_name,
amount,
payment_date
FROM
customer c
INNER JOIN payment p 
    ON p.customer_id = c.customer_id
ORDER BY 
   payment_date DESC;
3) 在自连接中使用表别名

当您将表连接到自身(也称为自连接)时,您需要使用表别名。这是因为在查询中多次引用同一个表会导致错误。

以下示例演示如何使用表别名在同一查询中两次引用employee表:

SELECT
    e.first_name employee,
    m .first_name manager
FROM
    employee e
INNER JOIN employee m 
    ON m.employee_id = e.manager_id
ORDER BY manager;

3. INNER JOIN 内连接

简介

在关系数据库中,数据通常分布在多个表中。为了查询完整的数据,经常需要从多个表中查询数据。

假设有两个表 A 和 B。表 A 有一个列pka,其值与表 B 的fka列中的值匹配。

在这里插入图片描述

要从两个表中查询数据,请在SELECT语句中使用INNER JOIN子句,如下所示:

SELECT
pka,
c1,
pkb,
c2
FROM
A
INNER JOIN B ON pka = fka;

要将表A与表B连接,请按照下列步骤操作:

  • 首先,指定要在SELECT列表子句中查询数据的两个表中的列。
  • 其次,在FROM子句中指定主表,即表A
  • 第三,在INNER JOIN子句中指定第二个表 (B) ,并在ON关键字后提供连接条件。

INNER JOIN是如何运作的?

对于表A中的每一行,内连接将pka列中的值与表B中每一行的fka列中的值进行比较:

  • 如果这些值相等,则内部联接将创建一个包含两个表的所有列的新行,并将其添加到结果集中。
  • 如果这些值不相等,内连接将忽略它们并移至下一行。

下面的维恩图说明了INNER JOIN子句的工作原理。

在这里插入图片描述

大多数时候,您想要连接的表将具有相同名称的列,例如,像customer_id这样的id列。

如果在查询中引用不同表中具有相同名称的列,则会出现错误。为了避免该错误,您需要使用以下语法完全限定这些列:

table_name.column_name
PostgreSQL INNER JOIN 示例

让我们看一些使用INNER JOIN子句的例子。

1) 使用 INNER JOIN 连接两个表

我们来看看示例数据库中的customerpayment表。

在这里插入图片描述

在这些表中,每当客户付款时,就会在payment表中插入一个新行。

每个客户可能有零次或多次付款。但是,每笔付款只属于一位客户。customer_id列建立了两个表之间的关系。

以下语句使用INNER JOIN子句从两个表中查询数据:

SELECT
customer.customer_id,
first_name,
last_name,
amount,
payment_date
FROM
customer
INNER JOIN payment 
    ON payment.customer_id = customer.customer_id
ORDER BY payment_date;

以下查询返回相同的结果。但是,它使用了表别名:

SELECT
c.customer_id,
first_name,
last_name,
email,
amount,
payment_date
FROM
customer c
INNER JOIN payment p 
    ON p.customer_id = c.customer_id
WHERE
    c.customer_id = 2;

由于两个表具有相同的customer_id列,因此您可以使用USING语法,如下:

SELECT
customer_id,
first_name,
last_name,
amount,
payment_date
FROM
customer
INNER JOIN payment USING(customer_id)
ORDER BY payment_date;
2) 使用 INNER JOIN 连接三个表

下图说明了三个表之间的关系:staffpaymentcustomer

  • 每个员工处理零笔或多笔付款。每笔付款均由一名且只有一名工作人员处理。
  • 每个客户进行零次或多次付款。每笔付款均由一位客户进行。

在这里插入图片描述

要连接三个表,请将第二个INNER JOIN子句放置在第一个INNER JOIN子句之后,查询如下:

SELECT
c.customer_id,
c.first_name customer_first_name,
c.last_name customer_last_name,
s.first_name staff_first_name,
s.last_name staff_last_name,
amount,
payment_date
FROM
customer c
INNER JOIN payment p 
    ON p.customer_id = c.customer_id
INNER JOIN staff s 
    ON p.staff_id = s.staff_id
ORDER BY payment_date;

要连接三个以上的表,可以应用相同的技术。

4. LEFT JOIN 左连接

简介

假设您有两个表:AB

A中的每一行在表B中可能有零个或多个对应的行,而表B中的每一行在表A中只有一个对应的行。

要从表A中查询在表B中可能有也可能没有对应行的数据,可以使用LEFT JOIN子句。

以下语句说明了连接表A与表BLEFT JOIN语法:

SELECT
pka,
c1,
pkb,
c2
FROM
A
LEFT JOIN B ON pka = fka;

要使用左连接将表A与表B连接起来,请执行以下步骤:

  • 首先,指定两个表中要在SELECT列表子句中查询数据的列。
  • 其次,在FROM子句中指定左表(表A)。
  • 第三,在LEFT JOIN子句中指定右表(表B),并在ON关键字后指定连接条件。

LEFT JOIN子句开始从左表中查询数据。对于左表中的每一行,它将pka列中的值与右表中每行中的fka列值进行比较。

如果这些值相等,则左连接子句将创建一个新行,其中包含SELECT列表子句中出现的列,并将该行添加到结果集中。

如果这些值不相等,左连接子句还会创建一个新行,其中包含SELECT列表子句中出现的列。此外,它还用 NULL 填充来自右表的列。

下面的维恩图说明了LEFT JOIN子句的工作原理:

在这里插入图片描述

请注意,LEFT JOIN也称为LEFT OUTER JOIN

PostgreSQL LEFT JOIN 示例

film表中的每一行在inventory表中可能有零行或多行。inventory表中的每一行在film表中有且仅有一行。

film_id列建立了filminventory表之间的链接。

以下语句使用LEFT JOIN子句将film表与inventory表连接起来:

SELECT
film.film_id,
title,
inventory_id
FROM
film
LEFT JOIN inventory 
    ON inventory.film_id = film.film_id
ORDER BY title;

在这里插入图片描述

film表中的某行在inventory表中没有匹配行时,该行的inventory_id列值为NULL

以下语句添加了一个WHERE子句来查找不在inventory表中的影片:

SELECT
film.film_id,
film.title,
inventory_id
FROM
film
LEFT JOIN inventory 
   ON inventory.film_id = film.film_id
WHERE inventory.film_id IS NULL
ORDER BY title;

如果两个表在ON子句中使用相同的列名,则可以使用USING语法,如下:

SELECT
f.film_id,
title,
inventory_id
FROM
film f
LEFT JOIN inventory i USING (film_id)
WHERE i.film_id IS NULL
ORDER BY title;

5. 自连接

简介

自连接是一种将表与其自身连接的常规连接。在实践中,您通常使用自连接来查询分层数据或比较同一表中的行。

要形成自连接,请使用不同的表别名指定同一表两次,并在ON关键字后提供连接谓词。

以下查询使用INNER JOIN将表连接到自身:

SELECT select_list
FROM table_name t1
INNER JOIN table_name t2 ON join_predicate;

在此语法中,使用INNER JOIN子句将table_name与其自身连接起来。

另外,您可以使用LEFT JOINRIGHT JOIN子句将表连接到自身,如下所示:

SELECT select_list
FROM table_name t1
LEFT JOIN table_name t2 ON join_predicate;
PostgreSQL 自连接示例

让我们举一些使用自连接的例子。

1) 查询分层数据示例

让我们设置一个示例表来进行演示。

假设,您有以下组织结构:

在这里插入图片描述

以下语句创建employee表并向表中插入一些示例数据。

CREATE TABLE employee (
employee_id INT PRIMARY KEY,
first_name VARCHAR (255) NOT NULL,
last_name VARCHAR (255) NOT NULL,
manager_id INT,
FOREIGN KEY (manager_id) 
REFERENCES employee (employee_id) 
ON DELETE CASCADE
);
INSERT INTO employee (
employee_id,
first_name,
last_name,
manager_id
)
VALUES
(1, 'Windy', 'Hays', NULL),
(2, 'Ava', 'Christensen', 1),
(3, 'Hassan', 'Conner', 1),
(4, 'Anna', 'Reeves', 2),
(5, 'Sau', 'Norman', 2),
(6, 'Kelsie', 'Hays', 3),
(7, 'Tory', 'Goff', 3),
(8, 'Salley', 'Lester', 3);

在此employee表中,manager_id列引用employee_id列。manager_id列中的值显示员工直接向其汇报的经理。当manager_id列中的值为空时,该员工不向任何人报告。换句话说,他或她是最高管理者。

以下查询使用自连接来查找谁向谁报告:

SELECT
    e.first_name || ' ' || e.last_name employee,
    m .first_name || ' ' || m .last_name manager
FROM
    employee e
INNER JOIN employee m ON m .employee_id = e.manager_id
ORDER BY manager;

在这里插入图片描述

此查询两次引用employees表,一次作为员工表,另一次作为经理表。它使用表别名e标识员工表和表别名m标识经理表。

连接谓词通过匹配employee_idmanager_id列中的值来查找员工/经理对。

请注意,最高管理者不会出现在输出中。

要将最高管理者包含在结果集中,请使用LEFT JOIN替代INNER JOIN子句,如以下查询所示:

SELECT
    e.first_name || ' ' || e.last_name employee,
    m .first_name || ' ' || m .last_name manager
FROM
    employee e
LEFT JOIN employee m ON m .employee_id = e.manager_id
ORDER BY manager;

在这里插入图片描述

2) 比较同一个表的行

请查看 DVD 租赁数据库中的film表,如下:

在这里插入图片描述

以下查询查找所有具有相同长度的电影对,

SELECT
    f1.title,
    f2.title,
    f1.length
FROM
    film f1
INNER JOIN film f2 
    ON f1.film_id <> f2.film_id AND 
       f1.length = f2.length;

在这里插入图片描述

连接谓词匹配具有相同长度 (f1.length = f2.length) 的两个不同电影 (f1.film_id <> f2.film_id)。

概括
  • PostgreSQL 自连接是一种常规连接,它使用INNER JOINLEFT JOIN将表与其自身连接。

  • 自连接对于查询分层数据或比较同一表中的行非常有用。

6. 使用FULL OUTER JOIN 进行全外连接

简介

假设您要执行表 A 和表 B 的完全外连接。以下说明了FULL OUTER JOIN语法:

SELECT * FROM A
FULL [OUTER] JOIN B on A.id = B.id;

在此语法中,OUTER关键字是可选的。

完全外连接结合了左连接和右连接的结果。

如果连接表中的行不匹配,则完全外连接会为表中没有匹配行的每一列设置 NULL 值。

如果一个表中的一行与另一个表中的行匹配,则结果行将包含由两个表中的行列填充的列。

下面的维恩图说明了FULL OUTER JOIN操作:

在这里插入图片描述

结果包括两个表中的匹配行以及不匹配的行。

PostgreSQL FULL OUTER JOIN 示例

首先,创建两个新表用于演示:employeesdepartments

DROP TABLE IF EXISTS departments;
DROP TABLE IF EXISTS employees;

CREATE TABLE departments (
department_id serial PRIMARY KEY,
department_name VARCHAR (255) NOT NULL
);

CREATE TABLE employees (
employee_id serial PRIMARY KEY,
employee_name VARCHAR (255),
department_id INTEGER
);

每个部门有零个或多个员工,每个员工属于零个或一个部门。

其次,将一些示例数据插入到departmentsemployees表中。

INSERT INTO departments (department_name)
VALUES
('Sales'),
('Marketing'),
('HR'),
('IT'),
('Production');

INSERT INTO employees (
employee_name,
department_id
)
VALUES
('Bette Nicholson', 1),
('Christian Gable', 1),
('Joe Swank', 2),
('Fred Costner', 3),
('Sandra Kilmer', 4),
('Julia Mcqueen', NULL);

第三步,使用FULL OUTER JOINemployeesdepartments表中查询数据。

SELECT
employee_name,
department_name
FROM
employees e
FULL OUTER JOIN departments d 
        ON d.department_id = e.department_id;

在这里插入图片描述

结果集包括属于某个部门的每个员工以及拥有该员工的每个部门。此外,它还包括不属于某个部门的每个员工以及没有员工的每个部门。

要查找没有任何员工的部门,请使用 WHERE子句,如下所示:

SELECT
employee_name,
department_name
FROM
employees e
FULL OUTER JOIN departments d 
        ON d.department_id = e.department_id
WHERE
employee_name IS NULL;

在这里插入图片描述

结果显示Production部门没有任何员工。

要查找不属于任何部门的员工,请检查WHERE子句中的department_name列值为NULL,如下所示:

SELECT
employee_name,
department_name
FROM
employees e
FULL OUTER JOIN departments d ON d.department_id = e.department_id
WHERE
department_name IS NULL;

在这里插入图片描述

7. 使用CROSS JOIN 进行交叉连接

简介

一个CROSS JOIN子句允许您生成两个或多个表中的行的笛卡尔积。

与其他连接子句(例如LEFT JOIN 或INNER JOIN)不同,CROSS JOIN子句没有连接谓词。

假设您必须使用CROSS JOIN连接两个表 T1 和 T2。

如果 T1 有n行且 T2 有m行,则结果集将有n * m行。例如,T1 有1,000行,T2 有1,000行,结果集将有1,000 x 1,000=1,000,000行。

下面举例说明CROSS JOIN语法的语法结构:

SELECT select_list
FROM T1
CROSS JOIN T2;

下面的语句与上面的语句是等价的:

SELECT select_list
FROM T1, T2;

此外,您可以使用一个条件始终为trueINNER JOIN子句来模拟交叉连接:

SELECT *
FROM T1
INNER JOIN T2 ON true;
PostgreSQL CROSS JOIN 示例

以下 CREATE TABLE 语句创建 T1 和 T2 表,并插入一些示例数据以进行演示。

DROP TABLE IF EXISTS T1;
CREATE TABLE T1 (label CHAR(1) PRIMARY KEY);

DROP TABLE IF EXISTS T2;
CREATE TABLE T2 (score INT PRIMARY KEY);

INSERT INTO T1 (label)
VALUES
('A'),
('B');

INSERT INTO T2 (score)
VALUES
(1),
(2),
(3);

以下语句使用CROSS JOIN运算符连接表 T1 和表 T2。

SELECT *
FROM T1
CROSS JOIN T2;
 label | score
-------+-------
 A     |     1
 B     |     1
 A     |     2
 B     |     2
 A     |     3
 B     |     3
(6 rows)

下图展示了使用CROSS JOIN将表 T1 连接到表 T2 时的结果:

在这里插入图片描述

8. 使用NATURAL JOIN 进行自然连接

简介

自然连接是一种基于连接表中相同列名创建隐式连接的连接。

下面显示了 PostgreSQL 自然连接的语法:

SELECT select_list
FROM T1
NATURAL [INNER, LEFT, RIGHT] JOIN T2;

自然连接可以是内连接、左连接或右连接。如果您没有显式指定连接,例如INNER JOIN, LEFT JOIN, RIGHT JOIN,PostgreSQL 将默认使用INNER JOIN

如果在选择列表中使用星号 (*),结果将包含以下列:

  • 所有公共列,即两个表中具有相同名称的列。
  • 两个表中的每一列,这不是公共列。
PostgreSQL NATURAL JOIN 示例

为了演示 PostgreSQL 自然连接,我们将创建两个表:categoriesproducts

以下CREATE TABLE语句创建categoriesproducts表。

DROP TABLE IF EXISTS categories;
CREATE TABLE categories (
category_id serial PRIMARY KEY,
category_name VARCHAR (255) NOT NULL
);

DROP TABLE IF EXISTS products;
CREATE TABLE products (
product_id serial PRIMARY KEY,
product_name VARCHAR (255) NOT NULL,
category_id INT NOT NULL,
FOREIGN KEY (category_id) REFERENCES categories (category_id)
);

每个类别有零个或多个产品,每个产品属于一个且仅一个类别。

products表中的category_id列是引用categories表主键的外键。category_id是我们将用来执行自然连接的公共列。

以下 INSERT语句将一些数据插入到categoriesproducts表中。

INSERT INTO categories (category_name)
VALUES
('Smart Phone'),
('Laptop'),
('Tablet');

INSERT INTO products (product_name, category_id)
VALUES
('iPhone', 1),
('Samsung Galaxy', 1),
('HP Elite', 2),
('Lenovo Thinkpad', 2),
('iPad', 3),
('Kindle Fire', 3);

以下语句使用NATURAL JOIN子句将products表与categories表连接起来:

SELECT * FROM products
NATURAL JOIN categories;

在这里插入图片描述

上面的语句等价于下面使用INNER JOIN子句的语句。

SELECT* FROM products
INNER JOIN categories USING (category_id);

NATURAL JOIN的便利之处在于它不需要您指定 join 子句,因为它使用基于公共列的隐式 join 子句。

但是,您应该尽可能避免使用NATURAL JOIN,因为有时它可能会导致意外结果。

例如,有如下两个表:

在这里插入图片描述
在这里插入图片描述

两个表具有相同的country_id列,因此您可以使用NATURAL JOIN来连接这些表,如下所示:

SELECT * 
FROM city
NATURAL JOIN country;

该查询返回一个空结果集。

原因是……

两个表还有另一个称为last_update的公共列,该列不能用于连接。但是,该NATURAL JOIN子句还是使用了last_update列。

9. 使用 LATERAL JOIN 进行横向连接

数据表

假设我们有一个 blog 表,用于存储由我们的平台托管的博客信息:
在这里插入图片描述

idcreated_ontitleurl
12024-09-30Blog 1https://xxx/blog1/
22024-01-22Blog 2https://xxx/blog2/

我们需要生成一个报表,用于从 blog 表中提取以下数据:

  • 博客 ID
  • 博客年龄(以年为单位)
  • 下一次博客周年纪念日的日期
  • 距离下一次周年纪念日还剩多少天
计算博客年龄

博客年龄通过将当前日期减去博客创建日期来计算。

下一次博客周年纪念日的日期可以通过将年龄加一并将其添加到博客创建日期来计算。

通过从下一次周年纪念日和当前日期之间的间隔中提取天数,可以计算到下一次周年纪念日的天数。

若要获取结果,可以使用以下查询:

 SELECT
  b.id AS blog_id,
  EXTRACT(YEAR FROM AGE(NOW(), b.created_on)) AS age_in_years,
  DATE(b.created_on + (EXTRACT(YEAR FROM AGE(NOW(), b.created_on)) + 1) ```sql INTERVAL '1 YEAR') AS next_anniversary,
  DATE(b.created_on + (EXTRACT(YEAR FROM AGE(NOW(), b.created_on)) + 1) ```sql INTERVAL '1 YEAR') - DATE(NOW()) AS days_to_next_anniversary
 FROM blog b
 ORDER BY blog_id;

这样,您将获得预期的结果:

blog_idage_in_yearsnext_anniversarydays_to_next_anniversary
172021-09-30295
232021-01-2244

如您所见,age_in_years 必须定义三次,因为在计算 next_anniversarydays_to_next_anniversary 值时需要它。

而这就是 LATERAL JOIN 可以帮助我们的地方。

使用 LATERAL JOIN 获取报表

LATERAL JOIN 允许我们只计算一次 age_in_years 值,并在计算 next_anniversarydays_to_next_anniversary 值时重用它。

例如,前面的 SQL 查询可以重写为:

 SELECT
  b.id AS blog_id,
  age_in_years,
  DATE(b.created_on + (age_in_years + 1) ```sql INTERVAL '1 YEAR') AS next_anniversary,
  DATE(b.created_on + (age_in_years + 1) ```sql 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_anniversarydays_to_next_anniversary 时重用。

最终结果如下:

blog_idage_in_yearsnext_anniversarydays_to_next_anniversary
172021-09-30295
232021-01-2244

LATERAL JOIN 的工作方式类似于相关的子查询,但它使得子查询的结果能够被主查询中的其他部分引用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2056313.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【机器学习】正则化,欠拟合与过拟合(详细代码与图片演示!助你迅速拿下!!!)

目录 &#x1f354;欠拟合与过拟合 1.1 欠拟合与过拟合定义 1.2 通过代码认识过拟合和欠拟合 1.3 原因以及解决办法 &#x1f354;正则化 2.1 什么是正则化 2.2 正则化类别 &#x1f354;小结 学习目标 &#x1f340; 掌握过拟合、欠拟合的概念 &#x1f340; 掌握过…

黄山黄小徽光影乐园:思特科技打造沉浸式光影乐园解决方案,快乐指数拉满了!

01      「黄小徽儿童光影乐园」是由思特科技全力打造&#xff0c;依托行业领先的数字光影技术与交互科技&#xff0c;专为3-8岁儿童设计的全场景、全交互、全沉浸的沉浸式光影乐园解决方案。    沉浸式光影乐园解决方案-黄小徽儿童光影乐园      02      思特…

无人机之固定翼无人机的组成

固定翼无人机是根据空气动力学原理设计机翼的形状&#xff0c;靠动力装置产生推力或者拉力&#xff0c;使无人机获得一定速度后&#xff0c;会导致空气在飞机上下表面的压力不同&#xff0c;进而产生升力&#xff0c;其升力主要来源于固定的机翼。大多数都是由机翼、机身、尾翼…

ultralytics实例分割mask读取

在前面学习YOLOv8的实例分割过程中&#xff0c;需要使用实例分割的数据集&#xff0c;其标签的标注格式如下&#xff1a; 实例分割勾勒轮廓 其中&#xff0c;第一个数字代表的是类别编号&#xff0c;后面的数据代表的是标注的坐标&#xff08;转换到0-1之间&#xff09;每两个…

编程修炼之Hibernate--- springboot启动初始化ddl过程与如何自定义修改 table 字段长度

文章目录 springboot启动初始化ddl过程如何自定义修改 table springboot启动初始化ddl过程 跟踪Springboot整合hibernate的启动代码&#xff1a; SessionFactoryImpl 的初始化里做了非常多的事情&#xff0c;初始化各种资源&#xff0c;并调用 SchemaManagementToolCoordinat…

c语言基础--------字符串指针

在 C 语言中&#xff0c;字符串指针是一个指向字符类型的指针&#xff0c;通常用于指向字符串的第一个字符。字符串在 C 语言中通常表示为字符数组&#xff0c;而字符串指针则是用来存储这种字符数组首地址的变量。 定义字符串指针 字符串指针的定义方式如下&#xff1a; ch…

Android更改包名和签名

一、更改包名 1、包名——鼠标右键——Refactor——Rename 修改自己想更改的包名和选择更改范围后点击Refactor就可以了 2.手动修改app的build.gradle文件中的applicationId&#xff08;改成和我们之前修改的包名相同&#xff09; 3.修改AndroidManifest.xml文件中的packag…

“AI+Security”系列第2期(三):面向LLM(大语言模型)的漏洞挖掘与对齐防御研究

近日&#xff0c;由安全极客、Wisemodel 社区和 InForSec 网络安全研究国际学术论坛联合主办的“AISecurity”系列第 2 期——对抗&#xff01;大模型自身安全的攻防博弈线上活动如期举行。 在此次活动中&#xff0c;前阿里云高级安全专家郑瀚带来了以《通往LLM安全对齐的道路…

网易云音乐故障 2 小时,这次到底谁背锅?(今天记得领补偿)

大家好&#xff0c;我是程序员鱼皮&#xff0c;8 月 19 日下午&#xff0c;网易云音乐突发严重故障&#xff0c;并登顶微博热搜&#xff0c;跟黑神话悟空抢了热度。 根据用户的反馈&#xff0c;故障的具体表现为&#xff1a;用户无法登录、歌单加载失败、播放信息获取失败、无法…

SQL— DML语句学习【后端 10】

数据库操作-DML 详解 在数据库管理系统中&#xff0c;DML&#xff08;Data Manipulation Language&#xff0c;数据操作语言&#xff09;扮演着至关重要的角色&#xff0c;它负责对数据库中的数据进行增、删、改操作。掌握DML操作对于数据库的日常维护和管理至关重要。本文将详…

CSP 2023 普及组第一轮 - CSP/S 2023初试题 完善程序第二题解析

一、题目阅读 &#xff08;编辑距离&#xff09;给定两个字符串&#xff0c;每次操作可以选择删除&#xff08;Delete&#xff09;、插入&#xff08;Insert&#xff09;、替换&#xff08;Replace&#xff09;&#xff0c;一个字符&#xff0c;求将第一个字符串转换为第二个字…

时序预测|基于贝叶斯BO-卷积-双向门控单元-注意力机制的单变量时间序列预测模型BO-CNN-BiGRU-Attention

时序预测|基于贝叶斯BO-卷积-双向门控单元-注意力机制的单变量时间序列预测模型BO-CNN-BiGRU-Attention 文章目录 前言时序预测|基于贝叶斯BO-卷积-双向门控单元-注意力机制的单变量时间序列预测模型BO-CNN-BiGRU-Attention 一、BO-CNN-BiGRU-Attention模型1. 贝叶斯优化&#…

【C++ 第十二章】二叉搜索树

1.1 二叉搜索树概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 左边小&#xff1a;若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值右边大&#xff1a;若它的右子树不为空&#xff0c;则右子树上…

并网式光伏气象站——科技百科

并网式光伏气象站的工作原理简洁而充满智慧&#xff0c;并网式光伏气象站巧妙地通过太阳能电池板将太阳能转化为电能&#xff0c;利用先进的气象监测设备&#xff0c;‌对风速、‌风向、‌温度、‌湿度、‌光照等关键气象要素进行实时监测和记录&#xff0c;不仅充分利用了太阳…

【初阶数据结构题目】32. 希尔排序

文章目录 希尔排序希尔排序的时间复杂度计算 希尔排序 希尔排序法又称缩小增量法。希尔排序法的基本思想是&#xff1a;先选定一个整数&#xff08;通常是gap n/31&#xff09;&#xff0c;把待排序文件所有记录分成各组&#xff0c;所有的距离相等的记录分在同一组内&#x…

全国10米分辨率逐年植被覆盖度(FVC)数据集

本数据集包括2017至2023年间&#xff0c;全国植被覆盖度数据&#xff0c;FVC范围值为0-1&#xff0c;数据为浮点型&#xff0c;GeoTIFF格式。GeoTIFF文件均可用ArcGIS软件和GDAL读取和打开。 植被覆盖度是指植被&#xff08;包括叶、茎、枝&#xff09;在地面的垂直投影面积占统…

系统编程-进程初步2

进程初步2 目录 进程初步2 1、进程等待清理函数&#xff08;wait&#xff09; 2、等待指定的子进程&#xff08;waitpid&#xff09; 3、新的开辟进程的函数&#xff08;vfork&#xff09; 4、在程序中运行系统下的指令&#xff08;system&#xff09; 5、exec 函数族 …

初识指针4の学习笔记

目录 1>>前言 2>>字符指针变量 3>>数组指针变量 4>>函数指针变量 5>>函数指针数组 6>>回调函数是什么&#xff1f; 7>>结语 1>>前言 今天我会继续分享一些我做的笔记&#xff0c;以及我对指针的理解&#xff0c; 后续会…

查看会议所属CCF级别(A/B/C类会议)

步骤&#xff1a; 1、打开中国计算机学会官网&#xff1a;中国计算机学会 (ccf.org.cn)。 2、搜索框中输入会议名称&#xff0c;例如&#xff1a;SIGKDD。 3、点击打开如图所示来源是“学术评价”的网页。 4、进入如下页面。 可以看到&#xff0c;SIGKDD是CCF A类会议。 参考…

低代码: 关于Test Driven Development - 测试驱动开发组件与测试示例

TDD 的开发方式 Test Driven Development - 测试驱动开发这是一种非常有意思的开发方式,我们进入一个实际场景,拿需要自研的colorpicker表单组件来说表单中的很多属性,都需要进行一个颜色的选择,如背景颜色,字体颜色等等我们来看一下相关设计交互在这里我们分两个图,点击…