3

データベースに 3 つのテーブルがあります。

  • PARENT_Aには「ID」主キー列があります。
  • PARENT_Bには「ID」主キー列があります。
  • CHILDには、「PARENT_A_ID」および「PARENT_B_ID」の外部キー列があります。また、VARCHAR である「START_DATE」列もあります (残念ながら、これを変更することはできません)。

さて、次のクエリがあります。

更新- サブクエリを更新して、実際のコードにもう少し似たものにしました。c2 には、START_DATE が有効な日付であることを保証する追加の制限があります。

SELECT *
FROM PARENT_B pb
LEFT OUTER JOIN CHILD c1 ON c1.PARENT_B_ID = pb.ID
WHERE pb.ID IN
(
    SELECT c2.PARENT_B_ID
    FROM PARENT_A pa
    LEFT OUTER JOIN CHILD c2 ON c2.PARENT_A_ID = pa.ID
    WHERE TO_DATE(c2.START_DATE, 'mm/dd/yyyy') BETWEEN
          ADD_MONTHS(TRUNC(SYSDATE, 'MONTH'), -12) AND
          (TRUNC(SYSDATE, 'MONTH') - 1)
    AND c2.HAS_VALID_DATE = 1
);

このクエリは失敗します。ORA-01843: not a valid month例外があります。ただし、最初の結合 (クエリの 3 行目) を削除すると、クエリは正常に実行されます。

何が起こっているのかわかりません。サブクエリはそれ自体で正常に実行され、すべての値が正しい日付形式になっています。

何が起こっているのか誰にも分かりませんか?

4

1 に答える 1

5

CHILD問題は、ほぼ確実に、形式を使用してstart_date文字列を日付に変換できない行が少なくともいくつかあることmm/dd/yyyyです。オラクルは任意の順序で述語を評価できるため、結合なしでたまたま取得する計画は、実行前にそれらの不良行を削除し、結合を使用してたまたま取得する計画は、他の行を削​​除する前にto_date評価することを期待しています。to_date理由。もちろん、明日、オプティマイザーが別のプランを選択すると、クエリは結合で機能し、それがないと失敗する可能性があります。または、クエリが最初の行を正常に返した後、行をフェッチしようとすると ORA-01843 エラーがスローされることがあります。

データを修正できない場合、通常は、文字列を日付に変換NULLできない場合に を返す独自の文字列から日付への変換ルーチンを作成するのが最善の方法です。何かのようなもの

CREATE OR REPLACE FUNCTION my_to_date( p_dt_str IN VARCHAR2, p_mask IN VARCHAR2 )
  RETURN DATE
  DETERMINISTIC
IS
  l_dt DATE;
BEGIN
  l_dt := to_date( p_dt_str, p_mask );
  RETURN l_dt;
EXCEPTION
  WHEN others THEN
    RETURN NULL;
END;

クエリは次のようになります

WHERE my_to_date( c2.start_date, 'mm/dd/yyyy' ) between ...

この関数を使用して、start_dateが無効な行を見つけることもできます

SELECT *
  FROM child
 WHERE start_date is not null
   AND my_to_date( start_date, 'mm/dd/yyyy' ) is null

Jonathan Gennick は Subquery Madnessという素晴らしい記事を書いており、この問題について詳しく説明しています。こちらも楽しく読めます。

于 2012-06-07T18:16:26.957 に答える