2013-09-30 17 views
7

私は、3つの異なるファイルタイプを含むテーブルを持っています。ファイルタイプAが存在する場合はAを選択し、ファイルタイプBが存在し、同じclient_idを持つタイプCがない場合はBを選択し、タイプCを選択する場合は
選択されたファイルテーブルから。列の値に基づく条件付き選択

私は、Oracleの10g SQLデータベースに次の表があります。私は作成するために期待しています

create table files (
    id varchar(8) primary key, 
    type varchar(4), 
    client_id number 
); 
insert into files values ('file1', 'A', 1); 
insert into files values ('file2', 'B', 1); 
insert into files values ('file3', 'C', 1); 
insert into files values ('file4', 'B', 2); 

ID | TYPE | CLIENT_ID 
######################## 
file1 | A | 1 
file2 | B | 1 
file3 | C | 1 
file4 | B | 2 

と自宅で一緒にフォローしたい人のために、sqlfiddeまたはSQLを上記の基準に基づいて次のファイルを取得するための大きな不快なクエリ。クエリが4回実行された場合、次の順序で結果が返されます。

#1: file1, A, 1 (grab any As first) 
#2: file4, B, 2 (grab any Bs who don't have any Cs with the same client_id) 
#3: file3, C, 1 (grab any Cs) 
#4: file2, B, 1 (same as run #2) 

私に最も遠いを得た試みが種類ごとに3つの別々のクエリを作成することでした:

--file type 'A' selector 
select * from files where type = 'A' 
--file type 'B' selector 
select * from files where type = 'B' and client_id = (
    select client_id from files group by client_id having count(*) = 1 
); 
--file type 'C' selector 
select * from files where type = 'C' 

私は行数を確認したいが、それぞれの後に返され、それが0使用である場合は、次の選択が、すべての1つのSQLステートメントで。

答えて

6

これはもう少し複雑それはおそらく必要以上に見えるけれどもあなたは、いくつかのネストされた分析を使用することができます:それは最終的な結果までを構築する方法を示す

select id, type, client_id 
from (
    select t.*, 
    case when type = 'a'then 1 
     when type = 'b' and c_count = 0 then 2 
     when type = 'c' then 3 
    end as rnk 
    from (
    select f.*, 
     sum(case when type = 'a' then 1 else 0 end) 
     over (partition by client_id) as a_count, 
     sum(case when type = 'b' then 1 else 0 end) 
     over (partition by client_id) as b_count, 
     sum(case when type = 'c' then 1 else 0 end) 
     over (partition by client_id) as c_count 
    from files f 
) t 
) 
order by rnk; 

SQL Fiddle

それとも少し良く、そして唯一の私が考える単一のレコードを引っ張って、この時間は、ループ内の最終的な目標である(?):あなたが見ることができるように

select id, type, client_id 
from (
    select t.*, 
    dense_rank() over (
     order by case when type = 'a' then 1 
     when type = 'b' and c_count = 0 then 2 
     when type = 'c' then 3 
     end, client_id) as rnk 
    from (
    select f.*, 
     sum(case when type = 'c' then 1 else 0 end) 
     over (partition by client_id) as c_count 
    from files f 
) t 
) 
where rnk = 1; 

Updated SQL Fiddleは、表示、再び作業評価された注文はあなたが求めたものです。

いずれかの方法では、この唯一の利点とすることができる、一度テーブルを打つ、しかし...これはないかもしれないが、編集のための

+0

おかげ全体をスキャンする必要があり、目標は1行のみを戻すことです。テーブルは小さいままなので、全体をスキャンすることは問題ではありません。 – cazzer

+0

これは将来的に他の誰かに役立ちます - オラクルは、すべての創意工夫が標準に反することを決め、デフォルトのバケットを「それ以外のもの」ではなく「その他」としました。 – killjoy

1

このすべてのロジックは、実際にはorder by文に詰め込むことができます。これはあなたのSQLフィドルインスタンスで動作します(それを提供してくれてありがとう、この答えはそれなしでは得られませんでした)。面白いorder byステートメントを使ってselect *を求めています。この順序を(あなたの第2の条件、bが存在しないところで)行うには、自己結合も必要です。

select f.* 
from files f 
left join files a on a.client_id = f.client_id and f.type = 'b' and a.type = 'c' 
order by 
case when f.type = 'a' then 1 
    when f.type = 'b' and a.id is null then 2 
    when f.type = 'c' then 3 
    when f.type = 'b' and a.id is not null then 4 
else 5 end 
関連する問題