- (A) 自分の家庭を持ったあとも、帰省時に使う私物を実家に置いておける。何なら自分の部屋まで残っている。
- (B) 自分の家庭を持ったあとは、帰省時に使う私物は実家に置いておけない。帰省するときは自分用の布団を車に積んでいく。
これは土地柄なんですかね? 家に対する帰属意識の違いともまた違うように思うのですが。
これは土地柄なんですかね? 家に対する帰属意識の違いともまた違うように思うのですが。
You will get the following error when you specify over 1000 entries in IN clause in Oracle:
ORA-01795: maximum number of expressions in a list is 1000
This is by design, and the following document says "You can specify up to 1000 expressions in expression_list." docs.oracle.com
There are some hacks to bypass this constraint. If you want to write IN clause like this:
SELECT NAME TBL_A FROM TBL_A WHERE ID IN ('1', '2', ..., '999', '1000', '1001');
You can write it with tuples with dummy values like this:
SELECT NAME TBL_A FROM TBL_A WHERE (ID, 1) IN (('1', 1), ('2', 1), ..., ('99999', 1), ('100000', 1), ('100001', 1));
I had a question about how many entries you can specify in IN clause with the tuples. In StackOverflow, some say the limit is 100,000*1, and some say 70,000*2, and there is no clear evidence.
So, let's experiment. I installed Oracle on my machine. The version is:
SQL> SELECT BANNER_FULL FROM v$version; BANNER_FULL -------------------------------------------------------------------------------- Oracle Database 21c Express Edition Release 21.0.0.0.0 - Production Version 21.3.0.0.0
And made a table like this:
CREATE TABLE TBL_A (ID CHAR(4), NAME VARCHAR2(20)); INSERT INTO TBL_A VALUES ('1', 'JOHN DOE');
And run this PowerShell script. This script creates a SELECT statement and copies it in the clipboard. Note that you cannot paste the result of this script into sqlplus
because the line breaks are ignored by sqlplus
. You have to paste and re-copy it in some text editor and paste it to sqlplus
. You can change the number of entries with the sequence in the head of this script. If you specify 100000..100001
, you will get two SELECT statements with 100000 entries and 100001 entries.
100000..100001 | ForEach-Object { "SELECT NAME, " + $_ + " TBL_A FROM TBL_A WHERE (ID, 1) IN (" + (((1..$_) | ForEach-Object { $crlf = ''; if ($_ % 100 -eq 1) { $crlf = "`n" }; $crlf + "('" + $_ + "', 1)" }) -join ", ") + ");" } | Set-Clipboard
In my environment, the IN clause with 100,000 entries, 100,001 entries, 120,001 entries are all successfully executed in sqlplus
. At least, "up to 100,000 entries" seems wrong in Oracle 21c.
For someone who wants to do a replication study, I have uploaded the SQL statement with 100,001 entries in Gist. SELECT with IN clause that contains 100,001 entries for Oracle 21c · GitHub
Of course, the performance is terrible. It takes some minutes to execute. In my environment, it takes 03:57.67 for 100,000 entries, and 5:18.24 for 120,001 entries. You had better rewrite the statement with EXISTS clause for performance.
I've got ORA-03113 error when I tried the IN clause with 200,000 entries. This error seems caused by the length of the SQL statement rather than the number of entries in IN clause. The error is resolved when I change the literals in IN clause to a shorter string.
To conclude, in Oracle 21c, the maximum number of entries you can specify in IN clause with tuples remains unknown. At least, you can specify 200,000 entries. I could not find the upper limit in the official Oracle document, so this number would be changed without notice.
OracleでIN句の内容が1000個を超えると以下のエラーが発生する。
ORA-01795: maximum number of expressions in a list is 1000
これはOracleの仕様で、以下のドキュメントに"You can specify up to 1000 expressions in expression_list."と記載されている。 docs.oracle.com
この制約を回避するハックとして、
SELECT NAME TBL_A FROM TBL_A WHERE ID IN ('1', '2', ..., '999', '1000', '1001');
とする代わりに
SELECT NAME TBL_A FROM TBL_A WHERE (ID, 1) IN (('1', 1), ('2', 1), ..., ('99999', 1), ('100000', 1), ('100001', 1));
のようにダミーの値を含んだタプルをIN句に指定する方法が知られている。 stackoverflow.com
ここで、タプルを指定した場合にIN句の中に指定できる内容の上限は何か、という問題がある。stackoverflowのコメントでは、最大100,000個*1と言われみたり、最大70,000個*2だと言われてみたりはっきりしない。
じゃぁ試してみよう、ということで、手元にOracleを入れて試してみた。Oracleのバージョンは以下。
SQL> SELECT BANNER_FULL FROM v$version; BANNER_FULL -------------------------------------------------------------------------------- Oracle Database 21c Express Edition Release 21.0.0.0.0 - Production Version 21.3.0.0.0
こんな感じでテーブルを作り……
CREATE TABLE TBL_A (ID CHAR(4), NAME VARCHAR2(20)); INSERT INTO TBL_A VALUES ('1', 'JOHN DOE');
こんな感じのPowerShellで、SELECT文をクリップボードにコピーする。注意として、この内容をそのままWindowsのsqlplusにペーストしても、改行が無視されて途中で内容が途切れてしまう。いちどテキストエディタ等にペーストしてから、再度コピーしてsqlplusにペーストしよう。スクリプトの最初のシーケンスを変更すれば複数のSELECT文を作ることができる。この場合、IN句が100,000個と、100,001個のSELECT文がクリップボードにペーストされる。
100000..100001 | ForEach-Object { "SELECT NAME, " + $_ + " TBL_A FROM TBL_A WHERE (ID, 1) IN (" + (((1..$_) | ForEach-Object { $crlf = ''; if ($_ % 100 -eq 1) { $crlf = "`n" }; $crlf + "('" + $_ + "', 1)" }) -join ", ") + ");" } | Set-Clipboard
試しに実行してみたところ、IN句が100,000個のケース・100,001個のケース、120,001個のケースはいずれも問題なく実行できた。少なくとも、100,000個が上限というのはOracle 21cでは誤りのようだ。 試したい人のために、実際に使ったSQL文(100,001個のケース)を以下のGistにアップロードした。
SELECT with IN clause that contains 100,001 entries for Oracle 21c · GitHub
もちろん性能は非常に悪く、分単位の時間がかかる。実測したところ、手元では100,000件のケースで03:57.67、120,001件のケースで5:18.24かかった。性能を考慮するなら、可能であればEXISTS句+副問い合わせの形に書き換えるのがよいだろう。
200,000個のケースでは、sqlplusから実行しようとしたところORA-03113エラーが発生したが、これはIN句の個数ではなくSQL文の長さが問題のようで、IN句に指定する定数の長さを短かくしたところ問題なく実行できた。
結論として、Oracle 21cの場合、タプルを使った場合にIN句に指定できる内容の個数の上限は不明。少なくとも200,000件は指定が可能だった。Oracle公式のドキュメントには上限の個数の記載が見当たらなかったので、その意味では予告なく変更される可能性もあるだろう。
要求仕様にセキュリティ対策の記載がない場合でも、十分に認知されているリスクはベンダが対策を行う必要があるとした東京地裁平成26年1月23日判決(平23(ワ)32060号)*1が出て以降、この点についてソフトウェア請負開発を行うベンダの立場は弱く、また求められる責務は多くなっていると言えるでしょう。
2019 MPOWER Cybersecurity Summitで徳丸先生が登壇されていた際*2は、ベンダ側としては「セキュリティ対策が必要であることを発注元に明示し、発注元から断わられたことを記録しておくことで、裁判になった場合のリスクを低減する(過失相殺が認定されるようにする)」が自衛策になるという話でした。上記の判例を所与のものとした場合、この方針しかないだろうというのは納得です。
では、「セキュリティ対策が必要であることを発注元に明示」するのは、いつ・どこで・だれがどのように提示するのがよいのでしょうか? また、裁判になった場合のリスクを低減するには、記録をどのような形で作成し、どのような形で保存しておくのがよいのでしょうか?
上記のような話がぜんぜん議論になっていないのは、いったいどうしてなんでしょうね?
Eclipseのコンソールの内容をクリップボードにコピーしようとした際、ANSIエスケープシーケンスが付いてくることがある*1。テキストエディタなどでは^[[2m
のようにに表示される(最初の^[
はASCIIのESC
)。ANSIエスケープシーケンスについてはTeraTermのページがよくまとまっている*2。
CyberChef*3にはURLのパーセントエンコーディングを戻したり、\n
などのエスケープ文字を解釈したりする機能があるが、ANSIエスケープシーケンスを削除する機能はない。
簡単に削除するには、Eclipseであればreplace-regexp
で^[.+?m
を空文字で置換すればよい。エスケープシーケンスは置換対象テキスト中からコピーする。