文字列列のOracle SQL TO_DATEのフォーマット文字列はリテラルと一致しません
質問
スタックオーバーフローからの親愛なるsqlの達人:
環境: オラクル
文字列を含むテーブル列でto_date選択を行えない理由を理解しようとしています。以下の例に名前の列が付いたtablezに注意してください。たとえば、2010年6月20日00:00:00などの正しい形式のいくつかの文字列が含まれています。
Tablez
| Value |
| __________________ |
| 6/21/2010 00:00:00 |
| Somestring |
| Some Other strings |
| 6/21/2010 00:00:00 |
| 6/22/2010 00:00:00 |
次の作品
SELECT To_Date(c.Value, 'MM/DD/YYYY HH24:MI:SS') somedate
FROM tableX a, tableY b, tableZ c
WHERE Lower(a.name) = 'somedate'
AND a.id = b.other_id
AND b.id = c.new_id
これは(良い)のようなものを返します:
| somedate |
| __________________ |
| 21.06.2010 00:00:00 |
| 21.06.2010 00:00:00 |
| 22.06.2010 00:00:00 |
以下は機能しません
SELECT To_Date(c.Value, 'MM/DD/YYYY HH24:MI:SS') somedate
FROM properties$aud a, template_properties$aud b, consumable_properties$aud c
WHERE Lower(a.name) = 'somedate'
AND a.id = b.property_id
AND b.id = c.template_property_id
AND To_Date(c.Value, 'MM/DD/YYYY HH24:MI:SS') IS NOT NULL
戻ってきます:
ORA-01861:リテラルはフォーマット文字列と一致しません
ここに何が欠けていますか?ちょっとしたメモ:
...
AND b.id = c.template_property_id
AND To_Date(c.Value, 'DD.MM.YYYY HH24:MI:SS') IS NOT NULL
動作しません。
ありがとう!!
ゴール 日付範囲を選択するために、C.Valueのクエリ間で日付を実行できるようにします。
解決
OracleがWhere句に見られる条件を評価する順序は、固定されていません。つまり、他の基準の前にto_dateを含む条件を評価することを選択できるということです。その場合、クエリは失敗します。それを防ぐために、Ordered_redicatesのヒントをクエリに追加しますが、パフォーマンスを改善するために追加の手動チューニングが必要になる場合があることに注意してください。
SELECT /*+ ordered_predicates */
To_Date(c.Value, 'MM/DD/YYYY HH24:MI:SS') somedate
FROM properties$aud a,
template_properties$aud b,
consumable_properties$aud c
WHERE Lower(a.name) = 'somedate'
AND a.id = b.property_id
AND b.id = c.template_property_id
AND To_Date(c.Value, 'MM/DD/YYYY HH24:MI:SS') IS NOT NULL
どうやら ordered_predicates
10gで開始して廃止されます。その場合、あなたの唯一のオプションは、オプティマイザーが最初に評価を余儀なくされるようにサブクエリを使用することだと思います(つまり、クエリを組み合わせることはできません)。これを行う最も簡単な方法は、置くことです rownum
内部クエリのステートメント。
SELECT To_Date(c.Value, 'MM/DD/YYYY HH24:MI:SS') somedate
FROM (SELECT value
FROM properties$aud a,
template_properties$aud b,
consumable_properties$aud c
WHERE Lower(a.name) = 'somedate'
AND a.id = b.property_id
AND b.id = c.template_property_id
AND rownum > 0)
WHERE To_Date(c.Value, 'MM/DD/YYYY HH24:MI:SS') IS NOT NULL
他のヒント
create or replace function to_date_or_null(v_str_date in varchar2
, v_str_fmt in varchar2 default null) return date as
begin
if v_str_fmt is null then
return to_date(v_str_date);
else
return to_date(v_str_date, v_str_fmt);
end if;
exception
when others then
return null;
end to_date_or_null;
/
テスト:
SQL> select to_date_or_null('2000-01-01', 'YYYY-MM-DD') from dual -- Valid;
TO_DATE_OR_NULL('20
-------------------
2000-01-01 00:00:00
SQL> select to_date_or_null('Not a date at all') from dual -- Not Valid;
TO_DATE_OR_NULL('NO
-------------------
SQL> select to_date_or_null('2000-01-01') from dual -- Valid matches my NLS settings;
TO_DATE_OR_NULL('20
-------------------
2000-01-01 00:00:00
SQL> select to_date_or_null('01-Jan-00') from dual -- Does not match my NLS settings;
TO_DATE_OR_NULL('01
-------------------
別の手法は、ケースに変換を埋め込むことです。例えば
SELECT * FROM table
WHERE col_a = '1'
AND case when col_a = '1' then to_date(col_b,'DD/MM/YYYY') end = trunc(sysdate)
しかし、条項が複雑になると、これは本当に醜くなります。
C.Valueが有効な形式であるかどうかを確認しますか
AND To_Date(c.Value, 'DD.MM.YYYY HH24:MI:SS') IS NOT NULL
?これは機能しません。他の方法でチェックを実行する必要があります。正規表現を使用できます(しばらく使用していません)。データモデルが問題の行を識別できる場合は、さらに良いことです。