如何获取多个连接行的前 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
查找TIME_A,其中EVENT_A = 'a0',如TIME_A0,
求TIME_B=TIME_A0,如EVENT_B0,
- 然后获取 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
干杯!!
我正在用 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
查找TIME_A,其中EVENT_A = 'a0',如TIME_A0,
求TIME_B=TIME_A0,如EVENT_B0,
- 然后获取 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
干杯!!