我正在尝试将左右部分与表格配对。该表包含列唯一部件ID,标识符和左/右指示器。我得到了一个类似question的答案,假设左边的部分总是在右边之前,但我发现事实并非如此。
目前识别一对的唯一方法是,如果它们具有相同的标识符,并且匹配的部分是之前的一部分或之后的一部分,如果右手部分具有与左手后续部分相同的标识符,那么这是正确的对,但如果左手部分与右后续部分具有相同的标识符,那么这是正确的对。例如(当前ID,标识符,是VARCHAR2)
(ID 4将为Left,ID 5将为Right)。但是,每个左右对之间有许多唯一ID,没有一对。我试图编写一个查询来查找表中的所有左对和右对。
例如(当前ID和LR都是VARCHAR2)
ID LR Identifier
1 L B15A
2 R A15C
3 L A15C
4 R A15C
5 L A15C
6 R D5A2
9 R D5A2
10 L E5A6
11 R E5A6
12 L E5A6
13 R E5A6
14 R H9S5
17 L EE5A
18 R EE5A
我需要查询返回
ID LR Identifier
2 R A15C
3 L A15C
4 R A15C
5 L A15C
10 L E5A6
11 R E5A6
12 L E5A6
13 R E5A6
17 L EE5A
18 R EE5A
上一个问题的链接是Here。问题是如果我在Right之前搜索Left(反之亦然),我将错误地匹配对。因此,查询必须找到链中的第一个标识符,然后将后续部分配对(如果它存在,无论它是右手还是左手)例如,错误的示例是:
ID LR Identifier
3 L A15C
4 R A15C
10 L E5A6
11 R E5A6
12 L E5A6
13 R E5A6
17 L EE5A
18 R EE5A
非常感谢任何帮助!提前谢谢。
答案 0 :(得分:2)
这是一个更普遍的解决方案,即使这些对不一定是彼此相邻的。 (如果事实上这是必需的,如果部件不能连续配对,那么这个条件可以添加到查询中。)
with
test_data ( id, lr, identifier ) as (
select '001', 'L', 'B15A' from dual union all
select '002', 'R', 'A15C' from dual union all
select '003', 'L', 'A15C' from dual union all
select '004', 'R', 'A15C' from dual union all
select '005', 'L', 'A15C' from dual union all
select '006', 'R', 'D5A2' from dual union all
select '009', 'R', 'D5A2' from dual union all
select '010', 'L', 'E5A6' from dual union all
select '011', 'R', 'E5A6' from dual union all
select '012', 'L', 'E5A6' from dual union all
select '013', 'R', 'E5A6' from dual union all
select '014', 'R', 'H9S5' from dual union all
select '017', 'L', 'EE5A' from dual union all
select '018', 'R', 'EE5A' from dual
)
-- end of test data, the solution (SQL query) begins below this line
select id, lr, identifier
from ( select id, lr, identifier,
row_number() over (partition by identifier, lr order by id) as rn,
least( count(case when lr = 'L' then 1 end) over (partition by identifier),
count(case when lr = 'R' then 1 end) over (partition by identifier)
) as least_count
from test_data
)
where rn <= least_count
order by id -- ORDER BY is optional
;
<强>输出强>:
ID LR IDENTIFIER
--- -- ----------
002 R A15C
003 L A15C
004 R A15C
005 L A15C
010 L E5A6
011 R E5A6
012 L E5A6
013 R E5A6
017 L EE5A
018 R EE5A
10 rows selected
说明:在内部查询中,我向初始数据添加了两列。对于每个标识符,一个rn
分别计数(从1开始并递增1),分别用于'L'和'R'。这将用于形成对。并且,ct
为每个标识符提供“L”和“R”的总计数中的最小值。在外部查询中,我只过滤掉rn > ct
中的所有行 - 这些行是初始表中没有对的行。剩下的就是对子。
ADDED :由于必须由“连续”行(由id
列测量)形成一对的附加条件,这将成为一个更有趣的问题。这是一个间隙和岛屿问题(识别具有相同特征的连续行组),但有一个扭曲:LR
值必须在组内交替,而不是常量。非常有效的“tabibitosan”方法不能在这里应用(我认为); “组的开始”方法,更通用,确实有效。这是我在这里使用的。请注意,如果组的计数是奇数,最后我会忽略组中的最后一行。 (我们可能会找到两个,四个或六个连续的行,这些行形成一对或两对或三对,但不是具有交替LR的奇数行。)另请注意,如果两行具有相同的标识符AND LR,则第二行将始终启动一个NEW组,因此如果它实际上是一对的一部分(其后为行),则此解决方案将正确捕获该行。
将此与Oracle 12及以上版本的MATCH_RECOGNIZE解决方案进行比较,我单独发布 - 并了解它有多简单!
with
prep ( id, lr, identifier, flag ) as (
select id, lr, identifier,
case when identifier = lag(identifier) over (order by id)
and lr != lag(lr) over (order by id)
then null else 1 end
from test_data -- replace "test_data" with actual table name
),
with_groups ( id, lr, identifier, gp ) as (
select id, lr, identifier,
sum(flag) over (order by id)
from prep
),
with_rn ( id, lr, identifier, rn, ct ) as (
select id, lr, identifier,
row_number() over (partition by identifier, gp order by id),
count(*) over (partition by identifier, gp)
from with_groups
)
select id, lr, identifier
from with_rn
where rn < ct or mod(rn, 2) = 0
order by id -- ORDER BY is optional
;
答案 1 :(得分:0)
为了完整起见,这是一个使用Oracle 12.1及以上MATCH_RECOGNIZE子句的答案。 (此时它对OP没有帮助,他在11.2,但它可能在将来帮助其他人。)它假定附加条件,如果在原始表中形成该对的行是连续的,则只能形成一对
select id, lr, identifier
from test_data -- replace "test_data" with the actual table name
match_recognize (
order by id
all rows per match
pattern ( A B )
define B as B.identifier = A.identifier and B.lr != A.lr
)
order by id -- ORDER BY is optional
;