#c# #tsql #ado.net
#c# #tsql #ado.net
Вопрос:
Я использую ADO.NET чтобы удалить некоторые данные из базы данных следующим образом:
using (SqlConnection conn = new SqlConnection(_connectionString))
{
try
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("Delete from Table where ID in (@idList);", conn))
{
cmd.Parameters.Add("@idList", System.Data.SqlDbType.VarChar, 100);
cmd.Parameters["@idList"].Value = stratIds;
cmd.CommandTimeout = 0;
cmd.ExecuteNonQuery();
}
}
catch (Exception e)
{
//_logger.LogMessage(eLogLevel.ERROR, DateTime.Now, e.ToString());
}
finally
{
conn.Close();
}
}
Этот код выполняется без исключений, но данные не были удалены из базы данных.
Когда я использую тот же алгоритм для вставки или обновления базы данных, все в порядке.
Кто-нибудь знает, в чем проблема?
Ответ №1:
Вы не можете сделать это в обычном TSQL, поскольку сервер обрабатывает @idList
как единственное значение, которое содержит запятые. Однако, если вы используете List<int>
, вы можете использовать dapper-dot-net, с
connection.Execute("delete from Table where ID in @ids", new { ids=listOfIds });
dapper выясняет, что вы имеете в виду, и генерирует соответствующую параметризацию.
Другой вариант — отправить строку и написать UDF для выполнения операции «разделения», а затем использовать этот UDF в своем запросе:
delete from Table where ID in (select Item from dbo.Split(@ids))
Комментарии:
1. Вы сказали — «Вы не можете этого сделать в обычном TSQL, поскольку сервер обрабатывает @idList как единственное значение, которое содержит запятые» — На самом деле я могу использовать код
SqlCommand cmd = new SqlCommand("Delete from Table where ID in (" stratIds ");", conn)
, и все работает правильно. Итак, я думаю, что могу заменить string stratIds на param.2. @Kate — Я не понимаю комментарий
3. @Kate, параметры SQL так не работают, ваш пример с использованием конкатенации строк будет работать, но параметры не заменяются и обрабатываются как конкатенация строк.
4. @Kate — ваша стратегия конкатенации строк — это ожидающая своего часа SQL-инъекционная атака.
5. Вот почему я хочу использовать SqlParametr
Ответ №2:
Согласно Split-UDF от Marc, это одна из рабочих реализаций:
CREATE FUNCTION [dbo].[Split]
(
@ItemList NVARCHAR(MAX),
@delimiter CHAR(1)
)
RETURNS @IDTable TABLE (Item VARCHAR(50))
AS
BEGIN
DECLARE @tempItemList NVARCHAR(MAX)
SET @tempItemList = @ItemList
DECLARE @i INT
DECLARE @Item NVARCHAR(4000)
SET @tempItemList = REPLACE (@tempItemList, ' ', '')
SET @i = CHARINDEX(@delimiter, @tempItemList)
WHILE (LEN(@tempItemList) > 0)
BEGIN
IF @i = 0
SET @Item = @tempItemList
ELSE
SET @Item = LEFT(@tempItemList, @i - 1)
INSERT INTO @IDTable(Item) VALUES(@Item)
IF @i = 0
SET @tempItemList = ''
ELSE
SET @tempItemList = RIGHT(@tempItemList, LEN(@tempItemList) - @i)
SET @i = CHARINDEX(@delimiter, @tempItemList)
END
RETURN
END
И вот как вы могли бы это назвать:
DELETE FROM Table WHERE (ID IN (SELECT Item FROM dbo.Split(@idList, ',')));
Комментарии:
1. выигрышная стратегия в sql уже давно. 1
2. обязательно упомяните основополагающий: sommarskog.se/arrays-in-sql-2005.html классический
Ответ №3:
Я хочу придать этому обсуждению немного больше контекста. Похоже, это подпадает под тему «как мне получить несколько строк данных в sql«. В случае @Kate она пытается УДАЛИТЬ-WHERE-IN, но полезные стратегии для этого пользовательского случая очень похожи на стратегии для UPDATE-FROM-WHERE-IN или INSERT-INTO-SELECT FROM. На мой взгляд, существует несколько основных стратегий.
Конкатенация строк
Это самый старый и простой способ. Вы делаете простой «SELECT * FROM MyTable, ГДЕ ИДЕНТИФИКАТОР В (» someCSVString «);»
- Супер просто
- Самый простой способ открыть себя для атаки с использованием SQL-инъекций.
- Усилия, которые вы потратили на очистку строки, было бы лучше потратить на одно из других решений
Средство сопоставления объектов
Как предложил @MarcGravell, вы можете использовать что-то вроде dapper-dot-net, точно так же, как работали бы Linq-to-sql или Entity Framework. Dapper позволяет вам делать connection.Execute("delete from MyTable where ID in @ids", new { ids=listOfIds });
аналогично Linq позволил бы вам сделать что-то вроде from t in MyTable where myIntArray.Contains( t.ID )
- Средства отображения объектов великолепны.
- Однако, если ваш проект является прямым ADO, это довольно серьезное изменение для выполнения простой задачи.
Разделение CSV
В этой стратегии вы передаете строку CSV в SQL, будь то ad-hoc или в качестве параметра хранимой процедуры. Строка обрабатывается табличным значением UDF, которое возвращает значения в виде таблицы с одним столбцом.
- Это была выигрышная стратегия со времен SQL-2000
- @TimSchmelter привел отличный пример функции разделения csv.
- Если вы загуглите это, то найдете сотни статей, в которых рассматриваются все аспекты, от основ до анализа производительности при различных длинах строк.
Параметры, имеющие табличное значение
В SQL 2008 могут быть определены пользовательские «типы таблиц». Как только тип таблицы определен, его можно создать в ADO и передать в качестве параметра.
- Преимущество здесь в том, что это работает для большего количества сценариев, чем просто список целых чисел — он может поддерживать несколько столбцов
- строго типизированный
- перенесите обработку строк обратно на уровень / язык, который в этом довольно хорош.
- Это довольно большая тема, но табличные параметры в SQL Server 2008 (ADO.NET ) является хорошей отправной точкой.