【发布时间】:2019-09-26 02:33:45
【问题描述】:
让我们在Oracle中做一个简单的查询:
SELECT
CASE.ID,
CASE.TYPE,
CASE.DATE_RAISED
FROM
CASE
WHERE
CASE.DATE_RAISED > '2019-01-01'
现在假设另一个表 EVENT 包含多个事件,这些事件可能与每个案例相关联(通过 EVENT.CASE_ID 链接)。或者根本不存在。我想报告每个案例中最早日期的未来事件 - 或者如果不存在任何内容,则返回 NULL。我可以使用 SELECT 子句中的子查询来做到这一点,如下所示:
SELECT
CASE.ID,
CASE.TYPE,
CASE.DATE_RAISED,
(
SELECT
MIN(EVENT.DATE)
FROM
EVENT
WHERE
EVENT.CASE_ID = CASE.ID
AND EVENT.DATE >= CURRENT_DATE
) AS MIN_EVENT_DATE
FROM
CASE
WHERE
CASE.DATE_RAISED > '2019-01-01'
这将返回一个像这样的表:
Case ID Case Type Date Raised Min Event Date
76 A 03/01/2019 10/05/2019
43 B 02/02/2019 [NULL]
89 A 29/01/2019 08/07/2019
90 A 04/03/2019 [NULL]
102 C 15/04/2019 20/05/2019
请注意,如果不存在任何符合条件的事件,则仍会返回该行但没有值。这是因为子查询在 SELECT 子句中。这工作得很好。
然而,我的问题是,如果我想从 EVENT 表返回多于一列 - 同时仍然保留 EVENT 表中没有匹配行的可能性。上述代码仅将 EVENT.DATE 作为单个子查询结果返回到主查询的 ONE 列。但是如果我还想返回 EVENT.ID 或 EVENT.TYPE 怎么办?同时仍然允许它们为 NULL(如果没有找到 CASE 中的匹配记录)?
我想我可以在 SELECT 子句中使用多个子查询:每个子查询只返回一列。但这似乎非常低效,因为每个子查询都基于相同的条件(CASE ID 与主查询匹配的最短日期事件;如果没有找到此类事件,则为 NULL)。
我怀疑一些漂亮的连接会是答案 - 尽管我很难理解到底是哪些。
请注意,以上示例是我实际代码的极大简化版本,其中已经包含多个“旧式”Oracle 格式的连接,例如:
WHERE
CASE.ID(+) = EVENT.CASE_ID
这是有原因的 - 因此请求任何回答这个问题的人,请您演示这种编码风格的任何解决方案,因为我的 SQL 还不够先进,无法重新考虑“更新" 样式加入现有代码。
【问题讨论】:
-
我强烈建议您停止使用“旧式”连接语法编写查询。将现有查询重构为新样式一般并不困难(除非现有 sql 中存在一些复杂怪异的连接。
-
要重构,您需要确定所涉及表的连接条件。如果您编辑问题以添加查询示例,我们可以为您重构它。 “新”(它已经存在好几年了)语法更容易理解,因为您的连接条件不再与谓词混在一起。
-
@Boneist - 我使用旧样式,因为这是我第一次学习它的地方,因为它是由 Business Objects 应用程序以那种形式生成的。我开始调整这个生成的代码并引入我自己的复杂性——同时保持我学到的相同风格。我正在使用的大多数代码以多种方式连接多个表,我无法理解如何以“新”样式执行此操作。问题中的这个最小示例可能很容易转换 - 但我不知道从哪里开始,有 10 多个表以不同的方式链接。
-
我也是第一次学习旧方法,并使用了大约 10 年。 “新方式”更好。你可以用它做比旧方法更多的事情,而且真的没那么难。但是,如果您被某个应用程序生成的 sql 卡住了,那就更难了。
-
@Boneist - 如果我要提供我正在使用的实际代码的示例(由于保护问题而更改了一些名称),您能否向我展示一个重构因素?不过,这超出了这个问题的范围,所以不确定在哪里提问(代码审查?)