Разложение типа XML на набор строк в хранимой процедуре с использованием node () - PullRequest
3 голосов
/ 24 августа 2010

У меня есть SP. Я вызываю следующие примеры способов (вызов не из SQL, а из программы .net)

или

    -- run with a few grantees
    exec someproc 99999, '<grantees><grantee id="99"/><grantee id="100"/><grantee id="101"/></grantees>'
   -- takes about 1 sec with > 59s on xml decomp

или, возможно,

   -- run with lots of grantees (approx 2000)
   exec someproc 99999, '<grantees><grantee id="99"/><grantee id="100"/>....<grantee id="2001"/></grantees>'
   -- takes about 5 sec with > 4s on xml decomp

или, возможно,

   -- run with mega loads of grantees (approx 12000)
   exec someproc 99999, '<grantees><grantee id="99"/><grantee id="100"/>....<grantee id="12001"/></grantees>'
   -- takes about 1 min with > 59s on xml decomp

И я обнаружил, что декомпозиция xml - самая медленная часть (около 96% запросов в каждом случае - и поверьте мне, я вставляю / удаляю / изменяю тонны данных в остальных из проц).

Мне очень любопытно, является ли мой способ разложения XML наиболее оптимальным способом для заданных входных наборов. Мой критерий использования XML - просто передать SP целое число целых чисел - поэтому любые предложения о лучших путях с благодарностью принимаются.

create procedure someproc(@id int, @users xml = '<grantees/>') as
begin
        -- decompose the users into a row set
        declare @allUsers table (
            id int
        )

        insert into @allUsers (id)
        select distinct grantee.value('@id', 'int') uno 
           from @users.nodes('/grantees/grantee') grantees(grantee)
           where isnull(grantee.value('@id', 'int'), 0) > 0

    select * from @allUsers

    -- other stuff happens
end

Ответы [ 2 ]

2 голосов
/ 16 сентября 2010

Поскольку вы не можете использовать параметр таблицы, попробуйте передать строку CSV, и хранимая процедура разделит его на строки.

Есть много способов разбить строку в SQL Server. В этой статье рассматриваются плюсы и минусы практически каждого метода:

"Массивы и списки в SQL Server 2005 и более поздних версиях, когда параметры табличных значений не обрезают его", Эрланд Соммарског

Вам необходимо создать функцию разделения. Вот как можно использовать функцию разделения:

SELECT
    *
    FROM YourTable                               y
    INNER JOIN dbo.yourSplitFunction(@Parameter) s ON y.ID=s.Value

Я предпочитаю подход с использованием таблиц чисел для разделения строки в TSQL , но существует множество способов разделения строк в SQL Server, см. Предыдущую ссылку, которая объясняет за и против каждого из них.

Чтобы метод таблицы чисел сработал, необходимо выполнить настройку единой таблицы, которая создаст таблицу Numbers, содержащую строки от 1 до 10000:

SELECT TOP 10000 IDENTITY(int,1,1) AS Number
    INTO Numbers
    FROM sys.objects s1
    CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)

После настройки таблицы Numbers создайте эту функцию разделения:

CREATE FUNCTION [dbo].[FN_ListToTable]
(
     @SplitOn  char(1)      --REQUIRED, the character to split the @List string on
    ,@List     varchar(8000)--REQUIRED, the list to split apart
)
RETURNS TABLE
AS
RETURN 
(   ----------------
    --SINGLE QUERY-- --this will not return empty rows
    ----------------
    SELECT
        ListValue
        FROM (SELECT
                  LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(@SplitOn, List2, number+1)-number - 1))) AS ListValue
                  FROM (
                           SELECT @SplitOn + @List + @SplitOn AS List2
                       ) AS dt
                      INNER JOIN Numbers n ON n.Number < LEN(dt.List2)
                  WHERE SUBSTRING(List2, number, 1) = @SplitOn
             ) dt2
        WHERE ListValue IS NOT NULL AND ListValue!=''
);
GO 

Теперь вы можете легко разбить строку CSV на таблицу и присоединиться к ней или использовать ее так, как вам нужно:

CREATE PROCEDURE YourProcedure
(
    @CSV_Param   varchar(1000)
)
AS

--just an example of what you can do
UPDATE t
    SET Col1=...
    FROM dbo.FN_ListToTable(',',@CSV_Param) dt
        INNER JOIN TBL_USERS                 t ON  CAST(dt.value AS INT)=t.id

GO

Просто выберите лучшую функцию разделения строк из статьи (CLR, loop, что угодно), которая работает с вашим большим набором CSV, и вы получите лучшую производительность.

0 голосов
/ 13 сентября 2010

Если все, что вы делаете, - это извлекаете одно поле из коллекции элементов, вам, вероятно, лучше обойдется, просто анализируя XML как строку, извлекая поле в массив и передавая массив SP или вставка значений во временную таблицу и передача имени таблицы в SP. В любом случае, у вас нет движка базы данных, анализирующего XML. Этот подход не самый гибкий, поэтому он может не подходить, если вам нужно сделать это для нескольких различных типов элементов.

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