web-dev-qa-db-ja.com

外部のWHERE句を無視するOracleビュー

良い一日、

これは私を困らせました。マテリアライズされていないOracleビューに格納したい、やや厄介な開発者クエリがあります。ビュー自体のテキストはここにリストするには少し長くなりますが、うまくコンパイルされ、正しい結果セットが生成されます。ビュークエリ自体は、CTEと2つの手動アンピボット(UNPIVOT演算子はOracle 10では使用できません)およびかなりの数のUNIONSを使用します。アイデアは、各CTEの「中間」クエリを繰り返し使用して、他の方法では1つのクエリでは合理的に不可能であるさまざまな集計を抽出することです(DBデザインが貧弱で、残念ながら自分では制御できません)。必要な集計は数多くありますが、これは、速度、保守性、読みやすさ、自己文書化コードの観点から考え出した最高のソリューションです。

さて、このビューは、単純な

SELECT *
  FROM myView;

ただし、結果をフィルタリングしようとすると、WHERE句が無視されているように見えます。の線に沿った何か

SELECT *
  FROM myView
 WHERE DATA_TYPE = 3;  --  <== There is no DATA_TYPE = 3 in the result set

何も返さないはずですが、すべての行を返します。他の列のいずれかを含む他の述語も無視されているように見えます。これを引き起こしている可能性のあるアイデアは何ですか?上記のSELECTは問題なく実行され、エラーは発生しません。

参考までに、DBMSはOracle 10gです。

よろしくお願いいたします。

編集:

同じ動作を示すビュークエリの縮小バージョンは次のとおりです(CTEの内部と外部の両方に多くのクエリがあり、コンパクトにするために2つの内部と1つの外部しか含めていません)。

CREATE OR REPLACE VIEW FSA.FSA_V_DB_TOTALS
(
    DATA_CATEGORY, DATA_TYPE, HUMAN_STRING, ELEMENT_NAME, ELEMENT_VAL,
    CLASS_CD, REGION, CNTY, DIST, TRA, FRAN, PROP
)
AS
WITH
--Queries to generate intermediate result sets
--Distribution TRA
DIST_TRA AS (
SELECT 'DISTRIBUTION - TRA' AS DATA_CATEGORY,
       1 AS DATA_TYPE,
       CLASS_CD AS CLASS_CD,
       NULL AS REGION,
       CNTY_CD AS CNTY,
       NULL AS DIST,
       TRA_CD AS TRA,
       NULL AS FRAN,
       NULL AS PROP,
       TEXT AS HUMAN_STRING,
       DECODE(UNPIVOT_ROW, 1, 'CIRCUIT MILEAGE',
                           2, 'WIRE MILEAGE',
                           3, 'DUCT MILEAGE',
                           'N/A') AS ELEMENT_NAME,
       DECODE(UNPIVOT_ROW, 1, CIRCT_FT,
                           2, WIRE_FT,
                           3, DUCT_FT,
                           'N/A') AS ELEMENT_VAL
  FROM (
       SELECT T1.CLASS_CD AS CLASS_CD,
              T1.CNTY_CD AS CNTY_CD,
              T2.TRA_CD AS TRA_CD,
              DECODE(COALESCE(T2.TRA_CD,
                              T1.CNTY_CD,
                              T1.CLASS_CD), NULL, 'DISTRIBUTION TRA TOTAL',
                                            T2.TRA_CD, 'TRA ' || T2.TRA_CD || ' TOTAL',
                                            T1.CNTY_CD, 'COUNTY ' || T1.CNTY_CD || ' TOTAL',
                                            T1.CLASS_CD, 'CLASS ' || T1.CLASS_CD || ' TOTAL') AS TEXT,
              SUM(T2.CIRCT_FT_QTY) AS CIRCT_FT,
              SUM(T2.WIRE_FT_QTY) AS WIRE_FT,
              SUM(T2.DUCT_FT_QTY) AS DUCT_FT
         FROM FSA.FSA001_DISTRIBUT T1
              INNER JOIN
              FSA.FSA002_DIST_TRA T2 ON T1.SERIAL_NO = T2.SERIAL_NO
        GROUP BY GROUPING SETS ((T1.CLASS_CD, T1.CNTY_CD, T2.TRA_CD),
                                (T1.CLASS_CD, T1.CNTY_CD),
                                (T1.CLASS_CD),
                                ())
        ),(
        SELECT level AS UNPIVOT_ROW FROM DUAL CONNECT BY level <= 3
        )
),

--Distribution Fran
DIST_FRAN AS (
SELECT 'DISTRIBUTION - FRAN' AS DATA_CATEGORY,
       2 AS DATA_TYPE,
       CLASS_CD AS CLASS_CD,
       NULL AS REGION,
       CNTY_CD AS CNTY,
       DIST_CD AS DIST,
       NULL AS TRA,
       FRAN_CD AS FRAN,
       PROP_CD AS PROP,
       TEXT AS HUMAN_STRING,
       DECODE(UNPIVOT_ROW, 1, 'POLE MILEAGE',
                           2, 'ST LIGHT MILEAGE',
                           'N/A') AS ELEMENT_NAME,
       DECODE(UNPIVOT_ROW, 1, POLE_FT,
                           2, SL_FT,
                           'N/A') AS ELEMENT_VAL
  FROM (
       SELECT T1.CLASS_CD AS CLASS_CD,
              T1.CNTY_CD AS CNTY_CD,
              T1.DIST_CD AS DIST_CD,
              T3.FRNCHSE_CD AS FRAN_CD,
              T3.PROPRTY_CD AS PROP_CD,
              DECODE(COALESCE(T3.PROPRTY_CD,
                              T3.FRNCHSE_CD,
                              T1.DIST_CD,
                              T1.CNTY_CD,
                              T1.CLASS_CD), NULL, 'DISTRIBUTION FRAN TOTAL',
                                            T3.PROPRTY_CD, 'PROPERTY ' || T3.PROPRTY_CD     || ' TOTAL',
                                            T3.FRNCHSE_CD, 'FRAN ' || T3.FRNCHSE_CD || ' TOTAL',
                                            T1.DIST_CD, 'DISTRICT ' || T1.DIST_CD || ' TOTAL',
                                            T1.CNTY_CD, 'COUNTY ' || T1.CNTY_CD || ' TOTAL',
                                            T1.CLASS_CD, 'CLASS ' || T1.CLASS_CD || ' TOTAL') AS TEXT,
              SUM(T3.POLE_FT_QTY) AS POLE_FT,
              SUM(T3.ST_LIGHT_FT_QTY) AS SL_FT
         FROM FSA.FSA001_DISTRIBUT T1
              INNER JOIN
              FSA.FSA003_DIST_FRAN T3 ON T1.SERIAL_NO = T3.SERIAL_NO
        GROUP BY GROUPING SETS ((T1.CLASS_CD, T1.CNTY_CD, T1.DIST_CD, T3.FRNCHSE_CD,     T3.PROPRTY_CD),
                                (T1.CLASS_CD, T1.CNTY_CD, T1.DIST_CD, T3.FRNCHSE_CD),
                                (T1.CLASS_CD, T1.CNTY_CD, T1.DIST_CD),
                                (T1.CLASS_CD, T1.CNTY_CD),
                                (T1.CLASS_CD),
                                ())
        ),(
        SELECT level AS UNPIVOT_ROW FROM DUAL CONNECT BY level <= 2
        )
)

--Queries to generate final result set based on CTE intermediate queries
--Subtotals / System totals for Dist-TRA, Dist-Fran, Trans-TRA, Trans-Fran
SELECT DATA_CATEGORY, DATA_TYPE, HUMAN_STRING, ELEMENT_NAME, ELEMENT_VAL,
       CLASS_CD, REGION, CNTY, DIST, TRA, FRAN, PROP
  FROM (SELECT * FROM DIST_TRA
        UNION ALL
        SELECT * FROM DIST_FRAN)

 ORDER BY DATA_CATEGORY, CLASS_CD, REGION, CNTY, DIST, NVL2(TRA, TRA, FRAN), ELEMENT_NAME, HUMAN_STRING
/

編集2:

述語付きのビューから選択するための完全な説明計画。 DBMSはトップレベルのSELECTに述語を表示しないことに注意してください。

SQL> SELECT * FROM FSA.FSA_V_DB_TOTALS WHERE DATA_TYPE = 3;

Execution Plan
----------------------------------------------------------
Plan hash value: 3417880006

---------------------------------------------------------------------------------------------------------------
| Id  | Operation                          | Name             | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                   |                  | 88095 |  6194K|       |  5232   (3)| 00:01:03 |
|   1 |  VIEW                              | FSA_V_DB_TOTALS  | 88095 |  6194K|       |  5232   (3)| 00:01:03 |
|   2 |   SORT ORDER BY                    |                  | 88095 |  6194K|    17M|  5232   (3)| 00:01:03 |
|   3 |    VIEW                            |                  | 88095 |  6194K|       |  3721   (4)| 00:00:45 |
|   4 |     UNION-ALL                      |                  |       |       |       |            |          |
|   5 |      MERGE JOIN CARTESIAN          |                  |  1995 |   150K|       |  1492   (6)| 00:00:18 |
|   6 |       VIEW                         |                  |     1 |    13 |       |     2   (0)| 00:00:01 |
|*  7 |        CONNECT BY WITHOUT FILTERING|                  |       |       |       |            |          |
|   8 |         FAST DUAL                  |                  |     1 |       |       |     2   (0)| 00:00:01 |
|   9 |       BUFFER SORT                  |                  |  1995 |   124K|       |  1492   (6)| 00:00:18 |
|  10 |        VIEW                        |                  |  1995 |   124K|       |  1490   (6)| 00:00:18 |
|  11 |         SORT GROUP BY ROLLUP       |                  |  1995 | 59850 |       |  1490   (6)| 00:00:18 |
|* 12 |          HASH JOIN                 |                  |   408K|    11M|  3280K|  1447   (3)| 00:00:18 |
|  13 |           TABLE ACCESS FULL        | FSA001_DISTRIBUT |   152K|  1488K|       |   292   (2)| 00:00:04 |
|  14 |           TABLE ACCESS FULL        | FSA002_DIST_TRA  |   408K|  7980K|       |   362   (5)| 00:00:05 |
|  15 |      MERGE JOIN CARTESIAN          |                  | 86100 |  5969K|       |  2229   (3)| 00:00:27 |
|  16 |       VIEW                         |                  |     1 |    13 |       |     2   (0)| 00:00:01 |
|* 17 |        CONNECT BY WITHOUT FILTERING|                  |       |       |       |            |          |
|  18 |         FAST DUAL                  |                  |     1 |       |       |     2   (0)| 00:00:01 |
|  19 |       BUFFER SORT                  |                  | 86100 |  4876K|       |  2229   (3)| 00:00:27 |
|  20 |        VIEW                        |                  | 86100 |  4876K|       |  2227   (3)| 00:00:27 |
|  21 |         SORT GROUP BY ROLLUP       |                  | 86100 |  2438K|    14M|  2227   (3)| 00:00:27 |
|* 22 |          HASH JOIN                 |                  |   267K|  7582K|  3728K|  1006   (3)| 00:00:13 |
|  23 |           TABLE ACCESS FULL        | FSA001_DISTRIBUT |   152K|  1935K|       |   293   (3)| 00:00:04 |
|  24 |           TABLE ACCESS FULL        | FSA003_DIST_FRAN |   267K|  4183K|       |   166   (5)| 00:00:02 |
---------------------------------------------------------------------------------------------------------------

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

   7 - filter(LEVEL<=3)
  12 - access("T2"."SERIAL_NO"="T1"."SERIAL_NO")
  17 - filter(LEVEL<=2)
  22 - access("T1"."SERIAL_NO"="T3"."SERIAL_NO")

SQL>
5
MysteryMoose

少し試行錯誤して、犯人を見つけることができました。結局のところ、ビュー自体とは何の関係もありません。 where句をクエリに直接手動でプッシュしようとしても、同じ結果が得られました。

結局のところ、問題は内部クエリのCONNECT BY句が原因でした。私の疑い(これは完全に間違っている可能性があります)は、Oracle 10gが再帰的なCTEを処理できないという事実に関係しています。なぜこれが起こったのかについて誰か他の情報があれば、私はすべて耳にした。

私は移動することで問題を回避することができました

SELECT level AS UNPIVOT_ROW FROM DUAL CONNECT BY level <= 3

独自のCTEブロックにサブクエリを実行し、通常どおりそれから選択します。以下に、上記の壊れたクエリの(圧縮)作業バージョンを含めました。

CREATE OR REPLACE VIEW FSA.FSA_V_DB_TOTALS_2
(...)
AS
WITH
--Queries to generate intermediate result sets

--'NUMBERS' CTE block to work around Oracle 10g limitation wherein WHERE
--  clause is ignored if CONNECY BY is directly written into inner queries.
--  NOTE: the size limitation (currently 10) only needs to be larger than
--        or equal to the largest value needed.
NUMBERS AS (
SELECT level AS UNPIVOT_ROW
  FROM DUAL CONNECT BY level <=10
),

--Distribution TRA
DIST_TRA AS (
SELECT ...
  FROM (
       SELECT ...
         FROM FSA.FSA001_DISTRIBUT T1
              INNER JOIN
              FSA.FSA002_DIST_TRA T2 ON T1.SERIAL_NO = T2.SERIAL_NO
        GROUP BY GROUPING SETS (...)
        ),(
        SELECT UNPIVOT_ROW FROM NUMBERS WHERE UNPIVOT_ROW <= 3      -- <==  THIS
        )
),

--Distribution Fran
DIST_FRAN AS (
SELECT ...
  FROM (
       SELECT ...
         FROM FSA.FSA001_DISTRIBUT T1
              INNER JOIN
              FSA.FSA003_DIST_FRAN T3 ON T1.SERIAL_NO = T3.SERIAL_NO
        GROUP BY GROUPING SETS (...)
        ),(
        SELECT UNPIVOT_ROW FROM NUMBERS WHERE UNPIVOT_ROW <= 2      -- <==  THIS AS WELL
        )
)

--Queries to generate final result set based on CTE intermediate queries
--Subtotals / System totals for Dist-TRA, Dist-Fran, Trans-TRA, Trans-Fran
SELECT ...
  FROM (SELECT * FROM DIST_TRA
        UNION ALL
        SELECT * FROM DIST_FRAN)
2
MysteryMoose

たぶん、SR on Metalinkを開く必要があります。これはバグの可能性があります。 10gで同様の動作をしたことを思い出してください。複数のCONNECT BYを使用するいくつかの複雑なクエリが誤った結果を返していました。端末のパッチセットにパッチを当てることで修正されました。

2
ibre5041