Это орех, который я раскалываю в наши дни
Приложение, над которым я работаю, имеет некоторую расширенную обработку для SQL. Одна из операций выбирает различные метаданные для объектов в текущем контексте из разных таблиц на основе имен элементов в коллекции. Для этого выполняется диапазон «select ... from ... where ... in ()», и для предотвращения вредоносного кода SQL используются параметры Sql для создания содержимого предложения «in ()».
Однако, когда набор элементов для создания предложения in () превышает 2100 элементов, это не удается из-за ограничения сервера Sql на максимальное количество параметров Sql в 2100 на запрос.
Один из подходов, который я сейчас пробую, - это создание таблицы #temp для хранения всех имен элементов и последующее присоединение к таблице в исходном запросе вместо использования «where in ()». Это заставляет меня ломать голову над тем, как заполнить таблицу именами элементов, хранящимися в массиве в коде .NET. Конечно, должен быть какой-то массовый способ вставить все, а не выпускать отдельную «вставку в» для каждого элемента?
Кроме того, меня очень интересуют альтернативные подходы к решению этой проблемы.
Большое спасибо





Хм, не зная контекста и дополнительной информации о данных и о том, как вы используете результаты и проблемы с производительностью, я попытаюсь предложить альтернативу. Не могли бы вы разбить на несколько запросов? Сделайте то же самое, что и сейчас, но вместо того, чтобы строить запрос с 2100+ элементами, создайте два по 1050 в каждом, а затем объедините результаты.
Один из возможных обходных путей - использовать возможность запрашивать XML и просто отправлять все данные для вашего «входа» в виде столбца xml, а затем присоединяться к нему.
Тот же подход можно использовать для заполнения вашей временной таблицы, но опять же, почему бы просто не использовать ее напрямую.
Вот небольшой пример, который должен проиллюстрировать:
declare @wanted xml
set @wanted = '<ids><id>1</id><id>2</id></ids>'
select *
from (select 1 Id union all select 3) SourceTable
where Id in(select Id.value('.', 'int') from @wanted.nodes('/ids/id') as Foo(Id))
Просто создайте xml в своем приложении и передайте его в качестве параметра.
Мне было бы очень интересно, если бы вы могли подробнее рассказать об этом варианте.
Еще раз спасибо, это похоже на решение, которое стоит изучить. Вопрос только в том, будет ли производительность лучше, чем при использовании таблицы #temp или нет.
Prevengint вредоносный код SQL:> Используйте хранимую процедуру.
И да, в SQL Server 2005 есть массовая вставка: http://msdn.microsoft.com/en-us/library/ms188365.aspx
Знаю, но не все так просто. Базы данных создаются автоматически, и есть некоторые проблемы, связанные с использованием и поддержанием кода в базах данных.
Для проблемы массового обновления: взгляните на адаптер данных с таблицей данных в нем. Вы можете установить параметр, который позволяет вставлять / обновлять элементы в таблице партиями, и вы можете выбрать количество элементов в партии. Статья MSDN
Похоже, вам следует более внимательно изучить бизнес-проблему или домен, чтобы определить лучший способ фильтрации элементов в вашем запросе. Предложение IN () может быть не лучшим способом сделать это. Возможно, в вашем случае будет лучше добавить категории данных или фильтры вместо большого списка элементов для включения. Трудно сказать, не зная больше о бизнес-проблеме / контексте.
Вы можете использовать класс SqlBulkCopy, представленный в .NET 2.0. На самом деле им очень просто пользоваться. Проверить это:
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.aspx
Хорошо, я не уверен, насколько это хорошо для вас и насколько он эффективен, но вот код, который я использовал в прошлом для достижения аналогичного:
CREATE FUNCTION [dbo].[Split](
@list ntext
)
RETURNS @tbl TABLE (listpos int IDENTITY(1, 1) NOT NULL,
number int NOT NULL)
AS
BEGIN
DECLARE @pos int,
@textpos int,
@chunklen smallint,
@str nvarchar(4000),
@tmpstr nvarchar(4000),
@leftover nvarchar(4000)
SET @textpos = 1
SET @leftover = ''
WHILE @textpos <= datalength(@list) / 2
BEGIN
SET @chunklen = 4000 - datalength(@leftover) / 2
SET @tmpstr = ltrim(@leftover + substring(@list, @textpos, @chunklen))
SET @textpos = @textpos + @chunklen
SET @pos = charindex(',', @tmpstr)
WHILE @pos > 0
BEGIN
SET @str = substring(@tmpstr, 1, @pos - 1)
INSERT @tbl (number) VALUES(convert(int, @str))
SET @tmpstr = ltrim(substring(@tmpstr, @pos + 1, len(@tmpstr)))
SET @pos = charindex(',', @tmpstr)
END
SET @leftover = @tmpstr
END
IF ltrim(rtrim(@leftover)) <> ''
INSERT @tbl (number) VALUES(convert(int, @leftover))
RETURN
END
Затем в другой хранимой процедуре вы можете передать строку идентификаторов, разделенных запятыми, например:
select a.number from split('1,2,3') a inner join myothertable b on a.number = b.ID
Как я уже сказал, это, вероятно, действительно плохо, потому что оно включает в себя множество манипуляций со строками, и я не могу вспомнить, откуда я взял функцию ... но она там, чтобы выбирать ...
Я предполагаю, что вы также можете вырезать биты, которые заполняют столбец listpos, если вам действительно не нужно индексировать исходную строку.
Спасибо, но, как я упоминал ранее в другом комментарии, я не ищу SQL-код, поскольку базы данных создаются автоматически по запросу на основе определенных действий пользователя. Поэтому код лучше в .net
SQL Server 2008 будет иметь параметры таблицы. Это молоток, который вам нужен.
На самом деле мне удалось сделать это довольно легко. Мы все равно проведем рефакторинг для запроса к таблице xml или #temp, но спасибо вам за простой выход из беспорядка ...