如何获得多个连接行的前N行

时间:2019-06-21 18:37:32

标签: sql oracle join multiple-join-rows

我正在使用Oracle Client 12驱动程序编写SQL。我有两个简化后的表,我想得到一个具有以下逻辑的表。 “ 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. 然后获取表B的行和前两行,其中找到了EVENT_B0。在此示例中,N为3,M为2,但在实际情况下,这两个数均超过3000,因此可以提高效率。

TableA

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

表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

2 个答案:

答案 0 :(得分:1)

查询1

如果范围不重叠,则可以使用:

    const myList = document.querySelector("div#wordList ul#myList") // Get the list

    function copyPaste(){
        let textAreaValue = document.querySelector("textarea#text").value //get the written text in textarea
        myList.innerHTML += `<li> ${textAreaValue} </li>` //put the "textAreaValue" in the list
    }

仅使用单个联接,然后使用解析函数查找有效行。

输出

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 <>提琴here


查询2

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

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     
     8 | b7      |     10 | a0     
     9 | b8      |     10 | a0     
    10 | b9      |     10 | a0     

db <>提琴here

答案 1 :(得分:0)

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

如果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

干杯!