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 子句的主要内容,如果未能解决你的问题,请参考以下文章