Параметр OracleParameter и IN
Есть ли способ добавить параметр в предложение IN с помощью System.Data.OracleClient.
Например:
string query = "SELECT * FROM TableName WHERE UserName IN (:Pram)";
OracleCommand command = new OracleCommand(query, conn);
command.Parameters.Add(":Pram", OracleType.VarChar).Value = "'Ben', 'Sam'";
12 ответов
Вы можете обернуть его в метод 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;
}
}
Вы можете сделать это легче с 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)));
Возможно, используя другой подход
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 может быть единственным параметром. Следовательно, его можно использовать на любом языке.
Я знаю, что это спросили некоторое время назад, но не блестящий ответ.
Я хотел бы сделать что-то вроде этого - пожалуйста, извините сырой псевдо-код
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 + ")";
Итак, запрос в конечном итоге имеет в (:1,:2), и каждый из них связаны в отдельности.
Здесь также есть похожий вопрос: Oracle / C#: как использовать переменные связывания с операторами select для возврата нескольких записей?
Вы можете использовать пользовательский тип данных Oracle, подобный следующему:
http://www.c-sharpcorner.com/code/2191/pass-collection-to-oracle-stored-procedure-from-net-layer.aspx
и здесь:
/questions/249664/oracle-v-c-svyazyivat-peremennyie-i-zaprosyi-takie-kak-id-in-1-2-3/249674#249674
Сначала создайте тип в 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)))
Старый вопрос, но я хотел бы поделиться своим кодом. Просто простой способ создать строку, которую вы можете объединить в динамически сгенерированный 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 + " ) ";
Это очень просто в ORACLE.
следующие шаги:
1. создать тип по умолчанию в оракуле
CREATE OR REPLACE TYPE t_varchar_tab AS TABLE OF VARCHAR2(4000);
2.создать в оракуле функцию для разделения заданной строки типа "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 в SP Oracle
cmd.Parameters.Add("i_input1", OracleType.VarChar, 50).Value = "S1,S2";
Таким образом, ваш запрос будет:
SELECT * FROM TableName, ГДЕ UserName IN ('''Ben'', ''Sam''');
Эти два имени будут введены как одно единственное значение.
Загляните в эту ветку на сайте asktom.oracle.com, чтобы узнать, как получить динамику в списке.
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:210612357425
Я столкнулся с этим при поиске того же вопроса, поэтому я хотел бы добавить ответ, который мне показался полезным, так как я не верю, что вышеизложенное действительно достигает этого:
http://forums.asp.net/t/1195359.aspx/1?Using%20bind%20variable%20with%20an%20IN%20clause
Я также добавлю ответ здесь, если ссылка станет недействительной:
Re: Использование переменной связывания с предложением IN 17 декабря 2007 г. 18:56 |LINK
Вы должны добавить каждое значение отдельно. Примерно так (пишу на 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);
Хотя вопрос старый, я объясняю, как я решил его в моем случае. Пример приведен на Vb.NET, но я думаю, что он одинаково понятен. Решение, в общем, состояло в том, чтобы преобразовать оператор IN в серию условий ИЛИ с соответствующими параметрами, все программно.
Начиная с наличия строки с искомыми значениями, разделенными запятыми, БЕЗ строковых кавычек, которые 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
Таким образом, запрос автоматически подстраивается под количество полученных значений без необходимости корректировать код.
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);
Решение не должно содержать запятую, ни одинарные кавычки, ни двойные кавычки. Я предлагаю вам использовать временную таблицу, а затем выбрать из этого. Заполните временную таблицу, используя обычные параметры команды.
На самом деле, я бы тоже попробовал этот код:
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;