了解在 SQL 查询的自联接中使用“Between”条件时的逻辑查询处理

Posted

技术标签:

【中文标题】了解在 SQL 查询的自联接中使用“Between”条件时的逻辑查询处理【英文标题】:Understand the logical query processing when 'Between' condition is used in a self join of a SQL query 【发布时间】:2020-11-01 03:12:43 【问题描述】:

我在 PostgreSQL 数据库中有以下 Orders

Order_date | Revenue
--------------------
2020-10-01 | 10

2020-10-02 | 5

2020-10-03 | 10

2020-10-04 | 5

2020-10-05 | 10

我需要返回过去 2 天内每个 order_date 的累计收入总和,包括该订单日期的收入。我正在使用以下查询

SELECT o1.order_date,  
       Sum(o2.revenue) as Revenue_sum 
FROM   orders o1  
       JOIN orders o2   
         ON o1.order_date BETWEEN o2.order_date AND o2.order_date + 2  
GROUP  BY o1.order_date 
ORDER  BY o1.order_date  

它返回以下结果

Order_date | Revenue_sum 
------------------------
2020-10-01 | 10

2020-10-02 | 15

2020-10-03 | 25

2020-10-04 | 20

2020-10-05 | 25

将按照查询处理的逻辑顺序执行以下步骤

    “JOIN”将首先通过执行交叉连接形成笛卡尔积,因此来自 o1 的每一行都将连接到 o2 的每一行。 那么'ON'子句中的限定符条件将只选择满足条件的行 从选定的行中,将通过 GROUP BY 子句 (o1.order_date) 按每个组汇总收入

根据上面的执行步骤,我正在尝试可视化查询的处理步骤。 第 1 步将是交叉连接,如下所示第 2 步将是基于“ON”条件的限定条件。我无法根据“JOIN”中指定的条件以及如何可视化从步骤 1 中选择哪些行 然后,第 3 步将对行进行分组并对收入求和

1.笛卡尔积

    o1.order_date | o2.order_date | o2.revenue
   -------------------------------------------
    2020-10-01    | 2020-10-01    | 10  
    2020-10-01    | 2020-10-02    | 5
    2020-10-01    | 2020-10-03    | 10
    2020-10-01    | 2020-10-04    | 5
    2020-10-01    | 2020-10-05    | 10
    2020-10-02    | 2020-10-01    | 10  
    2020-10-02    | 2020-10-02    | 5
    2020-10-02    | 2020-10-03    | 10
    2020-10-02    | 2020-10-04    | 5
    2020-10-02    | 2020-10-05    | 10
    2020-10-03    | 2020-10-01    | 10  
    2020-10-03    | 2020-10-02    | 5
    2020-10-03    | 2020-10-03    | 10
    2020-10-03    | 2020-10-04    | 5
    2020-10-03    | 2020-10-05    | 10
    2020-10-04    | 2020-10-01    | 10  
    2020-10-04    | 2020-10-02    | 5
    2020-10-04    | 2020-10-03    | 10
    2020-10-04    | 2020-10-04    | 5
    2020-10-04    | 2020-10-05    | 10
    2020-10-05    | 2020-10-01    | 10  
    2020-10-05    | 2020-10-02    | 5
    2020-10-05    | 2020-10-03    | 10
    2020-10-05    | 2020-10-04    | 5
    2020-10-05    | 2020-10-05    | 10

2。基于“开”条件的资格。将从上述第 1 步中选择哪些行?

【问题讨论】:

选择所有符合条件的行,分别应用于每一行。当使用较小的数据集写出来时,这可能更容易“可视化过程”,例如。 (1, 2, 3) 条件为 a.i >= b.i 和 a.i 如果您编写了查询,我看不出您在理解它的工作原理方面有什么困难。 我使用各种组合得出最终查询,但逻辑处理流程对我来说仍然不清楚。感谢这里专家的cmets和帖子,我现在清楚逻辑处理了 【参考方案1】:

执行笛卡尔积后,r1.date 的每个值都将与根据您提供的条件定义的 r2.date 范围进行比较(o1.order_date BETWEEN o2.order_date AND o2.order_date + 2) .请记住,对于 o2.order_date 的每个值,都会重新定义此日期范围。

示例: 当o1.order_date='2020-10-01'时:

它将比较 o1.order_date 是否在 '2020-10-01' 和 '2020-10-03' 之间的 o2.order_date 范围内,条件评估为 True,并且此行是从笛卡尔积中选择的。 下一次,o2.order_date 范围变为 '2020-10-02' 和 '2020-10-04',现在 order_date='2020-10-01' 不在此范围内,因此此条件计算为错误的。因此,对于 o1.order_date='2020-10-01',仅选择笛卡尔积中的 1 行(在上一步中提到)。

除非您的笛卡尔积中的所有行都已评估,否则重复上述步骤,并且只有满足给定日期范围条件的行才会被选择进入 group by 子句以汇总收入。

基于上述步骤,将选择以下行进入group-by子句:

o1.order_date | o2.order_date | o2.revenue
-------------------------------------------
2020-10-01    | 2020-10-01    | 10  
2020-10-02    | 2020-10-01    | 10  
2020-10-02    | 2020-10-02    | 5
2020-10-03    | 2020-10-01    | 10  
2020-10-03    | 2020-10-02    | 5
2020-10-03    | 2020-10-03    | 10
...

【讨论】:

【参考方案2】:

仅用于演示和演示。这并不声称是 Postgres 遵循的物理过程,但应该允许可视化。从您的笛卡尔积开始并扩展 2 列。谓词 o2.order_date+2。以及评估您的 ON 谓词的真值表(o1.order_date BETWEEN o2.order_date AND o2.order_date + 2)。然后你只选择那些具有真值结果的行。

+---------------+---------------+-----------------+-----------------------------+
| o1.order_date | o2.order_date | o2.order_date+2 | od1 >= od2 and od1 <= od2+2 |
+---------------+---------------+-----------------+-----------------------------+
| 2020-10-01    | 2020-10-01    | 2020-10-03      | true                        |
| 2020-10-01    | 2020-10-02    | 2020-10-04      | false                       |
| 2020-10-01    | 2020-10-03    | 2020-10-05      | false                       |
| 2020-10-01    | 2020-10-04    | 2020-10-06      | false                       |
| 2020-10-01    | 2020-10-05    | 2020-10-07      | false                       |
| 2020-10-02    | 2020-10-01    | 2020-10-03      | true                        |
| 2020-10-02    | 2020-10-02    | 2020-10-04      | true                        |
| 2020-10-02    | 2020-10-03    | 2020-10-05      | false                       |
| 2020-10-02    | 2020-10-04    | 2020-10-06      | false                       |
| 2020-10-02    | 2020-10-05    | 2020-10-07      | false                       |
| 2020-10-03    | 2020-10-01    | 2020-10-03      | true                        |
| 2020-10-03    | 2020-10-02    | 2020-10-04      | true                        |
| 2020-10-03    | 2020-10-03    | 2020-10-05      | true                        |
| 2020-10-03    | 2020-10-04    | 2020-10-06      | false                       |
| 2020-10-03    | 2020-10-05    | 2020-10-07      | false                       |
| 2020-10-04    | 2020-10-01    | 2020-10-03      | false                       |
| 2020-10-04    | 2020-10-02    | 2020-10-04      | true                        |
| 2020-10-04    | 2020-10-03    | 2020-10-05      | true                        |
| 2020-10-04    | 2020-10-04    | 2020-10-06      | true                        |
| 2020-10-04    | 2020-10-05    | 2020-10-07      | false                       |
| 2020-10-05    | 2020-10-01    | 2020-10-03      | false                       |
| 2020-10-05    | 2020-10-02    | 2020-10-04      | false                       |
| 2020-10-05    | 2020-10-03    | 2020-10-05      | true                        |
| 2020-10-05    | 2020-10-04    | 2020-10-06      | true                        |
| 2020-10-05    | 2020-10-05    | 2020-10-07      | true                        |
+---------------+---------------+-----------------+-----------------------------+

有结果

+---------------+---------------+-----------------+-----------------------------+
| o1.order_date | o2.order_date | o2.order_date+2 | od1 >= od2 and od1 <= od2+2 |
+---------------+---------------+-----------------+-----------------------------+
| 2020-10-01    | 2020-10-01    | 2020-10-03      | true                        |
| 2020-10-02    | 2020-10-01    | 2020-10-03      | true                        |
| 2020-10-02    | 2020-10-02    | 2020-10-04      | true                        |
| 2020-10-03    | 2020-10-01    | 2020-10-03      | true                        |
| 2020-10-03    | 2020-10-02    | 2020-10-04      | true                        |
| 2020-10-03    | 2020-10-03    | 2020-10-05      | true                        |
| 2020-10-04    | 2020-10-02    | 2020-10-04      | true                        |
| 2020-10-04    | 2020-10-03    | 2020-10-05      | true                        |
| 2020-10-04    | 2020-10-04    | 2020-10-06      | true                        |
| 2020-10-05    | 2020-10-03    | 2020-10-05      | true                        |
| 2020-10-05    | 2020-10-04    | 2020-10-06      | true                        |
| 2020-10-05    | 2020-10-05    | 2020-10-07      | true                        |
+---------------+---------------+-----------------+-----------------------------+

最后收集每个日期并对收入值求和。

【讨论】:

【参考方案3】:

假设您的日期总是按顺序排列,一次一天,您可以使用:

SELECT
    Order_date,
    SUM(Revenue) OVER (ORDER BY Order_date
                       ROWS BETWEEN 2 PRECEDING AND
                            CURRENT ROW) AS Revenue_sum
FROM orders
ORDER BY
    Order_date;

Demo

【讨论】:

这是一个很好的使用windows功能的解决方案。谢谢你。但是,我想了解的是查询中自联接的逻辑处理。在您的查询中,您已将窗口范围定义为当前行和前 2 行之间。在我的查询中,该窗口由 o2.order_date 和 o2.order_date + 2 定义。如何从步骤 1 的交叉连接表中选择此范围 @Shrestha 当然,在您的自联接中,ON 子句中的逻辑意味着每条记录都将与其自身匹配,或者与前两个日期的记录匹配。然后,您每天在联接的左表上聚合。然后,您可以将当前日期和前两个日期的收入相加。从当前查询中删除 GROUP BYSUM 以查看自联接在做什么。

以上是关于了解在 SQL 查询的自联接中使用“Between”条件时的逻辑查询处理的主要内容,如果未能解决你的问题,请参考以下文章

如何在自联接中获取唯一值以及如何在 psql 中动态获取 LIMIT 数

SQL Server - 使用内部查询自联接更新值的代码

SQL自联命名?

LINQ 查询中的自联接并返回视图

替代多个SQL自联接来转换表

SQL Server 递归自联接