Я пытаюсь передать параметр массива в SQL-команду на C #, как показано ниже, но это не работает. Кто-нибудь встречал это раньше?
string sqlCommand = "SELECT * from TableA WHERE Age IN (@Age)";
SqlConnection sqlCon = new SqlConnection(connectString);
SqlCommand sqlComm = new SqlCommand();
sqlComm.Connection = sqlCon;
sqlComm.CommandType = System.Data.CommandType.Text;
sqlComm.CommandText = sqlCommand;
sqlComm.CommandTimeout = 300;
sqlComm.Parameters.Add("@Age", SqlDbType.NVarChar);
StringBuilder sb = new StringBuilder();
foreach (ListItem item in ddlAge.Items)
{
if (item.Selected)
{
sb.Append(item.Text + ",");
}
}
sqlComm.Parameters["@Age"].Value = sb.ToString().TrimEnd(',');
Ответы:
Вам нужно будет добавлять значения в массив по одному.
var parameters = new string[items.Length]; var cmd = new SqlCommand(); for (int i = 0; i < items.Length; i++) { parameters[i] = string.Format("@Age{0}", i); cmd.Parameters.AddWithValue(parameters[i], items[i]); } cmd.CommandText = string.Format("SELECT * from TableA WHERE Age IN ({0})", string.Join(", ", parameters)); cmd.Connection = new SqlConnection(connStr);
ОБНОВЛЕНИЕ: вот расширенное и многоразовое решение, в котором используется ответ Адама вместе с его предложенным редактированием. Я немного улучшил его и сделал методом расширения, чтобы его было еще проще вызывать.
public static class SqlCommandExt { /// <summary> /// This will add an array of parameters to a SqlCommand. This is used for an IN statement. /// Use the returned value for the IN part of your SQL call. (i.e. SELECT * FROM table WHERE field IN ({paramNameRoot})) /// </summary> /// <param name="cmd">The SqlCommand object to add parameters to.</param> /// <param name="paramNameRoot">What the parameter should be named followed by a unique value for each value. This value surrounded by {} in the CommandText will be replaced.</param> /// <param name="values">The array of strings that need to be added as parameters.</param> /// <param name="dbType">One of the System.Data.SqlDbType values. If null, determines type based on T.</param> /// <param name="size">The maximum size, in bytes, of the data within the column. The default value is inferred from the parameter value.</param> public static SqlParameter[] AddArrayParameters<T>(this SqlCommand cmd, string paramNameRoot, IEnumerable<T> values, SqlDbType? dbType = null, int? size = null) { /* An array cannot be simply added as a parameter to a SqlCommand so we need to loop through things and add it manually. * Each item in the array will end up being it's own SqlParameter so the return value for this must be used as part of the * IN statement in the CommandText. */ var parameters = new List<SqlParameter>(); var parameterNames = new List<string>(); var paramNbr = 1; foreach (var value in values) { var paramName = string.Format("@{0}{1}", paramNameRoot, paramNbr++); parameterNames.Add(paramName); SqlParameter p = new SqlParameter(paramName, value); if (dbType.HasValue) p.SqlDbType = dbType.Value; if (size.HasValue) p.Size = size.Value; cmd.Parameters.Add(p); parameters.Add(p); } cmd.CommandText = cmd.CommandText.Replace("{" + paramNameRoot + "}", string.Join(",", parameterNames)); return parameters.ToArray(); } }
Это называется так ...
var cmd = new SqlCommand("SELECT * FROM TableA WHERE Age IN ({Age})"); cmd.AddArrayParameters("Age", new int[] { 1, 2, 3 });
Обратите внимание, что "{Age}" в операторе sql совпадает с именем параметра, которое мы отправляем в AddArrayParameters. AddArrayParameters заменит значение правильными параметрами.
источник
var paramPlaceholder = "{" & paramNameRoot & "}";
Debug.Assert(cmd.CommandText.Contains(paramPlaceholder), "Parameter Name Root must exist in the Source Query");
Это должно помочь разработчикам, если они забудут сопоставить paramNameRoot с запросом.Я хотел расширить ответ Брайана, чтобы его можно было легко использовать в других местах.
/// <summary> /// This will add an array of parameters to a SqlCommand. This is used for an IN statement. /// Use the returned value for the IN part of your SQL call. (i.e. SELECT * FROM table WHERE field IN (returnValue)) /// </summary> /// <param name="sqlCommand">The SqlCommand object to add parameters to.</param> /// <param name="array">The array of strings that need to be added as parameters.</param> /// <param name="paramName">What the parameter should be named.</param> protected string AddArrayParameters(SqlCommand sqlCommand, string[] array, string paramName) { /* An array cannot be simply added as a parameter to a SqlCommand so we need to loop through things and add it manually. * Each item in the array will end up being it's own SqlParameter so the return value for this must be used as part of the * IN statement in the CommandText. */ var parameters = new string[array.Length]; for (int i = 0; i < array.Length; i++) { parameters[i] = string.Format("@{0}{1}", paramName, i); sqlCommand.Parameters.AddWithValue(parameters[i], array[i]); } return string.Join(", ", parameters); }
Вы можете использовать эту новую функцию следующим образом:
SqlCommand cmd = new SqlCommand(); string ageParameters = AddArrayParameters(cmd, agesArray, "Age"); sql = string.Format("SELECT * FROM TableA WHERE Age IN ({0})", ageParameters); cmd.CommandText = sql;
Изменить: вот общий вариант, который работает с массивом значений любого типа и может использоваться как метод расширения:
public static class Extensions { public static void AddArrayParameters<T>(this SqlCommand cmd, string name, IEnumerable<T> values) { name = name.StartsWith("@") ? name : "@" + name; var names = string.Join(", ", values.Select((value, i) => { var paramName = name + i; cmd.Parameters.AddWithValue(paramName, value); return paramName; })); cmd.CommandText = cmd.CommandText.Replace(name, names); } }
Затем вы можете использовать этот метод расширения следующим образом:
var ageList = new List<int> { 1, 3, 5, 7, 9, 11 }; var cmd = new SqlCommand(); cmd.CommandText = "SELECT * FROM MyTable WHERE Age IN (@Age)"; cmd.AddArrayParameters("Age", ageList);
Убедитесь, что вы установили CommandText перед вызовом AddArrayParameters.
Также убедитесь, что имя вашего параметра не будет частично соответствовать чему-либо еще в вашем заявлении (например, @AgeOfChild)
источник
AddWithValue
функцией, есть ли шанс это исправить?Если вы можете использовать такой инструмент, как "dapper", это может быть просто:
int[] ages = { 20, 21, 22 }; // could be any common list-like type var rows = connection.Query<YourType>("SELECT * from TableA WHERE Age IN @ages", new { ages }).ToList();
Dapper развернет его по индивидуальным параметрам за вас .
источник
'{1,2,3}'
аргументы стиля для функции (не предложение WHERE IN), но я бы предпочел использовать простой ODBC, если нет хлопот с массивом. Полагаю, в этом случае мне также понадобится Dapper ODBC. Вот что он хочет тянуть. snipboard.io/HU0RpJ.jpg . Возможно, мне стоит прочитать больше о Dapper ...Если вы используете MS SQL Server 2008 и выше, вы можете использовать возвращающие табличное значение параметры, как описано здесь http://www.sommarskog.se/arrays-in-sql-2008.html .
1. Создайте тип таблицы для каждого типа параметра, который вы будете использовать.
Следующая команда создает тип таблицы для целых чисел:
create type int32_id_list as table (id int not null primary key)
2. Реализуйте вспомогательные методы.
public static SqlCommand AddParameter<T>(this SqlCommand command, string name, IEnumerable<T> ids) { var parameter = command.CreateParameter(); parameter.ParameterName = name; parameter.TypeName = typeof(T).Name.ToLowerInvariant() + "_id_list"; parameter.SqlDbType = SqlDbType.Structured; parameter.Direction = ParameterDirection.Input; parameter.Value = CreateIdList(ids); command.Parameters.Add(parameter); return command; } private static DataTable CreateIdList<T>(IEnumerable<T> ids) { var table = new DataTable(); table.Columns.Add("id", typeof (T)); foreach (var id in ids) { table.Rows.Add(id); } return table; }
3. Используйте это так
cmd.CommandText = "select * from TableA where Age in (select id from @age)"; cmd.AddParameter("@age", new [] {1,2,3,4,5});
источник
table.Rows.Add(id);
приводит к небольшому запаху кода при использовании SonarQube. Я использовал эту альтернативу внутри Еогеасп:var row = table.NewRow(); row["id"] = id; table.Rows.Add(row);
.Поскольку есть метод на
SqlCommand.Parameters.AddWithValue(parameterName, value)
Возможно, было бы удобнее создать метод, принимающий параметр (имя) для замены и список значений. Это не на уровне параметров (например, AddWithValue ), а на самой команде, поэтому лучше называть ее AddParametersWithValues, а не просто AddWithValues :
запрос:
SELECT * from TableA WHERE Age IN (@age)
Применение:
sqlCommand.AddParametersWithValues("@age", 1, 2, 3);
метод расширения:
public static class SqlCommandExtensions { public static void AddParametersWithValues<T>(this SqlCommand cmd, string parameterName, params T[] values) { var parameterNames = new List<string>(); for(int i = 0; i < values.Count(); i++) { var paramName = @"@param" + i; cmd.Parameters.AddWithValue(paramName, values.ElementAt(i)); parameterNames.Add(paramName); } cmd.CommandText = cmd.CommandText.Replace(parameterName, string.Join(",", parameterNames)); } }
источник
Хочу предложить другой способ решения ограничения с помощью оператора IN.
Например, у нас есть следующий запрос
select * from Users U WHERE U.ID in (@ids)
Мы хотим передать несколько идентификаторов для фильтрации пользователей. К сожалению, это невозможно сделать с C # простым способом. Но у меня есть обходной путь с помощью функции "string_split". Нам нужно немного переписать наш запрос на следующий.
declare @ids nvarchar(max) = '1,2,3' SELECT * FROM Users as U CROSS APPLY string_split(@ids, ',') as UIDS WHERE U.ID = UIDS.value
Теперь мы можем легко передать одно параметрическое перечисление значений, разделенных запятыми.
источник
Передача массива элементов в качестве свернутого параметра в предложение WHERE..IN завершится ошибкой, поскольку запрос примет форму
WHERE Age IN ("11, 13, 14, 16")
.Но вы можете передать свой параметр как массив, сериализованный в XML или JSON:
Используя
nodes()
метод:StringBuilder sb = new StringBuilder(); foreach (ListItem item in ddlAge.Items) if (item.Selected) sb.Append("<age>" + item.Text + "</age>"); // actually it's xml-ish sqlComm.CommandText = @"SELECT * from TableA WHERE Age IN ( SELECT Tab.col.value('.', 'int') as Age from @Ages.nodes('/age') as Tab(col))"; sqlComm.Parameters.Add("@Ages", SqlDbType.NVarChar); sqlComm.Parameters["@Ages"].Value = sb.ToString();
Используя
OPENXML
метод:using System.Xml.Linq; ... XElement xml = new XElement("Ages"); foreach (ListItem item in ddlAge.Items) if (item.Selected) xml.Add(new XElement("age", item.Text); sqlComm.CommandText = @"DECLARE @idoc int; EXEC sp_xml_preparedocument @idoc OUTPUT, @Ages; SELECT * from TableA WHERE Age IN ( SELECT Age from OPENXML(@idoc, '/Ages/age') with (Age int 'text()') EXEC sp_xml_removedocument @idoc"; sqlComm.Parameters.Add("@Ages", SqlDbType.Xml); sqlComm.Parameters["@Ages"].Value = xml.ToString();
Это немного больше на стороне SQL, и вам нужен правильный XML (с root).
Используя
OPENJSON
метод (SQL Server 2016+):using Newtonsoft.Json; ... List<string> ages = new List<string>(); foreach (ListItem item in ddlAge.Items) if (item.Selected) ages.Add(item.Text); sqlComm.CommandText = @"SELECT * from TableA WHERE Age IN ( select value from OPENJSON(@Ages))"; sqlComm.Parameters.Add("@Ages", SqlDbType.NVarChar); sqlComm.Parameters["@Ages"].Value = JsonConvert.SerializeObject(ages);
Обратите внимание, что для последнего метода вам также необходимо иметь уровень совместимости 130+.
источник
Обзор: используйте DbType, чтобы установить тип параметра.
var parameter = new SqlParameter(); parameter.ParameterName = "@UserID"; parameter.DbType = DbType.Int32; parameter.Value = userID.ToString(); var command = conn.CreateCommand() command.Parameters.Add(parameter); var reader = await command.ExecuteReaderAsync()
источник
Использование
.AddWithValue()
, так:sqlComm.Parameters.AddWithValue("@Age", sb.ToString().TrimEnd(','));
В качестве альтернативы вы можете использовать это:
sqlComm.Parameters.Add( new SqlParameter("@Age", sb.ToString().TrimEnd(',')) { SqlDbType = SqlDbType. NVarChar } );
Ваш общий образец кода будет выглядеть следующим образом:
string sqlCommand = "SELECT * from TableA WHERE Age IN (@Age)"; SqlConnection sqlCon = new SqlConnection(connectString); SqlCommand sqlComm = new SqlCommand(); sqlComm.Connection = sqlCon; sqlComm.CommandType = System.Data.CommandType.Text; sqlComm.CommandText = sqlCommand; sqlComm.CommandTimeout = 300; StringBuilder sb = new StringBuilder(); foreach (ListItem item in ddlAge.Items) { if (item.Selected) { sb.Append(item.Text + ","); } } sqlComm.Parameters.AddWithValue("@Age", sb.ToString().TrimEnd(',')); // OR // sqlComm.Parameters.Add(new SqlParameter("@Age", sb.ToString().TrimEnd(',')) { SqlDbType = SqlDbType. NVarChar });
источник
Вот небольшой вариант ответа Брайана, который может оказаться полезным кому-то другому. Берет список ключей и помещает его в список параметров.
//keyList is a List<string> System.Data.SqlClient.SqlCommand command = new System.Data.SqlClient.SqlCommand(); string sql = "SELECT fieldList FROM dbo.tableName WHERE keyField in ("; int i = 1; foreach (string key in keyList) { sql = sql + "@key" + i + ","; command.Parameters.AddWithValue("@key" + i, key); i++; } sql = sql.TrimEnd(',') + ")";
источник
пытаться
sqlComm.Parameters["@Age"].Value = sb.ToString().Replace(","," ");
источник
попробуй вот так
StringBuilder sb = new StringBuilder(); foreach (ListItem item in ddlAge.Items) { if (item.Selected) { string sqlCommand = "SELECT * from TableA WHERE Age IN (@Age)"; SqlConnection sqlCon = new SqlConnection(connectString); SqlCommand sqlComm = new SqlCommand(); sqlComm.Connection = sqlCon; sqlComm.CommandType = System.Data.CommandType.Text; sqlComm.CommandText = sqlCommand; sqlComm.CommandTimeout = 300; sqlComm.Parameters.Add("@Age", SqlDbType.NVarChar); sb.Append(item.Text + ","); sqlComm.Parameters["@Age"].Value = sb.ToString().TrimEnd(','); } }
источник