這是一個更普遍適用的解決方案,即使這些配對並不一定緊挨在一起。 (如果是實際上必需的,如果部分不能配對,如果他們的ID是不連續的,這一條件可以被添加到查詢中。)
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
列測量),這成爲一個更有趣的問題。這是一個間隙和孤島問題(識別具有相同特徵的連續行的組),但有一個扭曲:值必須在組內交替,而不是恆定。非常有效的「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
;
您正在使用哪種版本的Oracle? – mathguy
另外,讓我們確保需求足夠清晰。你是說如果找到一對,那麼這兩行就會彼此相鄰?或者,一對標識符可能會在幾行之後,而不是在緊隨其後的一行? – mathguy
@mathguy是的,你的第一個陳述是正確的。如果找到一對,則具有相同標識符的零件將會是之前的一個零件號或一個零件號。我也使用Oracle 12.9.0.71 (ps感謝您以前的所有幫助!!這只是...一個永無止境的項目) – mlin