2016-02-07 4 views
7

2つのテーブル間で結合を行いますが、結合条件を満たす最初の行に限定する方法はありますか?この単純な例ではSQL:最初に見つかった行の結合を制限する方法は?

、私は条件を満たしてtable_Bからの最初の行table_Aに行ごとに取得したいと思います:

select table_A.id, table_A.name, table_B.city 
from table_A join table_B 
on table_A.id = table_B.id2 
where .. 

table_A (id, name) 
1, John 
2, Marc 

table_B (id2, city) 
1, New York 
1, Toronto 
2, Boston 

The output would be: 
1, John, New York 
2, Marc, Boston 

は、Oracleのような機能を提供します(パフォーマンスが問題である)であってもよいです。

+0

selテーブル_A.id = b.id2の場合は –

+0

row_numberのサブクエリで結合し、結合条件でaddとrow_number = 1の場合 – Mihai

+0

期待される出力の例を提供する与えられたデータ(ダミーテーブルデータ)からあなたの要件を理解するのに役立ちます。 – saurav

答えて

1
select table_A.id, table_A.name, 
FIRST_VALUE(table_B.city) IGNORE NULLS 
     OVER (PARTITION BY table_B.id2 ORDER BY table_B.city) AS "city" 
from table_A join table_B 
on table_A.id = table_B.id2 
where .. 
3

あなただけの単一の値を使用する場合は、スカラー副問合せを使用することができます。

SELECT 
    id, name, (SELECT city FROM table_B WHERE id2 = table_A.id AND ROWNUM = 1) city 
FROM 
    table_A 
0

クエリ

SELECT a.id, 
     a.name, 
     b.city 
FROM table_A a 
     INNER JOIN 
     (SELECT id2, 
       city 
     FROM (
      SELECT id2, 
        city, 
        ROW_NUMBER() OVER (PARTITION BY id2 ORDER BY NULL) rn 
      FROM Table_B 
     ) 
     WHERE rn = 1 
     ) b 
     ON (a.id = b.id2) 
--WHERE ... 

出力

 ID NAME CITY 
---------- ---- -------- 
     1 John New York 
     2 Marc Boston 
3

キーワードはFIRSTです。分析機能FIRST_VALUEまたは集計構造FIRSTを使用することができます。導入12C以来

select table_A.id, table_A.name, firstFromB.city 
from table_A 
join (
    select table_B.id2, max(table_B.city) keep (dense_rank first order by table_B.city) city 
    from table_b 
    group by table_B.id2 
    ) firstFromB on firstFromB.id2 = table_A.id 
where 1=1 /* some conditions here */ 
; 

:低実行コスト、我々は余分ウィンドウの並べ替えを持っていないため、パフォーマンスが悪化し、同等FIRST_VALUEまたはLAST_VALUE構造よりも頻繁に良くなることはありませんFIRSTまたはLASTのために、結果として
オペレータLATERAL、などCROSS/OUTER APPLYJOIN句の右側に相関サブクエリを使用することを可能にし、参加する:

select table_A.id, table_A.name, firstFromB.city 
from table_A 
cross apply (
    select max(table_B.city) keep (dense_rank first order by table_B.city) city 
    from table_b 
    where table_B.id2 = table_A.id 
    ) firstFromB 
where 1=1 /* some conditions here */ 
; 
1

Oracle12cには、最終的に回避策なしであなたが求めたことを可能にする新しいcross/outer apply operatorがあります。

次はちょうど1「SYS」で始まる自分の名前を持っているユーザーが所有している(おそらく)多くのオブジェクトのためのディクショナリ・ビューに見えるの例です:Oracle 11gの前のバージョンでは

select * 
from (
     select USERNAME 
     from ALL_USERS 
     where USERNAME like 'SYS%' 
    ) U 
    cross apply (
     select OBJECT_NAME 
     from ALL_OBJECTS O 
     where O.OWNER = U.USERNAME 
      and ROWNUM = 1 
    ) 

同じ結果を得るためには、2番目のテーブルのIDに基づいて2番目のテーブルをフルスキャンするのが一般的である回避策を使うべきですが、テストのためにはlateral operator(新しいものを有効にする必要なしで12cでも利用可能です) one

-- Enables some new features 
alter session set events '22829 trace name context forever'; 

select * 
from (
     select USERNAME 
     from ALL_USERS 
     where USERNAME like 'SYS%' 
    ) U, 
    lateral (
     select OBJECT_NAME 
     from ALL_OBJECTS O 
     where O.OWNER = U.USERNAME 
      and ROWNUM = 1 
    ); 
関連する問題