Как оптимизировать SQL «XQuery»

У меня есть такая иерархия XML в столбце типа XML таблицы с 10 000 записей:

<Root>
     <Elem1>
         <Parent1>
              <Separator>
                  <Child1/>
              </Separator>
         </Parent1>
     </Elem1>
</Root>

У меня такой запрос -

DECLARE @Root VARCHAR(50)
DECLARE @Entity VARCHAR(50)
DECLARE @ParentNode VARCHAR(50)
DECLARE @Separator VARCHAR(50)
DECLARE @ChildNode VARCHAR(50)


SET @Root = 'Root'
SET @Entity = 'Elem1'
SET @ParentNode = 'Parent1'
SET @Separator = 'separator'
SET @ChildNode = 'Child1'

select Parent.P.value('.', 'varchar(max)') as MyValue, 
T.uniqueId, T.XMLCol
from [XMLTable] as T
cross apply 
 (SELECT
         XMLTable.XMLCol.query('(/*[local-name()=sql:variable("@Root")]/*[local-name(.)=sql:variable("@Entity")]/*[local-name(.)=sql:variable("@ParentNode")]/*[local-name(.)=sql:variable("@Separator")]/*[local-name(.)=sql:variable("@ChildNode")])[1]'
 )  as Parent(P)

Как я могу дополнительно оптимизировать этот запрос. В настоящее время это занимает 2 секунды, и если я буду делать дальше INNER JOINS, это увеличит время. Я попытался создать ОСНОВНОЙ индекс для столбца XML, но это занимает больше времени!

РЕДАКТИРОВАТЬ. Если я жестко запрограммирую путь вместо использования переменных, это займет меньше секунды. Но я хочу, чтобы это было в функции с табличным значением, и я не могу жестко указать путь?


РЕДАКТИРОВАТЬ - Решение

select x.value('(Parent1/Separator1/Child1)[1]', 'varchar(max)') as Col1,
x.value('(Parent2/Separator2/Child2)[1]', 'varchar(max)') as Col2,
x.value('(Parent3)[1]', 'varchar(max)') as Col3
from [XMLTable] T 
cross apply T.XMLCOL.nodes('/Root/Elem1') a(x)

Приведенный выше запрос занимает примерно одну секунду. Кажется, он самый быстрый из всех. Таким образом, вместо использования табличной функции с параметрами приведенный выше запрос может быть динамически подготовлен и выполнен на лету с использованием возможности ADO.NET

Поправьте меня пожалуйста...?


person Angshuman Agarwal    schedule 15.08.2011    source источник


Ответы (1)


Если вам нужно только одно значение из каждой строки, нет необходимости использовать cross apply.

select XMLCol.value('(/*[local-name()=sql:variable("@Root")]
                      /*[local-name(.)=sql:variable("@Entity")]
                      /*[local-name(.)=sql:variable("@ParentNode")]
                      /*[local-name(.)=sql:variable("@Separator")]
                      /*[local-name(.)=sql:variable("@ChildNode")])[1]', 'varchar(max)')
from XMLTable

Другой способ получить то же самое — использовать FLWOR. В моих ограниченных тестах это будет работать немного быстрее.

select XMLCol.value('(for $n1 in /*,
                          $n2 in $n1/*,
                          $n3 in $n2/*,
                          $n4 in $n3/*,
                          $n5 in $n4/*
                      where $n1[local-name(.) = sql:variable("@Root")] and
                            $n2[local-name(.) = sql:variable("@Entity")] and
                            $n3[local-name(.) = sql:variable("@ParentNode")] and
                            $n4[local-name(.) = sql:variable("@Separator")] and
                            $n5[local-name(.) = sql:variable("@ChildNode")]
                      return $n5
                     )[1]', 'varchar(max)')
from XMLTable
person Mikael Eriksson    schedule 15.08.2011
comment
При использовании FLOWR есть выигрыш в 2 секунды. Спасибо за это. Тем не менее, я должен получить его вниз. Скоро выложу обновление. - person Angshuman Agarwal; 15.08.2011
comment
@SeeSharp - Конечно. Я не вижу в этом ничего плохого. Если вы можете построить запрос динамически, это будет быстрее, чем поиск в XML с помощью local-name(). - person Mikael Eriksson; 15.08.2011
comment
Спасибо Микаэль. Ваши предложения действительно крутые! Многому научился. Спасибо за такой вклад в сообщество. - person Angshuman Agarwal; 15.08.2011