2009-10-26 14 views
7

変更が必要なC#.netアプリがあります。パラメータは上Settings.Settingsファイルから読み込まれていることIN文を使用したOracleのパラメータ?

select * from contract where contractnum = :ContractNum 

(非常に単純化し、ちょうど私たちは=と一つのパラメータを使用している示すため)

:現時点ではクエリが効果的にこれを行いますC#アプリには1つの文字列があります。私は複数の契約を含めるために、それを変更する必要があるので、私は私がSQLを変更することができます図:

select * from contract where contractnum in (:ContractNum) 

が、それは関係なく、私はパラメータの文字列をフォーマットする方法、結果を返しません。

oracleにパラメータを使用してINを実行させる方法はありますか?

ありがとうございました。

+0

データプロバイダとしてodp.netまたはdevartを使用する場合は、Oracleコレクション(ネストした表)をパラメータとして使用できます。これは最も速い方法ですが、system.data.oracleclientを使用する場合は不可能です。だから、どのような種類のデータを使用していますか? – tuinstoel

答えて

2

カンマを含む単一の文字列変数を評価して唯一のIN句として分離するデータベースをまだ見つけていない。

あなたのオプションは、カンマで区切られた変数の内容が行に変換されるように変数を部分文字列にすることです。動的SQLを使用することもできます。動的SQLは、文が実行される前にsproc内に文字列として作成されたSQL文です。

+0

私は&の代わりに使用して参照が見つかりました:パラメータ同定のために、それは動作します: パラメータ値を「1182411」、「1182423」 SQL: 私は見当がつかない*契約からどこcontractnum(ContractNum&)で選択しますなぜこれがうまくいくのか、それともOraceかTOADだけで「正式に」サポートされているのかどうか。前にOracleと&を使用しましたか?違いは何ですか? – Gareth

+0

:変数はバインド変数です。 &変数を使用して覚えているのは、PLSQL Developer内でバインド変数を定義/使用することだけでした。 –

+2

アンパサンドは、SQL * Plusの置換変数を示すデフォルトの文字です。 TOAD(および他のIDE)は、いくつかのSQL * Plus構文をサポートしています。 – APC

6

文字列をIN演算子で使用できるテーブルに変換するパイプライン関数を使用できます。例えば、(10gリリース2でテスト):以下のパッケージと

SQL> select * from table(demo_pkg.string_to_tab('i,j,k')); 

COLUMN_VALUE 
----------------- 
i 
j 
k 

SQL> CREATE OR REPLACE PACKAGE demo_pkg IS 
    2  TYPE varchar_tab IS TABLE OF VARCHAR2(4000); 
    3  FUNCTION string_to_tab(p_string VARCHAR2, 
    4       p_delimiter VARCHAR2 DEFAULT ',') 
    5  RETURN varchar_tab PIPELINED; 
    6 END demo_pkg; 
    7/

Package created 
SQL> CREATE OR REPLACE PACKAGE BODY demo_pkg IS 
    2  FUNCTION string_to_tab(p_string VARCHAR2, 
    3       p_delimiter VARCHAR2 DEFAULT ',') 
    4  RETURN varchar_tab PIPELINED IS 
    5  l_string   VARCHAR2(4000) := p_string; 
    6  l_first_delimiter NUMBER := instr(p_string, p_delimiter); 
    7  BEGIN 
    8  LOOP 
    9   IF nvl(l_first_delimiter,0) = 0 THEN 
10    PIPE ROW(l_string); 
11    RETURN; 
12   END IF; 
13   PIPE ROW(substr(l_string, 1, l_first_delimiter - 1)); 
14   l_string   := substr(l_string, l_first_delimiter + 1); 
15   l_first_delimiter := instr(l_string, p_delimiter); 
16  END LOOP; 
17  END; 
18 END demo_pkg; 
19/

Package body created 

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

select * 
    from contract 
where contractnum in (select column_value 
         from table(demo_pkg.string_to_tab(:ContractNum))) 
+1

+1 - AFAIK、これはバインド変数、不明な要素数、および "IN"句のすべてを使用する唯一の方法です。 要素の数に既知の上限がある場合は、その数の要素を使用するようにステートメントをコーディングし、残りのプレースホルダーがある場合にプログラムで代用することができます – dpbradley

+0

これはバインド変数を使用する唯一の方法ではありません。また、Oracleの数値コレクションをバインドして、table(:numbers)と結合することもできます。パイプライン機能は必要ありません。しかし、データプロバイダはそれをサポートしなければなりません。 – tuinstoel

+0

これは私たちがほぼ10年間使ってきたアプローチです。 ODP.NETを使用するために(最後に)移動した後、私たちはtuinstoelが投稿した答えに強調表示されているメソッドを使用することを余儀なくされています。とにかく、ストリング分割よりはるかに良い解決策であるようです。 – Carl

6

あなたのように数字のOracleコレクションを使用することができますデータプロバイダとしてODP.NETを使用する場合のパラメータ(バインド変数)。これは、Oracleサーバー9,10または11およびODP.netリリース> = 11.1.0.6.20で機能します。

OracleにDevartの.NETデータプロバイダを使用すると、同様の解決策が可能です。

はのはcontractnumの3と4

私たちは、私たちのクエリに契約番号の配列を転送するためにOracle型を使用する必要がありますとの契約を選択してみましょう。

MDSYS.SDO_ELEM_INFO_ARRAYが使用されています。これはすでに定義済みのOracleタイプを使用すると、独自のOracleタイプを定義する必要がないためです。 MDSYS.SDO_ELEM_INFO_ARRAYに最大1048576の数字を記入することができます。

using Oracle.DataAccess.Client; 
using Oracle.DataAccess.Types; 

[OracleCustomTypeMappingAttribute("MDSYS.SDO_ELEM_INFO_ARRAY")] 
public class NumberArrayFactory : IOracleArrayTypeFactory 
{ 
    public Array CreateArray(int numElems) 
    { 
    return new Decimal[numElems]; 
    } 

    public Array CreateStatusArray(int numElems) 
    { 
    return null; 
    } 
} 

private void Test() 
{ 
    OracleConnectionStringBuilder b = new OracleConnectionStringBuilder(); 
    b.UserID = "sna"; 
    b.Password = "sna"; 
    b.DataSource = "ora11"; 
    using (OracleConnection conn = new OracleConnection(b.ToString())) 
    { 
    conn.Open(); 
    using (OracleCommand comm = conn.CreateCommand()) 
    { 
     comm.CommandText = 
     @" select /*+ cardinality(tab 10) */ c.* " + 
     @" from contract c, table(:1) tab " + 
     @" where c.contractnum = tab.column_value"; 

     OracleParameter p = new OracleParameter(); 
     p.OracleDbType = OracleDbType.Array; 
     p.Direction = ParameterDirection.Input; 
     p.UdtTypeName = "MDSYS.SDO_ELEM_INFO_ARRAY"; 
     //select contract 3 and 4 
     p.Value = new Decimal[] { 3, 4 }; 
     comm.Parameters.Add(p); 

     int numContracts = 0; 
     using (OracleDataReader reader = comm.ExecuteReader()) 
     { 
     while (reader.Read()) 
     { 
      numContracts++; 
     } 
     } 
     conn.Close(); 
    } 
    } 
} 

1がヒント/ * +カーディナリティ(タブ10)* /を省略したときにcontract.contractnum上のインデックスが使用されていません。私はcontractnumが主キーだと仮定したので、この列は索引付けされます。

もここを参照してください:http://forums.oracle.com/forums/thread.jspa?messageID=3869879#3869879

+0

また読む:http://blog.tanelpoder.com/2012/08/02/the-limitations-of-cursor_sharing-force-and-force_matching_signature-for-sql-plan-stability/ – TTT

+0

これは私たちのシナリオのために働いた。コードで明示的に使われていなくても、NumberArrayFactoryを含んでいました。 – Carl

0

あなたはこの構造を使用することができたIN文でパラメータを使用した場合:ContractNumは、カスタム配列型をある

select * from contract where contractnum 
in (select column_value from table (:ContractNum)) 

関連する問題