OracleのLISTAGG関数で、非常に異常な動作が発生しました。LISTAGGが評価され、到達不能のケースステートメントで失敗する
4000文字を超える文字を扱うと、LISTAGGが失敗することがわかります。
私はこれを知っているので、100文字以上のセルを "数が多すぎる"というメッセージで置き換えるCASE文がありました。
CREATE TABLE EMP (
ID VARCHAR2(401),
DEP VARCHAR2(10)
);
INSERT INTO EMP VALUES (DBMS_RANDOM.string('A', 401), 'FOO'); -- Run exactly 9 times
INSERT INTO EMP VALUES (DBMS_RANDOM.string('A', 5), 'BAR'); -- Run 3 times
simplicitiesのために、のはカウント> 100のために私の特別なケースを無視してみましょう、とちょうどそのFOOが除外されなければならないと言う、とBARが含まれなければなりません。 10にFOO部門に合計をもたらし、1つだけ余分な行を追加し、
しかし:
SELECT DEP,
CASE
WHEN DEP = 'BAR' THEN
LISTAGG(ID, ',')
WITHIN GROUP (ORDER BY NULL)
OVER (PARTITION BY DEP)
ELSE
'Too many to count'
END AS ID_LIST
FROM EMP;
これは、(異なるランダムな文字ではなく)このようになっているはずな結果を提供します...
INSERT INTO EMP VALUES (DBMS_RANDOM.string('A', 401), 'FOO'); -- Same as before
は選択同じを再実行例外に会うために、私たちが発生します。
ORA-01489: result of string concatenation is too long
01489. 00000 - "result of string concatenation is too long"
*Cause: String concatenation result is more than the maximum size.
*Action: Make sure that the result is less than the maximum size.
不思議なことに、case文の条件が1 = 2に変更された場合でも、これは起こります。
ここで何が起こっているのか分かりません。 SQLは、それを使用する意図があるかどうかに関わらず、文を評価することにしていると思われます。そのため、4000文字以上のLISTAGGが満たされると失敗します。
問題にはいくつかの解決策がありますが、決して到達してはいけないのにSQLが(明らかに)LISTAGGを実行することに決めた理由についてもっと知りたいと思います。
case文の最初の 'when'条件が' 1 = 0'のときにオプティマイザがうまくやっていないのは変です。オプティマイザは、1 = 0のときはcaseを書き換え、else '多くの場合end_id_list'を' id_list'としてカウントするには多すぎます。すべてがコンパイルに進む前に書き直してください。オプティマイザは、クエリの解析の他の部分では非常に巧妙ですが、この場合は明らかにそうではありません。 – mathguy
数字比較(1 = 0)のための最適化を行うべきですが、文字列の比較( 'T' = 'F')ではないことを示唆するMOSノートがあります。しかし、プロセスでは遅すぎる必要もありますが、非集計バージョンではそうしますが、集計などを心配する必要がある場合は、クエリー・リライトの規模が大きすぎる可能性があります。見ている... –
ありがとうアレックス、それは非常に徹底的な説明だった。私は自分自身でこれについて説明する計画を立てるべきだと思っていたはずです。 – Addison