如何获取多个连接行的前 N ​​行

How to get previous N rows for multiple joining rows

我正在用 Oracle Client 12 驱动程序编写 SQL。我有两个简化为附加的 table,我想得到一个具有以下逻辑的 table。 "B.TIME_B <= A0.TIME_A" 似乎创建了大量连接并使查询非常慢。请帮助找到最佳解决方案。

WITH A0 AS (
  SELECT *
  FROM A
  WHERE A.EVENT = 'a0'
)

SELECT * FROM (
SELECT
  ROW_NUMBER() OVER (PARTITION BY A0.TIME_A0 ORDER BY B.TIME_B DESC) RN, 
  A0.*,
  B.*
FROM 
   A0,B
WHERE
   B.TIME_B <= A0.TIME_A) B0

WHERE B0.RN <= 3   
  1. 查找TIME_A,其中EVENT_A = 'a0',如TIME_A0,

  2. 求TIME_B=TIME_A0,如EVENT_B0,

  3. 然后获取 table B 的行和前 2 行,其中找到 EVENT_B0。本例中的N为3,M为2,但实际情况下两者均超过3000,效率高。

Table一个

TIME_A  EVENT_A
1       a1
2       a1
3       a1
4       a0
5       a2
6       a2
7       a3
8       a0

Table B

TIME_B  EVENT_B
1       b1
2       b2
3       b3
4       b4
5       b5
6       b5
7       b6
8       b7

加入A_B

TIME_A  EVENT_A TIME_B  EVENT_B
4       a0      2       b2
4       a0      3       b3
4       a0      4       b4
8       a0      6       b5
8       a0      7       b6
8       a0      8       b7

查询 1:

如果您不打算有重叠范围,那么您可以使用:

SELECT *
FROM   (
  SELECT TIME_B,
         EVENT_B,
         MAX( TIME_A  ) OVER ( ORDER BY TIME_B ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING )
           AS TIME_A,
         MAX( EVENT_A ) OVER ( ORDER BY TIME_B ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING )
           AS EVENT_A
  FROM   tableB B
         LEFT OUTER JOIN tableA A
         ON ( B.TIME_B = A.TIME_A AND A.EVENT_A = 'a0' )
)
WHERE  TIME_A IS NOT NULL;

它只使用一个连接,然后用分析函数找到有效的行。

输出:

TIME_B | EVENT_B | TIME_A | EVENT_A
-----: | :------ | -----: | :------
     2 | b2      |      4 | a0     
     3 | b3      |      4 | a0     
     4 | b4      |      4 | a0     
     6 | b5      |      8 | a0     
     7 | b6      |      8 | a0     
     8 | b7      |      8 | a0     

db<>fiddle here


查询 2:

如果范围重叠,则可以使用分层查询来生成行:

SELECT TIME_B,
       EVENT_B,
       CONNECT_BY_ROOT( TIME_A ) AS TIME_A,
       CONNECT_BY_ROOT( EVENT_A ) AS EVENT_A
FROM   (
  SELECT A.*,
         B.*,
         ROW_NUMBER() OVER ( ORDER BY TIME_B ) AS rn
  FROM   tableB B
         LEFT OUTER JOIN tableA A
         ON ( B.TIME_B = A.TIME_A AND A.EVENT_A = 'a0' )
)
WHERE LEVEL <= 2
START WITH EVENT_A IS NOT NULL
CONNECT BY PRIOR rn -2 <= rn AND rn < PRIOR rn
ORDER BY time_a, time_b

输出:

TIME_B | EVENT_B | TIME_A | EVENT_A
-----: | :------ | -----: | :------
     2 | b2      |      4 | a0     
     3 | b3      |      4 | a0     
     4 | b4      |      4 | a0     
     6 | b5      |      8 | a0     
     7 | b6      |      8 | a0     
     8 | b7      |      8 | a0     
     8 | b7      |     10 | a0     
     9 | b8      |     10 | a0     
    10 | b9      |     10 | a0     

db<>fiddle here

这可以使用简单连接 来实现。无需使用任何功能。

如果 TIME_A 和 TIME_B 是连续的,请尝试以下代码:

WITH tableA  ( TIME_A, EVENT_A ) AS
  (SELECT  1, 'a1' FROM DUAL UNION ALL
  SELECT  2, 'a1' FROM DUAL UNION ALL
  SELECT  3, 'a1' FROM DUAL UNION ALL
  SELECT  4, 'a0' FROM DUAL UNION ALL
  SELECT  5, 'a2' FROM DUAL UNION ALL
  SELECT  6, 'a2' FROM DUAL UNION ALL
  SELECT  7, 'a3' FROM DUAL UNION ALL
  SELECT  8, 'a0' FROM DUAL),
 tableB ( TIME_B, EVENT_B ) AS
  (SELECT  1, 'b1' FROM DUAL UNION ALL
  SELECT  2, 'b2' FROM DUAL UNION ALL
  SELECT  3, 'b3' FROM DUAL UNION ALL
  SELECT  4, 'b4' FROM DUAL UNION ALL
  SELECT  5, 'b5' FROM DUAL UNION ALL
  SELECT  6, 'b5' FROM DUAL UNION ALL
  SELECT  7, 'b6' FROM DUAL UNION ALL
  SELECT  8, 'b7' FROM DUAL)
 SELECT
    TIME_A,
    EVENT_A,
    TIME_B,
    EVENT_B
FROM
    TABLEA A
    JOIN TABLEB B ON ( EVENT_A = 'a0'
                       AND TIME_B BETWEEN TIME_A - 2 AND TIME_A )
ORDER BY
    TIME_A,
    TIME_B

如果 TIME_A 和 TIME_B 不连续,请尝试以下代码:

WITH tableA  ( TIME_A, EVENT_A ) AS
  (SELECT  1, 'a1' FROM DUAL UNION ALL
  SELECT  2, 'a1' FROM DUAL UNION ALL
  SELECT  3, 'a1' FROM DUAL UNION ALL
  SELECT  4, 'a0' FROM DUAL UNION ALL
  SELECT  5, 'a2' FROM DUAL UNION ALL
  SELECT  6, 'a2' FROM DUAL UNION ALL
  SELECT  7, 'a3' FROM DUAL UNION ALL
  SELECT  8, 'a0' FROM DUAL),
 tableB ( TIME_B, EVENT_B ) AS
  (SELECT  1, 'b1' FROM DUAL UNION ALL
  SELECT  2, 'b2' FROM DUAL UNION ALL
  SELECT  3, 'b3' FROM DUAL UNION ALL
  SELECT  4, 'b4' FROM DUAL UNION ALL
  SELECT  5, 'b5' FROM DUAL UNION ALL
  SELECT  6, 'b5' FROM DUAL UNION ALL
  SELECT  7, 'b6' FROM DUAL UNION ALL
  SELECT  8, 'b7' FROM DUAL)

 SELECT
    TIME_A,
    EVENT_A,
    TIME_B,
    EVENT_B FROM
 (SELECT
    TIME_A,
    EVENT_A,
    TIME_B,
    EVENT_B,
    ROW_NUMBER() OVER (PARTITION BY TIME_A ORDER BY TIME_B DESC NULLS LAST) AS RN
FROM
    TABLEA A
    JOIN TABLEB B ON ( EVENT_A = 'a0'
                       AND TIME_B <= TIME_A ))
WHERE RN <= 3
ORDER BY
    TIME_A,
    TIME_B

DB Fiddle demo

干杯!!