Это проблема, с которой я недавно столкнулся. Мне нужно было выбрать из таблицы те строки, которые пересекались с большим списком первичных ключей. Вопрос заключался в том, как эффективно отправить большой список ключей на сервер SQL в форме, которая быстра и эффективна для использования.
Что действительно хорошо подходит для такого типа ситуаций, так это тип данных XML. Если вы создаете хранимую процедуру, которая принимает один параметр типа XML, вы можете предварительно отформатировать входные данные во фрагмент XML. В качестве примера, скажем, фрагмент XML будет выглядеть так:
<a>
<b>1</b>
<b>3</b>
<b>7</b>
<b>14</b>
<b>147</b>
</a>
Я дал элементам короткие имена ("a" и "b"), потому что более длинное имя будет означать больше байтов для передачи от клиента к серверу SQL. Вот как вы должны выбрать все содержимое элементов «b» в качестве набора записей:
declare @x xml
set @x = '<a><b>1</b><b>3</b><b>7</b><b>14</b><b>147</b></a>'
select t.item.value('.', 'int') from @x.nodes('//a/b') as t(item)
Хотя синтаксис является загадочным, тип XML может запрашиваться как таблица. Теперь вы должны увидеть, куда это идет. Если мы можем запросить тип XML как таблицу, мы можем пересечь его с другой таблицей:
select * from MyTable where ID in
(select t.item.value('.', 'int') from @x.nodes('//a/b') as t(item))
или используя соединение
;with cte as
(select ID = t.item.value('.', 'int') from @x.nodes('//a/b') as t(item))
select * from MyTable inner join cte on MyTable.ID = cte.ID
Вам нужно запустить обе версии, чтобы узнать, какая из них будет быстрее для ваших данных. Я считаю, что JOIN работает быстрее с моими данными. Вот хранимая процедура, которая принимает тип XML в качестве входных данных и возвращает обратно выбранные строки:
create procedure MyProc @x xml as
begin
set nocount on
;with cte as
(select ID = t.item.value('.', 'int') from @x.nodes('//a/b') as t(item))
select * from MyTable inner join cte on Table.ID = cte.ID
end
Пример вызова новой хранимой процедуры:
exec MyProc '<a><b>1</b><b>3</b><b>7</b><b>14</b><b>147</b></a>'
Я также обнаружил, что добавление схемы XML для входного фрагмента помогло немного ускорить хранимую процедуру. Я не буду вдаваться в подробности XML-схем здесь, но идея состоит в том, чтобы заранее сообщить SQL, как будет выглядеть фрагмент XML. Вот как мы могли бы ввести нашу схему:
create xml schema collection MyInputSchema as
'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="a">
<xsd:complexType>
<xsd:complexContent>
<xsd:restriction base="xsd:anyType">
<xsd:sequence>
<xsd:element name="b" type="xsd:integer" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
</xsd:schema>'
Теперь мы можем связать эту схему с входом нашей хранимой процедуры следующим образом:
create procedure MyProc @x xml(MyInputSchema) as
begin
set nocount on
;with cte as
(select ID = t.item.value('.', 'int') from @x.nodes('//a/b') as t(item))
select * from MyTable inner join cte on Table.ID = cte.ID
end
Имея все это, я смог отправить фрагмент XML из 43 016 символов с моей клиентской машины на сервер SQL и очень быстро вернуть набор результатов. Я сделал тест 1000 запросов на 10 потоков на общую сумму 10000 запросов. В результате было обработано 72 запроса в секунду. Конечно, ваш доход зависит от вашего аппаратного и программного обеспечения.
ПРИМЕЧАНИЕ. Этот код работает в SQL 2005, а также в 2008 году.