如何在 LEFT JOIN ON 子句中使用子选择?

Posted

技术标签:

【中文标题】如何在 LEFT JOIN ON 子句中使用子选择?【英文标题】:How to use a subselect in a LEFT JOIN ON clause? 【发布时间】:2022-01-23 19:27:28 【问题描述】:

我有一张桌子和

ORD_DATE ORD_ID ORD_REF ORD_TYPE1 ORD_TYPE2 PRODNUM PRODQUAL PRICE
2020-09-01 101 101 ORDER ORDER 456 F 555
2020-09-02 102 101 CONF ORDER 456 F 555
2020-11-30 103 102 ORDER ORDER 123 K 444
2020-12-01 104 102 CONF ORDER 123 K 444
2020-12-01 105 103 ORDER ORDER 123 K 444
2020-12-01 106 104 ORDER ORDER 123 K 333
2020-12-02 107 104 CONF ORDER 123 K 333
2020-12-08 108 104 CONF RETURN 123 K -333
2020-12-01 109 105 ORDER ORDER 123 F 222
2020-12-02 110 105 CONF ORDER 123 F 222

和一个表:

ORD_DATE PROD_NUMBER PROD_QUAL
2020-12-01-00.00.00.000000 123 K
2020-12-01-00.00.00.000000 123 L
在表 t 中是每天的所有销售额。 销售有 2 个阶段:首先在客户购买商品时生成订单 (“订单”/“订单”)。然后它会被确认是在第二天还是通常在接下来的几天内(“CONF”/“ORDER”)。如果客户将产品退回,则视为退货(“CONF”/“RETURN”)。

表中是“二手”的产品。

如果产品在该表中,则表示表 t 中的所有销售额
ORDER_TYPE_1 = "ORDER"
AND ORDER_TYPE_2 = "ORDER"
AND t.ORD_DATE >= s.ORD_DATE
AND t.PROD_NUMBER = s.PROD_NUMBER
AND t.PROD_QUAL = s.PROD_QUAL

算作“二手”。

我需要从 2021 年和第 12 个月确认的所有“二手”销售额的总和。但只有带有 CONF/ORDER 或 CONF/RETURN 的行应该在计算中。为此,我在表 t 中有 CAL_YEAR 和 CAL_MONTH(为减少混乱而省略)。

从表 t 中,只有 ORDER_REF 105 匹配,总和将为 0,因为只有这 2 行很重要:

| 2020-12-02 | 107    | 104     | CONF      | ORDER     | 123     | K        | 333
| 2020-12-08 | 108    | 104     | CONF      | RETURN    | 123     | K        | -333

到目前为止我的代码:

SELECT SUM(PRICE)
FROM t
--
LEFT JOIN s
ON t.PRODNUM = s.PRODNUM
AND t.PRODQUAL = s.PRODQUAL
AND (SELECT ORD_DATE FROM t WHERE ORDER_TYPE_1 = 'ORDER' AND ORDER_TYPE_2 = 'ORDER') >= s.ORD_DATE
--
WHERE CAL_YEAR = 2021
AND CAL_MONTH = 12
AND ORDER_TYPE_1 = 'CONF'
AND ORDER_TYPE_2 IN ('ORDER', 'RETURN')
--
GROUP BY PRICE
;

SQL 错误:“单行子查询返回多行

我的问题是将 LEFT JOIN 限制为 ORDER/ORDER(以便 ORDER_REF 105 进入),但仅使用 CONF/ORDER 和 CONF/RETURN 作为总和(以便 ORDER_REF 102 退出)。

有人可以帮忙吗?

【问题讨论】:

连接条件中是否需要子查询不清楚...不能直接说t.order_type_1 = 'ORDER'...吗?但我认为我更大的问题是,如果t 拥有您需要的所有信息,您为什么还要加入s 当产品开始作为“二手”出售时,没有任何信息。该信息仅在表 s 中。 啊,我的错。感谢您的澄清。 (当我读到“如果产品在该表中,则表示表 t 中的所有销售额”,我认为它的意思是“s 是……表 t 中的所有销售额”。)我仍然不清楚你为什么想要一个但是,连接条件中的子查询,而不是引用您正在加入的 t 请edit 将minimal reproducible example 包含在您的示例数据的预期输出中。 改为加入子查询, 【参考方案1】:

我能想到的最简单的方法是进行自联接,您可以在其中加入一个 表 t 的第二个副本,别名为 t2,用于 CONF/ORDER 和 CONF/RETURN行,而您对 ORDER/ORDER 行使用 t

SELECT SUM(t2.PRICE)
FROM t
--
INNER JOIN t t2
ON t2.ORD_REF = t.ORD_REF
AND t2.ORDER_TYPE_1 = 'CONF'
AND t2.ORDER_TYPE_2 IN ('ORDER', 'RETURN')
-- 
LEFT JOIN s
ON t.PRODNUM = s.PRODNUM
AND t.PRODQUAL = s.PRODQUAL
AND t.ORD_DATE >= s.ORD_DATE
--
WHERE t.CAL_YEAR = 2021
AND t.CAL_MONTH = 12
AND t.ORDER_TYPE_1 = 'ORDER'
AND t.ORDER_TYPE_2 = 'ORDER'
;

如果您需要提高效率,您可以使用分析/窗口函数将总价格从 CONF 行拉入 ORDER/ORDER 行作为新列。这样它只会查询表t一次而不是两次。

SELECT SUM(t2.order_price_sum)
FROM (select t.*, 
            sum(case when ORDER_TYPE_1 = 'CONF' 
                          AND ORDER_TYPE_2 IN ('ORDER', 'RETURN') 
                     then t.price 
                     else 0 end) over (partition by ord_ref) as order_price_sum
            from t) t2
--
LEFT JOIN s
ON t2.PRODNUM = s.PRODNUM
AND t2.PRODQUAL = s.PRODQUAL
AND t2.ord_date >= s.ORD_DATE
--
WHERE CAL_YEAR = 2021
AND CAL_MONTH = 12
AND ORDER_TYPE_1 = 'ORDER'
AND ORDER_TYPE_2 = 'ORDER'
;

【讨论】:

以上是关于如何在 LEFT JOIN ON 子句中使用子选择?的主要内容,如果未能解决你的问题,请参考以下文章

hive 的 left semi join

为啥以及何时在 WHERE 子句中带有条件的 LEFT JOIN 不等于在 ON 中的相同 LEFT JOIN? [复制]

不支持 Left Join On And 子句

关于在left join的on子句中限制左边表的取值时出现非期望的结果

带有 ON 子句或替代方法的休眠 LEFT JOIN FETCH

Impala:AnalysisException:LEFT OUTER JOIN 需要 ON 或 USING 子句