在不借助 PL/SQL 的情况下从第二个表中查找相关值

Posted

技术标签:

【中文标题】在不借助 PL/SQL 的情况下从第二个表中查找相关值【英文标题】:Finding correlated values from second table without resorting to PL/SQL 【发布时间】:2009-04-07 18:38:35 【问题描述】:

我的数据库中有以下两个表:

a) 包含在特定日期获取的值的表格(您可以将这些视为温度读数):

sensor_id |收购 |价值 ----------+----------+-------- 1 | 2009-04-01 10:00:00 | 20 1 | 2009-04-01 10:01:00 | 21 1 | 2009-04 01 10:02:00 | 20 1 | 2009-04 01 10:09:00 | 20 1 | 2009-04 01 10:11:00 | 25 1 | 2009-04 01 10:15:00 | 30 ...

读数之间的间隔可能不同,但 (sensor_id, acquired) 的组合是唯一的。

b) 包含时间段和描述的第二个表(您可以将这些视为,例如,有人打开散热器的时间段):

sensor_id |开始日期 |结束日期 |描述 ----------+---------------------+----------------- -+------------------ 1 | 2009-04-01 10:00:00 | 2009-04-01 10:02:00 |一些描述 1 | 2009-04-01 10:10:00 | 2009-04-01 10:14:00 |别的东西

同样,时间段的长度可能不同,但任何给定传感器都不会有重叠的时间段。

对于任何传感器和任何日期范围,我都希望得到类似这样的结果:

传感器 ID |开始日期 | v1 |结束日期 | v2 |描述 ----------+----------+----+------------ ---------+----+------ 1 | 2009-04-01 10:00:00 | 20 | 2009-04-01 10:02:00 | 20 |一些描述 1 | 2009-04-01 10:10:00 | 25 | 2009-04-01 10:14:00 | 30 |一些描述

或文本来自:给定sensor_idrange_startrange_end 的日期范围, 找到与日期范围重叠的所有时间段(即start_date < range_endend_date > range_start),对于这些行中的每一行,从值表中找到时间段start_dateend_date 的对应值(用acquired > start_dateacquired > end_date 找到第一行)。

如果不是 start_valueend_value 列,这将是一个教科书般的简单示例,说明如何连接两个表。

我能否以某种方式在一条 SQL 语句中获得所需的输出,而无需编写 PL/SQL 函数来查找这些值?

除非我忽略了一些明显的东西,否则这不能用简单的子选择来完成。

数据库是 Oracle 11g,因此任何特定于 Oracle 的特性都是可以接受的。

编辑:是的,循环是可能的,但我想知道这是否可以通过单个 SQL 选择来完成。

【问题讨论】:

【参考方案1】:

你可以试试这个。不过请注意最后的警告。

SELECT
    RNG.sensor_id,
    RNG.start_date,
    RDG1.value AS v1,
    RNG.end_date,
    RDG2.value AS v2,
    RNG.description
FROM
    Ranges RNG
INNER JOIN Readings RDG1 ON
    RDG1.sensor_id = RNG.sensor_id AND
    RDG1.acquired => RNG.start_date
LEFT OUTER JOIN Readings RDG1_NE ON
    RDG1_NE.sensor_id = RDG1.sensor_id AND
    RDG1_NE.acquired >= RNG.start_date AND
    RDG1_NE.acquired < RDG1.acquired
INNER JOIN Readings RDG2 ON
    RDG2.sensor_id = RNG.sensor_id AND
    RDG2.acquired => RNG.end_date
LEFT OUTER JOIN Readings RDG1_NE ON
    RDG2_NE.sensor_id = RDG2.sensor_id AND
    RDG2_NE.acquired >= RNG.end_date AND
    RDG2_NE.acquired < RDG2.acquired
WHERE
    RDG1_NE.sensor_id IS NULL AND
    RDG2_NE.sensor_id IS NULL

这使用范围开始日期之后的第一个读数和结束日期之后的第一个读数(我个人认为使用开始和结束之前的最后一个日期会更有意义或最接近的值,但我不知道你的应用程序)。如果没有这样的阅读,那么您将一无所获。您可以将 INNER JOIN 更改为 OUTER,并根据您自己的业务规则添加额外的逻辑来处理这些情况。

【讨论】:

OUTER JOIN 的绝妙技巧,现在我为什么没有想到呢? :) 感谢您证明这是可以做到的,而且,确实,是我愚蠢。【参考方案2】:

看起来很简单。

    查找每个范围的传感器值。找到一行 - 我将调用该行的获取只是 X - 其中 X &gt; start_date 并且不存在任何其他带有 acquired &gt; start_dateacquired &lt; X 的行。对结束日期执行相同操作。

    仅选择满足查询条件的范围 - start_date 早于和 end_date 晚于查询提供的日期。

在 SQL 中就是这样。

SELECT R1.*, SV1.aquired, SV2.aquired
FROM ranges R1
INNER JOIN sensor_values SV1 ON SV1.sensor_id = R1.sensor_id
INNER JOIN sensor_values SV2 ON SV2.sensor_id = R1.sensor_id  
WHERE SV1.aquired > R1.start_date
AND NOT EXISTS (
    SELECT *
    FROM sensor_values SV3
    WHERE SV3.aquired > R1.start_date
    AND SV3.aquired < SV1.aquired)
AND SV2.aquired > R1.end_date
AND NOT EXISTS (
    SELECT *
    FROM sensor_values SV4
    WHERE SV4.aquired > R1.end_date
    AND SV4.aquired < SV2.aquired)
AND R1.start_date < @range_start
AND R1.end_date > @range_end

【讨论】:

我的错误,我没有澄清我试图在一个 SQL 查询中找到一种方法来执行此操作。否则,是的,你的想法是非常有效的,也是实现这一点的最直接的方法。 我了解到您只需要一个查询。您可以将所有内容放在一个巨大的查询中。不好,但可能。

以上是关于在不借助 PL/SQL 的情况下从第二个表中查找相关值的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL 仅使用游标将 2 个表中的数据检索到新表中

如何在不计算第二个表中的双打的情况下建立连接? [复制]

使用第一个表的输出从第二个表中选择特定数据

Excel:使用一个表中的值在第二个表中查找值

SQL LEFT JOIN 从第二个表中排除两条记录

在不使用 UTL_FILE 的情况下从 PL/SQL 中的文件读取/写入数据