OracleParameter 和 IN 子句

Posted

技术标签:

【中文标题】OracleParameter 和 IN 子句【英文标题】:OracleParameter and IN Clause 【发布时间】: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 应该生成这个而不是应用程序。这也适用于任何类型的字符串分隔符 ' " 【参考方案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 更轻松地做到这一点:

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

    CREATE TYPE t_varchar2 AS TABLE OF VARCHAR2(4000);
    

    创建集合参数:

    OracleParameter param = new OracleParameter();
    param.OracleDbType = OracleDbType.Varchar2;
    param.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
    

    填写参数:

    param = new string[2] "Ben", "Sam" ;
    

    将参数绑定到以下查询:

    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://***.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")" - 只取表中的第一个元素

以上是关于OracleParameter 和 IN 子句的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 视图 OR vs IN 子句

MySQL使用select in or子句的结果[重复]

sql中=all和 in的区别

在 OR 子句 SQL 中使用别名 [重复]

按 SQL IN() 子句中的值顺序排序

配置数据源Where子句怎么添加多个查询条件并且用OR相连