Параметризованные запросы Sql - PullRequest
2 голосов
/ 03 октября 2008

Это орех, который я сейчас ломаю

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

Однако, когда коллекция элементов для построения предложения "in ()" превышает 2100 элементов, это происходит сбой из-за ограничения Sql Server не более 2100 параметров Sql на запрос.

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

Кроме этого, меня очень интересуют альтернативные подходы к решению этой проблемы.

Большое спасибо

Ответы [ 7 ]

5 голосов
/ 03 октября 2008

Один из возможных обходных путей - использовать возможность запрашивать 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 в своем приложении и передайте его в качестве параметра.

1 голос
/ 03 октября 2008

Вы можете использовать класс SqlBulkCopy, который был представлен в .NET 2.0. Это на самом деле очень просто в использовании. Проверьте это:

http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.aspx

1 голос
/ 03 октября 2008

Prevengint вредоносный код SQL:> Используйте хранимую процедуру.

И да, SQL Server 2005 имеет массовую вставку: http://msdn.microsoft.com/en-us/library/ms188365.aspx

1 голос
/ 03 октября 2008

Хм, не зная контекста и больше о данных и о том, как вы используете результаты и проблемы с производительностью, я постараюсь предложить альтернативу. Не могли бы вы разделить на несколько запросов? Сделайте то же самое, что и сейчас, но вместо того, чтобы строить запрос с 2100+ в элементах, постройте два с 1050 в каждом, а затем объедините результаты.

0 голосов
/ 03 октября 2008

SQL Server 2008 будет иметь параметры таблицы. Это тот молот, который тебе нужен.

0 голосов
/ 03 октября 2008

Хорошо, я не уверен, насколько это хорошо для вас или насколько он эффективен, но вот код, который я использовал в прошлом для достижения подобного:

    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, если вам действительно не нужно индексировать исходную строку.

0 голосов
/ 03 октября 2008

Для проблемы массового обновления: взгляните на адаптер данных с таблицей данных в нем. Вы можете установить параметр, который позволяет вставлять / обновлять элементы в таблице в пакетном режиме, и вы можете выбрать количество элементов в пакете MSDN, статья

Похоже, вам следует внимательнее взглянуть на бизнес-проблему или домен, чтобы определить лучший способ фильтрации элементов в вашем запросе. Предложение IN () может быть не лучшим способом сделать это. Возможно, в вашем случае было бы лучше добавить категории данных или фильтры вместо большого списка элементов для включения. Не зная больше о бизнес-проблеме / контексте, трудно сказать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...