2011-08-11 25 views
4

我有一個查詢,需要檢查所有字段的值都在有效代碼列表中。現在我一次又一次地調用同一個子查詢。我想抽象出子查詢,以便它更快,代碼不重複。這是有問題的查詢:如何抽象出子查詢?

select count(*) 
into cnt 
from pdv_validcodes c 
where c.code_type = 'YNNA' 
and (upper(:new.spec_1) in 
    (select code from pdv_validcodes where code_type = 'YNNA') or 
    :new.spec_1 is null) 
and (upper(:new.spec_2) in 
    (select code from pdv_validcodes where code_type = 'YNNA') or 
    :new.spec_2 is null) 
and (upper(:new.spec_3) in 
    (select code from pdv_validcodes where code_type = 'YNNA') or 
    :new.spec_3 is null) 
and (upper(:new.spec_4) in 
    (select code from pdv_validcodes where code_type = 'YNNA') or 
    :new.spec_4 is null) 
and (upper(:new.spec_5) in 
    (select code from pdv_validcodes where code_type = 'YNNA') or 
    :new.spec_5 is null) 
and (upper(:new.spec_6) in 
    (select code from pdv_validcodes where code_type = 'YNNA') or 
    :new.spec_6 is null) 
and (upper(:new.spec_7) in 
    (select code from pdv_validcodes where code_type = 'YNNA') or 
    :new.spec_7 is null) 
and (upper(:new.spec_8) in 
    (select code from pdv_validcodes where code_type = 'YNNA') or 
    :new.spec_8 is null) 
and (upper(:new.spec_9) in 
    (select code from pdv_validcodes where code_type = 'YNNA') or 
    :new.spec_9 is null) 
and (upper(:new.spec_10) in 
    (select code from pdv_validcodes where code_type = 'YNNA') or 
    :new.spec_10 is null) 
and (upper(:new.add_spec_1) in 
    (select code from pdv_validcodes where code_type = 'YNNA') or 
    :new.add_spec_1 is null) 
and (upper(:new.add_spec_2) in 
    (select code from pdv_validcodes where code_type = 'YNNA') or 
    :new.add_spec_2 is null) 
and (upper(:new.add_spec_3) in 
    (select code from pdv_validcodes where code_type = 'YNNA') or 
    :new.add_spec_3 is null) 
and (upper(:new.add_spec_4) in 
    (select code from pdv_validcodes where code_type = 'YNNA') or 
    :new.add_spec_4 is null) 
and (upper(:new.add_spec_5) in 
    (select code from pdv_validcodes where code_type = 'YNNA') or 
    :new.add_spec_5 is null); 
+2

作爲一個方面說明,這是一個非常可怕的,懶惰的數據庫設計... – Blindy

+0

這絕對是。另一組列可以達到33(在同一張表中)。儘管它來自外部,但遺憾的是沒有太多可以做到。 –

+1

如果它來自外部源並加載到數據庫中,請記住ETL中的「T」代表轉換 –

回答

3

邁克爾,

我還沒有機會測試這一點,但因爲它是觸發代碼,因此PL/SQL,東西沿此線可能工作:

CREATE OR REPLACE TYPE "strarray" AS TABLE OF VARCHAR2 (255) 
/


DECLARE 
    validcodes strarray; 
BEGIN 
    SELECT code 
    BULK COLLECT INTO validcodes 
    FROM pdv_validcodes 
    WHERE code_type = 'YNNA' 
    UNION 
    SELECT 'NULL' 
    FROM dual; 

    IF NVL(upper(:new.spec_1), 'NULL') MEMBER OF validcodes 
    AND NVL(upper(:new.spec_2), 'NULL') MEMBER OF validcodes 
    AND NVL(upper(:new.spec_3), 'NULL') MEMBER OF validcodes 
    AND NVL(upper(:new.spec_4), 'NULL') MEMBER OF validcodes 
    AND NVL(upper(:new.spec_5), 'NULL') MEMBER OF validcodes 
    AND NVL(upper(:new.spec_6), 'NULL') MEMBER OF validcodes 
    AND NVL(upper(:new.spec_7), 'NULL') MEMBER OF validcodes 
    AND NVL(upper(:new.spec_8), 'NULL') MEMBER OF validcodes 
    AND NVL(upper(:new.spec_9), 'NULL') MEMBER OF validcodes 
    AND NVL(upper(:new.spec_10), 'NULL') MEMBER OF validcodes 
    AND NVL(upper(:new.add_spec_1), 'NULL') MEMBER OF validcodes 
    AND NVL(upper(:new.add_spec_2), 'NULL') MEMBER OF validcodes 
    AND NVL(upper(:new.add_spec_3), 'NULL') MEMBER OF validcodes 
    AND NVL(upper(:new.add_spec_4), 'NULL') MEMBER OF validcodes 
    AND NVL(upper(:new.add_spec_5), 'NULL') MEMBER OF validcodes 
    THEN 
     -- Business logic 
    ELSE 
     -- Business logic 
    END IF; 
END; 
+1

如果這是經常使用的觸發器的一部分,您可能希望將嵌套表的validcodes存儲在包中,並將其填充到before-table觸發器中(因此代碼僅在每個語句中收集一次,而不是每行一次)。此外,您應該刪除「strarray」附近的引號,或者使用現有的類型。 –

+0

@jonearles。我完全同意,我一口氣寫完了,而且很晚。取決於DML的數量,每個事務在一個包中填充嵌套表格將非常有益。 – Ollie

1

可以封裝代碼在VIEW。這對性能沒有幫助,但是它會有助於可讀性和錯誤粘貼的錯誤並提高可維護性。

最好的選擇是重新設計該表。

2

根據Oracle版本的不同,可以使用WITH子句來分解子查詢。我不知道,你買太多在這種情況下,但

with valid as (
    select code 
    from pdv_validcodes 
    where code_type = 'YNNA') 
select count(*) 
into cnt 
from pdv_validcodes c 
where c.code_type = 'YNNA' 
and (upper(:new.spec_1) in 
    (select * from valid) or 
    :new.spec_1 is null) 
and (upper(:new.spec_2) in 
    (select * from valid) or 
    :new.spec_2 is null) 
and (upper(:new.spec_3) in 
    (select * from valid) or 
    :new.spec_3 is null) 
... 
+0

謝謝,我以前曾想過這件事,並希望能找到讓我多一點的東西,但它肯定比我的好。 –

1

我建議稍微不同的解決問題的方法:兌現的:new.值該列表的結果集。這將允許你像處理桌子一樣處理它。

下面的這個查詢將返回一個非零值且不匹配pdv_validcodes表中的代碼的:new.值的計數。


SELECT COUNT(1) 
    INTO cnt 
    FROM (
     SELECT q.spec 
      FROM (SELECT :new.spec_1 AS spec FROM DUAL 
        UNION ALL SELECT :new.spec_2 FROM DUAL 
        UNION ALL SELECT :new.spec_3 FROM DUAL 
        UNION ALL SELECT :new.spec_4 FROM DUAL 
        UNION ALL SELECT :new.spec_5 FROM DUAL 
        UNION ALL SELECT :new.spec_6 FROM DUAL 
        UNION ALL SELECT :new.spec_7 FROM DUAL 
        UNION ALL SELECT :new.spec_8 FROM DUAL 
        UNION ALL SELECT :new.spec_9 FROM DUAL 
        UNION ALL SELECT :new.spec_10 FROM DUAL 
        UNION ALL SELECT :new.add_spec_1 FROM DUAL 
        UNION ALL SELECT :new.add_spec_2 FROM DUAL 
        UNION ALL SELECT :new.add_spec_3 FROM DUAL 
        UNION ALL SELECT :new.add_spec_4 FROM DUAL 
        UNION ALL SELECT :new.add_spec_5 FROM DUAL 
       ) q WHERE q.spec IS NOT NULL 
     ) p 
    LEFT 
    JOIN pdv_validcodes c 
    ON c.code = UPPER(p.spec) AND c.code_type = 'YNNA' 
WHERE c.code IS NULL 

下面是它如何工作的:

首先,我們返回的:new.值列表的結果集。 (這是內聯視圖別名爲q。)

接下來,我們排除該結果集中的任何NULL值。 (這是別名爲p線視圖)。

接下來,我們加入這個結果與pdv_validcodes表中設置。 (我們只匹配給'YNNA' code_type,和我們做的匹配爲外部連接(LEFT JOIN),以便我們返回所有行從p結果集,無論它們是否匹配在pdv_validcodes代碼表。

作爲最後一步,我們排除了,我們找到了一個匹配任何行(c.codeNULL,其中來自p行沒有匹配),留給我們的:new.列表沒有匹配的值。


注:

這個查詢將返回計數爲零時,所有新的。值相匹配,並且如果有任何新的,則會返回非零計數。沒有找到匹配的值(我認爲它與原始值相反)

這可能不是執行該操作的最佳方式,但它確實消除了原始查詢中的大量冗餘代碼。

'YNNA'文字只指定一次,而每一個:new。表達式僅被指定一次。

我假設所有這些數據類型:新的。表達式是兼容的(例如,都是VARCHAR),因爲我們確實注意到它們都與代碼列進行了比較。如果它們不是,那麼在原始查詢中會進行一些隱式數據類型轉換,這可能需要在其中明確指出,以便UNION ALL操作可以正常工作。)

公用表表達式可以工作作爲(舊式)內聯視圖的替代品。

此代碼尚未測試。

1

檢查所有領域都值在有效代碼列表

東西沿着這些線路應該做

select sys.dbms_debug_vc2coll( 
     :new.spec_1 , :new.spec_2 , :new.spec_3 , :new.spec_4 , :new.spec_5 , 
     :new.spec_6 , :new.spec_7 , :new.spec_8 , :new.spec_9 , :new.spec_10, 
     :new.add_spec_1, :new.add_spec_2 , :new.add_spec_3 , :new.add_spec_4) 
     multiset except distinct 
     (select cast(collect(code) as sys.dbms_debug_vc2coll) 
      from pdv_validcodes where code_type = 'YNNA') 
from dual; 

我用sys.dbms_debug_vc2coll但你可以創建自己的集合類型[CREATE TYPE tab_char AS TABLE OF VARCHAR2(20)]

如果查詢返回除一組空值之外的任何其他值,那麼這些就是錯誤匹配值。

就個人而言,我會考慮忽略了檢查,並確保有一個在DB引用約束和簡單地使用DML錯誤記錄處理任何躲閃值。