4

Oracle SQL PLUSコマンドプロンプトのBIND変数に、約3000個の値を複数渡そうとしています..

SELECT JOB
  FROM EMP 
 WHERE JOB IN :JOB -- bind variable value

その変数リストに一致するEMP列のテーブルのすべての値をフェッチする必要があるため、結果を確認したいと思います。JOB


実稼働環境であるため、テーブルを作成することはできませんが、SELECT 句に権限を付与しています。

UNIX-SQL PLUS 環境から同じクエリを実行したときに、それがどのように正確に実行されるかについて、さらに詳しい情報が必要です。

BIND 変数の値を入力するよう求めるプロンプトが表示されますか、それとも次のような値を持つファイルを参照できますか? :JOB1 := 'MANAGER' :JOB2 := 'CLERK' :JOB3 := 'ACCOUNTANT'

4

6 に答える 6

4

Oracle bind variables are a one-to-one relationship, so you'd need one defined for each value you intend to include in the IN clause:

SELECT JOB
  FROM EMP 
 WHERE JOB IN (:JOB1, :JOB2, :JOB3, ..., :JOB3000)

You need to also be aware that Oracle IN only supports a maximum of 1,000 values, or you'll get:

ORA-01795: maximum number of expressions in a list is 1000

The best alternative is to create a table (derived, temporary, actual, or view), and join to it to get the values you want. IE:

SELECT a.job
  FROM EMP a
  JOIN (SELECT :JOB1 AS col FROM DUAL
        UNION ALL
        SELECT :JOB2 FROM DUAL
        UNION ALL
        SELECT :JOB3 FROM DUAL
        UNION ALL 
        ...
        UNION ALL 
        SELECT :JOB3000 FROM DUAL) b ON b.col = a.job
于 2011-07-09T04:42:42.053 に答える
1

Ugly-Delimited-String-Approach(tm)を見てください。

つまり、文字列をバインドし、SQL でリストに変換します。醜い、つまり。

于 2011-07-09T09:38:13.860 に答える
1

10g 以上でそれを行う 1 つの方法は、サブクエリ ファクタリングを使用することです。

Assume:JOBは値のコンマ区切りリストです。次のように動作します。

with job_list as
(select trim(substr(job_list,
                    instr(job_list, ',', 1, level) + 1,
                    instr(job_list, ',', 1, level + 1)
                      - instr (job_list, ',', 1, level) - 1
                   )
            ) as job
  from (select 
               -- this is so it parses right
               ','|| :JOB ||',' job_list
         from dual)
connect by level <= length(:JOB)
                     - length (replace (:JOB, ',', '') ) + 1
)
select * from emp
 where job in (select * from job_list);

はい、読むのは少し醜いですが、動作します.Oracleは、行ごとに1回ではなく、値のリストの解析を1回行うのに十分賢いです。内部で行うことは、解析された値の一時テーブルを作成し、それをベース テーブルに結合できるようにすることです。

(私は自分でこれを思いついたわけではありません-元のクレジットはasktomの質問に送られます。)


:JOB使用する前に宣言してデータを設定する必要があるバインド変数です。以下のステートメントは、SQL*Plus でこれを行う方法を示しています。

SQL> variable JOB varchar2(4000);

SQL> exec :JOB := '10, 20';
于 2011-07-12T16:53:20.557 に答える
0

私が尋ねなければならない最初の質問はこれです: この約 3000 の値のリストはどこから来ているのでしょうか? 別のテーブルからのものである場合は、次のように記述できます。

SELECT JOB
  FROM EMP
 WHERE JOB IN (SELECT something FROM some_other_table WHERE ... )

この回答の残りの部分では、データベースのどこにもないと仮定します。

理論的には、あなたが望むことをすることは可能です。多くのバインド変数を含むクエリを作成するには、さまざまな方法があります。all_objects例として、 3000 個のバインド変数を使用してデータ ディクショナリ ビューをクエリするスクリプトを作成します。3000 のバインド変数を含む SQL*Plus スクリプトを作成するつもりはありません。代わりに、この SQL*Plus スクリプトを生成する Python スクリプトを作成しました。ここにあります:

ns = range(1, 9001, 3) # = 1, 4, 7, ..., 8998

# This gets rid of a lot of lines saying 'PL/SQL procedure successfully completed'.
print "SET FEEDBACK OFF;"
print

# Declare the bind variables and give them values.
for i, n in enumerate(ns):
    print "VARIABLE X%04d NUMBER;" % i
    print "EXEC :X%04d := %d;" % (i, n)
    print

query = "SELECT object_name FROM all_objects WHERE"

# Break up the query into lines to avoid SQL*Plus' limit of 2500 characters per line.
chunk_size = 100
for i in range(0, len(ns), chunk_size):
    query += "OR object_id IN (" + ",".join( ":X%04d" % j for j in range(i, i + chunk_size) ) + ")\n"

query = query.replace("WHEREOR", "WHERE") + ";\n"
print query

その後、このスクリプトを実行し、その出力を.sqlファイルにリダイレクトしてから、その.sqlファイルを SQL*Plus で実行することができました。

上記で「理論的には可能です...」と書いたことに気付くかもしれません。正当な理由から、理論上の節をそこに置きました。クエリは有効なようですが、実行しない理由がわかりません。ただし、Oracle インスタンス (XE 11g Beta) で実行すると、次の出力が得られました。

SQL> @genquery.sql
SELECT object_name FROM all_objects WHERE object_id IN (:X0000,:X0001,:X0002,:X0
003,:X0004,:X0005,:X0006,:X0007,:X0008,:X0009,:X0010,:X0011,:X0012,:X0013,:X0014
,:X0015,:X0016,:X0017,:X0018,:X0019,:X0020,:X0021,:X0022,:X0023,:X0024,:X0025,:X
0026,:X0027,:X0028,:X0029,:X0030,:X0031,:X0032,:X0033,:X0034,:X0035,:X0036,:X003
7,:X0038,:X0039,:X0040,:X0041,:X0042,:X0043,:X0044,:X0045,:X0046,:X0047,:X0048,:
X0049,:X0050,:X0051,:X0052,:X0053,:X0054,:X0055,:X0056,:X0057,:X0058,:X0059,:X00
60,:X0061,:X0062,:X0063,:X0064,:X0065,:X0066,:X0067,:X0068,:X0069,:X0070,:X0071,
:X0072,:X0073,:X0074,:X0075,:X0076,:X0077,:X0078,:X0079,:X0080,:X0081,:X0082,:X0
083,:X0084,:X0085,:X0086,:X0087,:X0088,:X0089,:X0090,:X0091,:X0092,:X0093,:X0094
,:X0095,:X0096,:X0097,:X0098,:X0099)
*
1 行目のエラー:
ORA-03113: 通信チャネルでファイルの終わりです
プロセス ID: 556
セッション ID: 137 シリアル番号: 29

このORA-03113エラーは、サーバー プロセスがクラッシュしたことを示しています。

これについていくつかのバリエーションを試しました:

  • バインド変数をまったく使用しない (つまり、値を直接入れる)
  • リストを使わないIN、つまり書くSELECT ... FROM all_objects WHERE object_id=:X0000 OR object_id=:X0001 OR ...
  • OMGポニーのアプローチを使用して、
  • バインド変数を使用せずに OMG Ponies のアプローチを使用し、
  • データをテーブルにコピーし、all_objects代わりにクエリを実行します。

上記のアプローチはすべてORA-03113エラーを引き起こしました。

もちろん、Oracle の他のエディションでこれらのクラッシュが発生するかどうかはわかりませんが (他のエディションにはアクセスできません)、良い兆候ではありません。

編集:のようなものを達成できるかどうか尋ねますSELECT JOB FROM EMP WHERE JOB IN (:JOB)。それに対する簡単な答えはノーです。このコマンドに対するSQL*Plusの使用法メッセージVARIABLEは次のとおりです。

使用法: VAR[IABLE] [ [ NUMBER | CHAR | CHAR (n [CHAR|BYTE]) |
                    VARCHAR2 (n [文字|バイト]) | NCHAR | NCHAR (n) |
                    NVARCHAR2 (n) | CLOB | NCLOB | ブロブ | BFILE
                    リカーソル | BINARY_FLOAT | BINARY_DOUBLE ] ]

を除いて、上記の型はすべて単一のデータ値ですREFCURSORが、SQL*Plus はそれを単一の値として扱うようです。この方法で返されたデータをクエリする方法が見つかりませんREFCURSOR

要約すると、達成しようとしていることはほぼ確実に不可能です。ここであなたの最終的な目的が何であるかはわかりませんが、SQL*Plus で単一のクエリを使用してそれを行うことはできないと思います。

于 2011-07-09T13:48:19.917 に答える
0

同様の問題に直面している間、私はこの汚い解決策を思いつきました:

select * from my_table where ',param_1,param_2,param_3,param_4,' LIKE '%,'||my_column||',%'
于 2011-09-21T11:15:15.027 に答える