2013-09-30 174 views
7

我有一個表可能包含三種不同的文件類型。如果存在文件類型A,則選擇A;否則,如果文件類型B存在並且沒有具有相同client_id的類型Cs,則選擇B,否則選擇類型C.
稍後會發生一些其他魔術,其將移除所選文件從桌子上。基於列值的條件選擇

我在Oracle 10g的SQL數據庫下表:

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

,併爲那些誰想要一起在家裏,sqlfidde或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); 

我希望能創造根據上述標準抓取下一個文件的一個很大的討厭的查詢,如果查詢運行四次,應該導致以下順序:

#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) 

這讓我最遠的嘗試是寫三個獨立的查詢每種類型:

--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使用的下一個選擇,但所有在一個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

感謝您的編輯,我們的目標是隻返回一行。該表格將保持較小,因此掃描整個事件不是問題。 – cazzer

+0

這樣做對未來的其他人有所幫助 - 甲骨文在所有智慧方面都決定違背常態,並將默認桶命名爲「其他」而不是「其他」 – killjoy

1

所有這些邏輯都可以真正地塞進按順序排列。這在你的SQL小提琴實例中起作用(感謝提供這個答案,如果沒有它,這個答案就不會一起出現)。你幾乎要求一個選擇*與聲明有趣的順序。按照(你的第二個條件,b不存在c的地方)來做這個命令,我們也需要一個自己的加入。

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