C# 语句中包含的Oracle参数?

C# 语句中包含的Oracle参数?,c#,sql,oracle,parameters,oracle10g,C#,Sql,Oracle,Parameters,Oracle10g,我有一个c#net应用程序需要修改。目前的查询有效地做到了这一点: select * from contract where contractnum = :ContractNum (非常简单,只是为了说明我们使用了一个=和一个参数) 该参数从C#应用程序的Settings.Settings文件中读取,其中有一个字符串。我需要修改它以包含多个合同,因此我想我可以将SQL更改为: select * from contract where contractnum in (:ContractNum)

我有一个c#net应用程序需要修改。目前的查询有效地做到了这一点:

select * from contract where contractnum = :ContractNum
(非常简单,只是为了说明我们使用了一个=和一个参数)

该参数从C#应用程序的Settings.Settings文件中读取,其中有一个字符串。我需要修改它以包含多个合同,因此我想我可以将SQL更改为:

select * from contract where contractnum in (:ContractNum)
但无论我如何格式化参数中的字符串,都不会返回任何结果


有没有办法让oracle使用参数执行IN操作?

还没有找到一个db,它支持计算包含逗号的单个字符串变量,并将其作为子句中唯一的
分隔


您的选项是对变量进行子字符串化,以便将逗号分隔的变量内容转换为行,这样您就可以加入到该变量中。或者使用动态SQL,它是在执行语句之前在存储过程中构造为字符串的SQL语句。

您可以使用管道函数将字符串转换为可与
中的
运算符一起使用的表。例如(使用10gR2测试):

使用以下软件包:

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)))

当使用ODP.NET作为数据提供程序时,可以将Oracle数字集合用作参数(绑定变量)。这适用于Oracle server 9、10或11以及ODP.net版本>=11.1.0.6.20

当您使用Devart的.NET dataprovider for Oracle时,也可以使用类似的解决方案

让我们选择contractnum的3和4的合同

我们必须使用Oracle类型将合同号数组传输到查询中

之所以使用
MDSYS.SDO_ELEM_INFO_ARRAY
,是因为如果我们使用这个已经预定义的Oracle类型,就不必定义自己的Oracle类型。您可以用最多1048576个数字填充
MDSYS.SDO_ELEM_INFO_数组

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();
    }
  }
}
如果省略提示/*+基数(表10)*/,则不使用contract.contractnum上的索引。我假设contractnum是主键,因此此列将被索引


另请参见此处:

有关在语句中使用参数的信息,您可以使用以下结构:

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

其中ContractNum是自定义数组类型。

我知道这是一个老问题,但这是选择的答案没有解决我的问题的几个问题之一,我不想再开始关于这个主题的另一个话题,所以我将把我在旅行中发现的东西记下来,希望它能帮助别人

我不经常使用Oracle,但与SQL Server一样,要传递表值参数,似乎需要有相应的UDT(用户定义表),您可以对其执行权限(我可能错了)。这意味着其他建议使用内置SYS UDT的答案会带来一些麻烦,我无法确定在当前版本的ODP.net中是否真的可以将表传递给不是PL/SQL存储过程的对象

其次,字符串解析解决方案是一个难题,原因显而易见(无法缓存执行计划或Oracle调用的任何内容,伸缩性不好,等等)

所以我花了相当多的时间尝试在一个数据集市上使用一个表值参数来执行IN子句,在我被一个显而易见的()的闪现击中之前,我只有读取权限。事实证明,Oracle支持“本地”Xml查询,因此您可以传递一个Xml列表(如果您只需要这个列表的话),而不是传递一个值数组。同样,我可能错了,但它被作为合法的绑定参数处理,这是一个如何简单使用的示例(vb.net、ADO.net、ODP.net使用NuGet包):

这更多的是一个观察,而不是一个仔细研究的解决方案,所以如果这样做有任何不合适的地方,请发表评论


显然,使用这种方法时有4000个字符的限制(如果是NVARCHAR,则为2000个字符),因此我不得不观察我的分页。如果您查看,则会收到信息性错误消息

ORA-01460:请求的转换未实现或不合理


也许有人还在寻找答案,下面是rexexp的一个例子。

在这种情况下,每个emp no是分开的

WHERE Emp.ENAME in 
  (select regexp_substr(:Bind_Ename_Comma_Sep_List,'[^,]+', 1, level) 
   from dual 
    connect by 
        regexp_substr(:Bind_Ename_Comma_Sep_List, '[^,]+', 1, level) 
            is not null)

我找到了一个使用&而不是:进行参数标识的引用,该引用有效:参数值:'1182411','1182423'SQL:select*from contract where contractnum in(&contractnum))我不知道为什么会这样,也不知道它是由Orace“正式”支持还是仅由TOAD支持。您以前是否使用过&for Oracle?知道区别是什么吗?:变量是绑定变量;我记得使用&variable的唯一一次是在PLSQL Developer中定义/使用绑定变量。符号AND是指示SQLPlus中替换变量的默认字符。TOAD(和其他IDE)支持一些SQLPlus语法。符号可能无法与C#一起使用。即使它确实能工作,它也无法绑定(性能问题),并且会对SQL注入开放,因为它会动态生成查询(安全问题)。您的选项当然是选项,但它们不是唯一的选项。+1-AFAIK,这是使用所有选项的唯一方法:bind变量、未知数量的元素和“IN”子句。如果已知元素数的上限,则始终可以对语句进行编码以使用该数量的元素,并在有剩余占位符时以编程方式替换空值。这不是使用绑定变量的唯一方法。您还可以绑定Oracle数字集合并与表(:numbers)联接。您将不再需要流水线函数。但是,您的数据提供商必须支持它。这是我们使用了近10年的方法。在(最终)转向使用ODP.NET之后,我们被迫使用tuinstoel发布的答案中强调的方法。不管怎样,这似乎是一个更好的解决方案,而不是字符串拆分。@Carl我同意:一般来说,收集是一个更好的选择。设置可能需要花费更多的精力,但集合不起作用
    Dim xe As New XElement("l", New XElement("i", "ITEM-A"), New XElement("i", "ITEM-B"))
    Using conn As New OracleConnection(myConnectionString)
        conn.Open()
        Using cmd As OracleCommand = conn.CreateCommand()
            cmd.CommandType = CommandType.Text
            Dim query As String
            query = "  SELECT s.FOO, q.BAR " & vbCrLf
            query &= " FROM TABLE1 s LEFT OUTER JOIN " & vbCrLf
            query &= "      TABLE2 q ON q.ID = s.ID " & vbCrLf
            query &= " WHERE (COALESCE(q.ID, 'NULL') NOT LIKE '%OPTIONAL%') AND "
            query &= "       (s.ID IN ("
            query &= "                      SELECT stid "
            query &= "                      FROM XMLTable('/l/i' PASSING XMLTYPE(:stid) COLUMNS stid VARCHAR(32) PATH '.')"
            query &= "                 )"
            query &= "        )"
            cmd.CommandText = query
            Dim parameter As OracleParameter = cmd.Parameters.Add("stid", OracleDbType.NVarchar2, 4000)
            parameter.Value = xe.ToString
            Using r As OracleDataReader = cmd.ExecuteReader
                While r.Read()
                    //Do something
                End While
            End Using
        End Using
        conn.Close()
WHERE Emp.ENAME in 
  (select regexp_substr(:Bind_Ename_Comma_Sep_List,'[^,]+', 1, level) 
   from dual 
    connect by 
        regexp_substr(:Bind_Ename_Comma_Sep_List, '[^,]+', 1, level) 
            is not null)