Ошибка XQuery при чтении атрибута из родительского узла с помощью *typed* XML (для значения () требуется синглтон)

#sql #sql-server #xml #xquery-sql

#sql #sql-сервер #xml #xquery-sql

Вопрос:

У меня простой XML:

 DECLARE @x1 xml = '<r>
    <o a="1">
        <o a="2">
        </o>
    </o>
</r>';
  

И выберите следующее:

 SELECT r.o.value('(../@a)[1]','varchar(20)') FROM @x1.nodes('/r//o') r(o);
  

Все хорошо:

 NULL
1
  

Когда я использую типизированный XML:

 CREATE XML SCHEMA COLLECTION [dbo].test AS '
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <xsd:element name="r">
        <xsd:complexType>
            <xsd:complexContent>
                <xsd:restriction base="xsd:anyType">
                    <xsd:sequence>
                        <xsd:element name="o" type="o" minOccurs="0" maxOccurs="unbounded" />
                    </xsd:sequence>
                </xsd:restriction>
            </xsd:complexContent>
        </xsd:complexType>
    </xsd:element>
    <xsd:complexType name="o">
        <xsd:complexContent>
            <xsd:restriction base="xsd:anyType">
                <xsd:sequence>
                    <xsd:element name="o" type="o" minOccurs="0" maxOccurs="unbounded" />
                </xsd:sequence>
                <xsd:attribute name="a" type="xsd:string" />
            </xsd:restriction>
        </xsd:complexContent>
    </xsd:complexType>
</xsd:schema>'
GO

DECLARE @x2 xml(dbo.test) = '<r>
    <o a="1">
        <o a="2">
        </o>
    </o>
</r>';

SELECT r.o.value('(../@a)[1]','varchar(20)') FROM @x2.nodes('/r//o') r(o);
  

Я получил ошибку:

 XQuery [value()]: 'value()' requires a singleton (or empty sequence), found operand of type 'xdt:untypedAtomic *'
  

Почему нетипизированный XML работает, а типизированный XML — нет?

Комментарии:

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

Ответ №1:

Похоже, вы хорошо понимаете, что это не обычная проблема, связанная с необходимостью убедить анализатор в том, что передается только одна вещь value() . Как вы заметили, эта проблема специфична для typed XML.

Используя Google, я нашел этот древний пост в блоге с многообещающим текстом

rt является частью модели данных XQuery 1.0 / XPath 2.0.

Большинство людей справляются с этим. Настоящее веселье начинается, когда вы выполняете примеры с использованием нетипизированных выражений XML и XPath с помощью теста узла text(). text () отлично работает при использовании нетипизированного XML, но не работает с типизированным XML с простым содержимым

В нем говорится об онлайн-книгах по SQL и документе с рекомендациями по SQL Server 2005 XML, который, думаю, является этой книгой. Я не искал более актуальную ссылку. Но, используя то, что я там нашел, я смог решить вашу проблему: простая замена

 SELECT r.o.value('(../@a)[1]','varchar(20)') FROM @x2.nodes('/r//o') r(o);
  

с

 SELECT r.o.value('fn:string(../@a)[1]','varchar(20)') FROM @x2.nodes('/r//o') r(o);
  

В принципе, value() не нравится, когда ему присваиваются значения типа typed-xml, но ему нужны строки. Итак, мы присваиваем ему строку.