2016-05-04 12 views
1

Oracle 12c。やや複雑な例ですが、簡単に説明できます...私は別々の製品に基づいた一連のテーブルを持っているとします...私はCARSのテーブル、BOATSのテーブル、PLANESの別のテーブルなどがあります。これらのテーブルの約15です。各テーブルには、このアイテムを販売していた担当者と、Repsマネージャーとマネージャーなど、4つのレベルについて同じ列のセットがあります。Oracle SQL - Where句の作成関数

通常のクエリでは、ログオンしている人物やその人物の階層内のすべてのアクティビティを表示します。私が今やっていることは、私は各機能は、データベースを照会したいことが表示されます

select * 
from boats 
where in_hierarchy(PERSON_LOGGED_IN) = true; 

ような何かを言うことができるように、私は、データベースの機能を使用できるようにしたいと思い

enter image description here

ですそれが使用する値は、クエリの現在の行である行の値を渡されません。

階層構造のすべての列、BOATS表、CARS、PLANES、および他の表で使用できるすべての列をチェックするCOMMON関数を記述できますか?

+3

ため息..別の設計が間違っているテーブル構造。それを変更する可能性はありますか?理論的には、ただ1つのREP列が必要です。従業員/マネージャ階層は他の場所に格納する必要があります。担当者が新しいマネージャーを取得するとどうなりますか?または、スタッフの変更が一般的です - これらのテーブルを新しい従業員に更新する必要がありますか? –

+0

これはやや工夫された例ですが、貧弱なデザインに対するあなたのコメントと関連して、これには過去のデータが含まれているという理由があります。このアプリケーションは、販売時点の担当者の階層を知る必要があります。それが外部で管理されている場合(別の表でも言えば)、すべての担当者とすべての階層を何年も保持する必要があります。 – user1009073

+0

これは優れていますが、残念ながら、製品タイプを持つ1つのテーブルではなく、各製品のテーブルがあります。私はあなたが6つのパラメータ( 'Person_logged_in'と従業員レベルの5つ)を取る関数を書くことができると思うので、あなたのクエリは' select * from boats 'になるかもしれないと思いますin_hiearchy(person_logged_in、rep、mgr、mgr2、mgr3 、mgr4)= 1' –

答えて

1

それぞれのテーブルの構造が似ているとします(つまり、すべてのテーブルにrep、mgr、mgr2、mgr3、mgr4などの列があります)。それらの列がすべて存在する場合は動的にチェックし、真または偽の条件を返す関数を作成できます。

次の関数は、PERSON_LOGGED_IN(回答の目的では整数のキー値と見なされます)と、検索するテーブルのスキーマと名前(スキーマ名が含まれています異なるスキーマの重複するテーブル名は競合しません)。

次に、条件のセット(rep、mgr、mgr2など)に一致する、指定したテーブルのすべての列のリストを取得します。これは、異なる表に異なる列がある場合は、である可能性があるすべてを指定でき、表に実際に存在する列のみが照合されることになります。

一致する列が特定されたら、一致する行の数を確認して1または0を戻します。プレーンSQLはPL/SQLのようなブール値を認識しないため数値を返します。

最終的なクエリはなる:

SELECT * 
FROM BOATS 
WHERE IN_HIERARCHY(PERSON_LOGGED_IN, 'SCHEMA.BOATS') = 1 

そして、ここでの機能です:

CREATE OR REPLACE FUNCTION IN_HIERARCHY (
    PERSON_LOGGED_IN IN PLS_INTEGER, 
    TABLE_SCHEMA_AND_NAME IN VARCHAR2 
) RETURN PLS_INTEGER 
IS 
    MATCHING_COLUMNS VARCHAR2(200); 
    NUMBER_OF_ROWS_FOUND PLS_INTEGER := 0; 
BEGIN 
    select LISTAGG(COLUMN_NAME, ',') WITHIN GROUP (ORDER BY COLUMN_NAME) 
    INTO MATCHING_COLUMNS 
    from all_tab_columns 
    where OWNER = regexp_substr(TABLE_SCHEMA_AND_NAME,'[^.]+', 1, 1) 
    AND TABLE_NAME = regexp_substr(TABLE_SCHEMA_AND_NAME,'[^.]+', 1, 2) 
    and COLUMN_NAME in ('REP', 'MGR', 'MGR2', 'MGR3', 'MGR4'); 

    EXECUTE IMMEDIATE 
     'SELECT COUNT(*) 
     FROM ' || TABLE_SCHEMA_AND_NAME || ' 
     WHERE :PERSON_LOGGED_IN IN (' || MATCHING_COLUMNS || ')' 
    INTO NUMBER_OF_ROWS_FOUND 
    USING PERSON_LOGGED_IN; 

    IF NUMBER_OF_ROWS_FOUND > 0 THEN 
     RETURN 1; 
    ELSE 
     RETURN 0; 
    END IF; 
END; 
/

サイドノート:私は個人的にテーブルのリストを取得する方法を知りません。 from句を実行しているステートメントに基づいてこの関数内から削除することができますが、何らかの方法でそれを判断できる場合は、論理的にも関数からテーブル名の引数を削除することもできます。しかし、それはおそらくそれほど価値がなく、実際のwhere節はおそらく読者にとってあまり明確ではないでしょう。

@kordirkoが指摘するようにクエリを簡素化し、それをより読みやすくで本当にただ興味があるならその後、再び、、、その後、このような何かがずっと簡単になります:

SELECT * 
FROM BOATS 
WHERE PERSON_LOGGED_IN IN (REP, MGR, MGR2, MGR3, MGR4)