Как оптимизировать SQL XQuery - PullRequest
3 голосов
/ 15 августа 2011

У меня есть такая иерархия 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 секунды, и если я продолжу ВНУТРЕННИЕ СОЕДИНЕНИЯ, это прибавит время. Я пытался создать ПЕРВИЧНЫЙ индекс для столбца 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 возможностей

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

1 Ответ

1 голос
/ 15 августа 2011

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