了解在 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 BY
和 SUM
以查看自联接在做什么。以上是关于了解在 SQL 查询的自联接中使用“Between”条件时的逻辑查询处理的主要内容,如果未能解决你的问题,请参考以下文章