Как получить предыдущие N строк для нескольких строк объединения - PullRequest
0 голосов
/ 21 июня 2019

Я пишу SQL с драйвером Oracle Client 12. У меня есть две таблицы, упрощенные при добавлении, и я хочу получить таблицу со следующей логикой. «B.TIME_B <= A0.TIME_A», кажется, создал массовое объединение и сделал запрос очень медленным. Пожалуйста, помогите найти лучшее решение. </p>

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. А затем получите строку и предыдущие 2 строки таблицы 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

JOIN 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 ]

1 голос
/ 22 июня 2019

Запрос 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 здесь


Запрос 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     

дБ <> скрипка здесь

0 голосов
/ 24 июня 2019

Это может быть достигнуто с помощью простого соединения .Нет необходимости использовать какие-либо функции.

Попробуйте следующий код, если 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

Приветствия !!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...