Поиск элемента *без* данного дочернего элемента (где дочерний элемент является переменным)

В SQL Server 2008 я хочу идентифицировать элементы, которые не содержат заданный дочерний элемент в случае, когда имя дочернего элемента является динамическим. В этом XML все три элемента li содержат дочерние элементы F1 и F2, а второй и третий элементы li do не содержат элемент edges. Я могу найти элементы li без дочерних элементов edges, если заранее известно имя элемента edges — см. exist< /em> запрос ниже, который возвращает 1, как и должно быть.

declare @x xml = '<data>
  <li>
    <F1>ellipse</F1>
    <F2 />
    <edges/>
  </li>
  <li>
    <F1>triangle</F1>
    <F2>3</F2>
  </li>
  <li>
    <F1>square</F1>
    <F2>4</F2>
  </li>
</data>';

select @x.exist('/data/li[not(F1)]') -- returns 0, there is no li without an F1 (OK)
select @x.exist('/data/li[not(edges)]') -- returns 1, there is an li without edges (OK)

Но если значение edges заранее неизвестно, как указать его с помощью переменной SQL? Я хотел бы закодировать что-то вроде следующего:

declare @ChildElement varchar(100) = 'edges';
select @x.exist('/data/li[not(sql:variable("@ChildElement"))]') -- not allowed

но sql:variable недействительна в этом контексте в SQL Server. Я также пробовал комбинации с использованием local-name():

select @x.exist('/data/li[not(local-name()=sql:variable("ChildElement"))]') -- always returns 1

но он всегда возвращает 1. Я думаю, потому что всегда есть li с дочерним элементом, который не является F1, и всегда есть li с дочерним элементом, который не является ребрами. Как я могу идентифицировать только li без подэлемента edges, если edges находится в переменной SQL? Кстати, я использую SQL Server 2008, хотя я думаю, что это относится ко всем версиям SQL Server с 2005 года.


person Ubercoder    schedule 15.06.2014    source источник


Ответы (1)


Я предполагаю, что ваш первый подход не работает, так как переменная "заменяется" строкой (я недостаточно хорошо знаю возможности SQL Server XQuery, чтобы подробно объяснить, что происходит).

Второй подход — запрашивать все элементы li, где local-name() элемента li (текущий контекст!) равно переменной SQL — возможно, это не то, что вы хотите сделать. Вместо этого попробуйте

select @x.exist('/data/li[not(./*[local-name()=sql:variable("@ChildElement")])]')

который вместо этого проверяет всех детей.

person Jens Erat    schedule 15.06.2014
comment
Это ./ нужно? Я знаю, что у XPath в SQL Server есть некоторые особенности, но в стандартном XPath этот ./ не нужен. - person JLRishe; 15.06.2014
comment
@JLRishe нет, ./ не требуется в SQL Server. * достаточно. +1 - person Mikael Eriksson; 15.06.2014
comment
Некоторые пользователи XPath используют ./ как способ обратить внимание читателя на тот факт, что путь является относительным, а не абсолютным. Я не знаю, было ли это намерением Йенса Эрата, но именно поэтому я часто использую ./ для начала относительных путей. - person C. M. Sperberg-McQueen; 15.06.2014
comment
@JLRishe: то, что говорит К. М. Сперберг-МакКуин, - это именно то, что я делаю. Если использование текущего контекста вызывает какие-либо сомнения, я обычно явно обозначаю его. В основном в начале запроса, чтобы намекнуть, что я не забыл корень /. - person Jens Erat; 16.06.2014
comment
Jens / Mikael — я только что попробовал (с ./*), и все сработало отлично. Я предполагаю, что * означает, что он должен проверить все подэлементы в li, чтобы убедиться, что они не совпадают, а это именно то, что мне нужно. Спасибо за вашу помощь - я отмечу это как принятый ответ. - person Ubercoder; 16.06.2014
comment
Это выражение означает более или менее все li, у которых нет дочерних элементов, чье имя равно переменной SQL, так что да, именно так, как вы описали это в своем комментарии. - person Jens Erat; 16.06.2014