【问题标题】:OracleParameter and IN ClauseOracleParameter 和 IN 子句
【发布时间】:2010-10-07 04:31:24
【问题描述】:

有没有办法使用 System.Data.OracleClient 向 IN 子句添加参数。

例如:

string query = "SELECT * FROM TableName WHERE UserName IN (:Pram)";
OracleCommand command = new OracleCommand(query, conn);
command.Parameters.Add(":Pram", OracleType.VarChar).Value = "'Ben', 'Sam'";

【问题讨论】:

  • 恕我直言,硬编码“,”字符是解决方案一部分的任何解决方案都是无效的。 Oracle 应该生成这个而不是应用程序。这也适用于任何类型的字符串分隔符 ' "

标签: c# .net oracle


【解决方案1】:

您可以将其包装在 OracleCommandExtension 方法中:

public static class OracleCommandExtension
{
    public static OracleCommand AddParameterCollection<TValue>(this OracleCommand command, string name, OracleType type, IEnumerable<TValue> collection)
    {
        var oraParams = new List<OracleParameter>();
        var counter = 0;
        var collectionParams = new StringBuilder(":");
        foreach (var obj in collection)
        {
            var param = name + counter;
            collectionParams.Append(param);
            collectionParams.Append(", :");
            oraParams.Add(new OracleParameter(param, type) { Value = obj });
            counter++;
        }
        collectionParams.Remove(collectionParams.Length - 3, 3);
        command.CommandText = command.CommandText.Replace(":" + name, collectionParams.ToString());
        command.Parameters.AddRange(oraParams.ToArray());
        return command;
    }
}

【讨论】:

  • 2016 年,托管给 Oracle 的 .NET 客户端仍然无法将数组参数传递给 in。只有 oracle 原生客户端可以。
  • 这将添加“n”个参数,每个不同的arity将是一个不同的查询,将无法使用缓存的查询计划。
  • 2019年,Oracle仍然没有接受阵列存在的事实。无论如何...这对我有用,但我不得不将 OracleType 更改为 OracleDbType
【解决方案2】:

您可以使用ODP.NET 更轻松地做到这一点:

  1. 在您的数据库中创建TABLE 类型:

    CREATE TYPE t_varchar2 AS TABLE OF VARCHAR2(4000);
    
  2. 创建集合参数:

    OracleParameter param = new OracleParameter();
    param.OracleDbType = OracleDbType.Varchar2;
    param.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
    
  3. 填写参数:

    param = new string[2] {"Ben", "Sam" };
    
  4. 将参数绑定到以下查询:

    SELECT * FROM TableName WHERE UserName IN (TABLE(CAST(:param AS t_varchar2)));
    

【讨论】:

  • 它对我不起作用,我不断收到 ORA-00936: missing expression
  • 那是因为你需要写“WHERE UserName IN (SELECT column_value FROM TABLE(CAST(:param AS t_varchar2)))”,但是你可能会得到“ORA-01484: arrays can only绑定到 PL/SQL 语句”,这表明此类代码应该放在 PL/SQL 语句中,而不是 C# 代码中
  • 这种方法需要数据库权限来修改架构——这不是我的情况。
【解决方案3】:

我知道这是不久前提出的,但不是一个绝妙的答案。

我会做这样的事情 - 请原谅粗糙的伪代码

string args[] = {'Ben', 'Sam'};
string bindList = "";
for(int ii=0;ii<args.count;++ii)
{
  if(ii == 0)
  {
   bindList += ":" + ii;
  }
  else
  {
   bindList += ",:" + ii;
  }
  OracleParameter param = new OracleParameter();
  param.dbType = types.varchar;
  param.value = args[ii];
  command.Parameters.Add(param);
}

query = "select * from TableName where username in(" + bindList + ")";

那么查询最终会包含 in(:1,:2) 并且每个都是单独绑定的。

这里也有类似的问题:Oracle/c#: How do i use bind variables with select statements to return multiple records?

【讨论】:

  • 我觉得应该是Sql注入
  • 我相信你弄错了?你会如何注入这个?
【解决方案4】:

也许使用不同的方法

SELECT * FROM SCOTT.EMP WHERE EMPNO IN (SELECT TO_NUMBER(X.COLUMN_VALUE) FROM XMLTABLE('7788,7900') X);

SELECT * FROM SCOTT.EMP WHERE ENAME IN (SELECT X.COLUMN_VALUE.GETSTRINGVAL() FROM XMLTABLE('"SCOTT", "JAMES"') X);

XMLTABLE 的内容可以是单个参数。 因此它应该适用于任何语言。

【讨论】:

    【解决方案5】:

    您可以使用类似于此处的 Oracle 自定义数据类型:
    http://www.c-sharpcorner.com/code/2191/pass-collection-to-oracle-stored-procedure-from-net-layer.aspx

    这里:
    https://stackoverflow.com/a/31466114/1867157

    首先在 Oracle 中创建一个类型并赋予它权限:

    CREATE TYPE MYSCHEMA.VARCHAR2_TAB_T AS TABLE OF VARCHAR2(4000);
    GRANT EXECUTE ON MYSCHEMA.VARCHAR2_TAB_T TO MYROLE
    

    然后创建2个类:

    StringListCustomType.cs

    public class StringListCustomType : IOracleCustomType, INullable
    {
        public const string Name = "MYSCHEMA.VARCHAR2_TAB_T";
    
        [OracleArrayMapping()]
        public string[] Array;
    
        #region IOracleCustomType
        public OracleUdtStatus[] StatusArray { get; set; }
    
        public void ToCustomObject(OracleConnection con, IntPtr pUdt)
        {
            object objectStatusArray = null;
            Array = (string[])OracleUdt.GetValue(con, pUdt, 0, out objectStatusArray);
            StatusArray = (OracleUdtStatus[])objectStatusArray;
        }
    
        public void FromCustomObject(OracleConnection con, IntPtr pUdt)
        {
            OracleUdt.SetValue(con, pUdt, 0, Array, StatusArray);
        }
        #endregion
    
        #region INullable
        public bool IsNull { get; set; }
    
        public static StringListCustomType Null
        {
            get
            {
                StringListCustomType obj = new StringListCustomType();
                obj.IsNull = true;
                return obj;
            }
        }
        #endregion
    }
    

    StringListCustomTypeFactory.cs

    [OracleCustomTypeMapping(StringListCustomType.Name)]
    public class StringListCustomTypeFactory : IOracleCustomTypeFactory, IOracleArrayTypeFactory
    {
        #region IOracleCustomTypeFactory
        IOracleCustomType IOracleCustomTypeFactory.CreateObject()
        {
            return new StringListCustomType();
        }
        #endregion
    
        #region IOracleArrayTypeFactory
        Array IOracleArrayTypeFactory.CreateArray(int numElems)
        {
            return new string[numElems];
        }
    
        Array IOracleArrayTypeFactory.CreateStatusArray(int numElems)
        {
            return new OracleUdtStatus[numElems];
        }
        #endregion
    }
    

    然后你可以像这样添加一个参数:

    dbParameter = new OracleParameter();
    dbParameter.ParameterName = "myparamname";
    dbParameter.UdtTypeName = StringListCustomType.Name;
    dbParameter.OracleDbType = OracleDbType.Array;
    
    if (myarray != null)
    {
        StringListCustomType newArray = new StringListCustomType();
        newArray.Array = myarray;
        dbParameter.Value
    }
    else
    {
        dbParameter.Value = StringListCustomType.Null;
    }
    

    您的查询将如下所示:

    SELECT * 
      FROM MYSCHEMA.MYTABLE 
     WHERE MYVARCHARFIELD IN (SELECT COLUMN_VALUE 
                                FROM TABLE(CAST(:myparamname AS MYSCHEMA.VARCHAR2_TAB_T)))
    

    【讨论】:

      【解决方案6】:

      这样您的查询将是:

      SELECT * FROM TableName WHERE UserName IN ('''Ben'', ''Sam''');

      这两个名称将作为一个值输入。

      查看 asktom.oracle.com 中的此线程,了解如何获取动态列表。

      http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:210612357425

      【讨论】:

        【解决方案7】:

        老问题,但我想分享我的代码。只是一个简单的方法来创建一个可以连接到动态生成的 sql 的字符串,而不会失去绑定参数的性能和安全性:

            /// <summary>
            /// 1 - Given an array of int, create one OracleParameter for each one and assigin value, unique named using uniqueParName
            /// 2 - Insert the OracleParameter created into the ref list.
            /// 3 - Return a string to be used to concatenate to the main SQL
            /// </summary>
            /// <param name="orclParameters"></param>
            /// <param name="lsIds"></param>
            /// <param name="uniqueParName"></param>
            /// <returns></returns>
            private static string InsertParameters(ref List<OracleParameter> orclParameters, int[] lsIds, string uniqueParName)
            {
                string strParametros = string.Empty;
        
                for (int i = 0; i <= lsIds.Length -1; i++)
                {
                    strParametros += i == 0 ? ":" + uniqueParName + i : ", :" + uniqueParName + i;
        
                    OracleParameter param = new OracleParameter(uniqueParName + i.ToString(), OracleType.Number);
                    param.Value = lsIds[i];
                    orclParameters.Add(param);
                }
                return strParametros;
            }
        

        并像这样使用:

        List<OracleParameter> parameterList = new List<OracleParameter>();
        int[] idAr = new int[] { 1, 2, 3, 4};
        string idStr = InsertParameters(ref parameterList, idAr, "idTest");
        string SQL = " SELECT name FROM tblTest WHERE idTest in ( " + idStr + " )  ";
        

        【讨论】:

          【解决方案8】:
          SELECT * FROM Clients 
          WHERE id IN ( 
          SELECT trim(regexp_substr(str, '[^,]+', 1, level)) strRows 
          FROM (SELECT :Pram as str from dual ) t   
          CONNECT BY instr(str, ',', 1, level -1) >0);
          

          【讨论】:

            【解决方案9】:

            我在搜索同一个问题时遇到了它,所以我想添加一个我认为有帮助的答案,因为我不相信上述方法真的能实现它:

            http://forums.asp.net/t/1195359.aspx/1?Using%20bind%20variable%20with%20an%20IN%20clause

            如果链接失效,我也会在这里添加答案:

            Re: 使用带有 IN 子句的绑定变量 2007 年 12 月 17 日 06:56 PM|链接

            您必须单独添加每个值。像这样的东西(写在 Mac,所以我无法测试它)

            string sql = "select id, client_id as ClientID, acct_nbr as AcctNbr from acct where acct_nbr in ( %params% )";
                    OracleConnection conn = new OracleConnection(DBConnection);
                    OracleCommand cmd = new OracleCommand();
            
            
                    List<string> params=new List<string>();
            
                    foreach(string acctNbr in AcctNbrs.Split(','))
                    {
                        string paramName=":acctNbr" + params.Count.Tostring();
                        params.Add(paramName)
                        OracleParameter parms = new OracleParameter(paramName, OracleType.VarChar);
                        parms.Value = acctNbr;
                        cmd.Parameters.Add(parms);
            
                    }
            
                    cmd.CommandType = CommandType.Text;
                    cmd.CommandText = sql.Replace("%params%",params.ToArray().Join(","));
                    cmd.Connection = conn;
            
                    OracleDataAdapter da = new OracleDataAdapter(cmd);
                    da.Fill(ds);
            

            【讨论】:

              【解决方案10】:

              虽然这个问题很老,但我解释了我在我的案例中解决它的方式。 该示例在 Vb.NET 中,但我认为它同样可以理解。 解决方案,一般来说,是通过程序将 IN 语句转换为一系列带有各自参数的 OR 条件。

              从包含搜索值的字符串开始,用逗号分隔,没有 Oracle 将使用的字符串引号,并假设您有一个已定义的 OracleCommand,我在示例中将其称为 oraCommando。 我所做的是通过拆分具有搜索值的字符串来组装查询字符串,根据需要创建尽可能多的 OR 比较,并使用它们各自的参数为它们分配值。 在为查询字符串集合中的参数命名时要特别注意,不要在名称和末尾的数字之间留有空格,使它们都是不同的名称。

              strCommand & = " UserName = :userName" & puntParam & " "
              

              示例代码是:

              dim param as string = "Ben, Sam"
              dim strCommand as string = "SELECT * FROM TableName WHERE"
              dim puntParam as integer = 0
              for each paramAnali as string in split (param, ",")
                  puntParam + = 1
                  if puntParam> 1 then
                      strCommand & = "or"
                  end if
                  strCommand & = "UserName =: userName" & puntParam.ToString () & ""
              
                  Dim paramNew As New OracleParameter With {
                    .ParameterName = "userName" & puntParam.ToString (),
                    .OracleDbType = OracleDbType.Varchar2,
                    .Direction = ParameterDirection.Input,
                    .Value = Trim (paramAnali)}
              
                  oraCommando.Parameters.Add (paramNew)
              
              next
              

              另外,为了不出现参数绑定问题,必须指示 Oracle 命令按名称进行“绑定”。

              oraCommando.BindByName = True
              

              这样,查询自动调整到接收到的值的个数,无需调整代码。

              【讨论】:

                【解决方案11】:

                在 ORACLE 中非常简单。

                以下步骤:

                1.在oracle中创建默认类型

                CREATE OR REPLACE TYPE t_varchar_tab AS TABLE OF VARCHAR2(4000);
                

                2.在oracle中创建函数,用于将给定的字符串如“a,b,c”分离成''a','b','c''

                CREATE OR REPLACE FUNCTION in_list(p_in_list  IN  VARCHAR2)ETURNt_varchar_tab
                
                AS
                
                  l_tab   t_varchar_tab := t_varchar_tab();
                
                  l_text  VARCHAR2(32767) := p_in_list || ',' ;
                
                  l_idx   NUMBER;
                
                BEGIN
                
                  LOOP
                
                    l_idx := INSTR(l_text, ',');
                
                    EXIT WHEN NVL(l_idx, 0) = 0;
                
                    l_tab.extend;
                
                    l_tab(l_tab.last) := TRIM(SUBSTR(l_text, 1, l_idx - 1));
                
                    l_text := SUBSTR(l_text, l_idx + 1);
                
                  END LOOP;
                
                
                  RETURN l_tab;
                
                END;
                

                3:然后使用以下查询从表中提取数据

                SELECT * FROM TABLE_NAME EMP WHERE  IN (SELECT * FROM TABLE(in_list(i_input1)));
                

                4.输入参数从c#.net传递到oracle SP 喜欢

                 cmd.Parameters.Add("i_input1", OracleType.VarChar, 50).Value = "S1,S2";
                

                【讨论】:

                  【解决方案12】:

                  解决方案不应包含逗号字符或单引号、双引号。我建议您使用临时表,然后从中进行选择。使用常规命令参数填充临时表。

                  【讨论】:

                    【解决方案13】:

                    其实我也想试试这段代码:

                    string query = "SELECT * FROM TableName WHERE UserName IN (:Pram)";
                    param = new string[2] {"Ben", "Sam" };
                    OracleCommand command = new OracleCommand(query, conn);
                    command.ArrayBindCount = param.Length;
                    command.Parameters.Add(":Pram", OracleType.VarChar).Value = param;
                    

                    【讨论】:

                    • 这个解决方案对我不起作用。相当于:"SELECT * FROM TableName WHERE UserName IN ("Ben")" - 只取表中的第一个元素
                    猜你喜欢
                    • 2022-12-11
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-11-17
                    • 2016-06-08
                    • 2013-05-15
                    相关资源
                    最近更新 更多