2016-10-12 50 views
1

我如何(高效地)在昂貴的表格表達式上執行多個選擇?通過昂貴的表格表達式掃描多個選項

我有一個表,像這樣

CREATE TABLE facts (
    subject_id INT, 
    visit_id INT, 
    study_id INT, 
    provider_id INT, 
    variable_id INT, 
    value TEXT 
) 

即各行的結構是,具有許多尺寸和值的測量。 facts表很大,但每個維度都小得多,例如, SELECT DISTINCT subject_id FROM facts的基數可能是幾百。

現在我想找到一個事實子集的獨特維度值,即不同主體,訪問,提供者和變量study_id = X的ID。這很容易通過做多個查詢來查詢,如在

SELECT DISTINCT subject_id FROM facts WHERE study_id = X; 
SELECT DISTINCT visit_id FROM facts WHERE study_id = X; 
SELECT DISTINCT provider_id FROM facts WHERE study_id = X; 
SELECT DISTINCT variable_id FROM facts WHERE study_id = X; 

但是,每個查詢然後必須對facts表(或索引)執行單獨掃描。 (SELECT * FROM facts WHERE study_id = X的基數也很大,儘管不如整個表大)。

是否有某種方法可以組合這些查詢,以便數據庫只需對facts表執行一次掃描並收集所有不同的維度ID的一次去?

到目前爲止,我嘗試使用公共表格表達式,但仍然導致(在Postgres中)在CTE上進行多次掃描,因此它沒有幫助。 e.g:

WITH selected AS (SELECT * FROM facts WHERE study_id = X) 
SELECT DISTINCT subject_id, 1 FROM selected 
UNION ALL SELECT DISTINCT visit_id, 2 FROM selected 
UNION ALL SELECT DISTINCT provider_id, 3 FROM selected 
UNION ALL SELECT DISTINCT variable_id, 4 FROM selected 

有什麼辦法有DB只做單次掃描在facts,並收集所有需要的結果嗎?我特別感興趣的是Postgres和Oracle的支持。

+0

「*但仍然導致在CTE *多次掃描」當然,當你選擇它多次,它的作用。數據庫**有**可以多次掃描。但是,如果選擇的結果足夠小,那麼這些「掃描」將完全在內存中完成 - 如果您需要儘快更新,請在Postgres中增加「work_mem」。 '解釋(分析,詳細,緩衝)'會告訴你,如果這是在內存中完成的。 –

+1

'select array_agg(distinct subject_id)as subject_ids,array_agg(distinct visit_id)as visit_ids,... from facts where study_id = x;' - 它用於PostgreSQL – Abelisto

+0

@a_horse_with_no_name數據庫不必多次掃描,如果優化器足夠聰明。數據庫可以自由使用任何查詢策略,只要它提供了正確的答案。在我的情況下,CTE可能仍然太大,無法保存在內存中,這取決於同時發生的其他事情。 – JanKanis

回答

2

這可能是Oracle中的一種方法。

設置:

create table test(a, b, c) as (
    select 1, 2, 3 from dual union all 
    select 1, 20, 30 from dual union all 
    select 1, 2, 30 from dual union all 
    select 1, 20, 30 from dual union all 
    select 10, 2, 3 from dual union all 
    select 10, 22, 3 from dual union all 
    select 1, 20, 3 from dual 
) 

查詢:

select distinct column_name, column_value 
from test 
unpivot (column_value for column_name in (a, b, c)) 

計劃:

----------------------------------------------------------------------------- 
| Id | Operation   | Name | Rows | Bytes | Cost (%CPU)| Time  | 
----------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT  |  | 21 | 336 | 10 (10)| 00:00:01 | 
| 1 | HASH UNIQUE   |  | 21 | 336 | 10 (10)| 00:00:01 | 
|* 2 | VIEW    |  | 21 | 336 |  9 (0)| 00:00:01 | 
| 3 | UNPIVOT   |  |  |  |   |   | 
| 4 |  TABLE ACCESS FULL| TEST |  7 | 273 |  3 (0)| 00:00:01 | 
----------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    2 - filter("unpivot_view_006"."COLUMN_VALUE" IS NOT NULL) 

重sult:

C COLUMN_VALUE 
- ------------ 
B   22 
C   3 
C   30 
A   10 
A   1 
B   2 
B   20 

我做了一個非常小的表測試;這裏的計劃顯示了對錶格的一次完整掃描,但後面跟着未轉義字符和一個獨特的散列。

同一個表,與UNION的解決方案確實:

select distinct a , 'a' from test union 
select distinct b , 'b' from test union 
select distinct c , 'c' from test 

---------------------------------------------------------------------------- 
| Id | Operation   | Name | Rows | Bytes | Cost (%CPU)| Time  | 
---------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |  | 21 | 273 | 12 (75)| 00:00:01 | 
| 1 | SORT UNIQUE  |  | 21 | 273 | 12 (75)| 00:00:01 | 
| 2 | UNION-ALL   |  |  |  |   |   | 
| 3 | TABLE ACCESS FULL| TEST |  7 | 91 |  3 (0)| 00:00:01 | 
| 4 | TABLE ACCESS FULL| TEST |  7 | 91 |  3 (0)| 00:00:01 | 
| 5 | TABLE ACCESS FULL| TEST |  7 | 91 |  3 (0)| 00:00:01 | 
----------------------------------------------------------------------------