У меня есть TableC и TableA. Мне нужны все записи из TableC, тогда как только совпадающие записи из TableA, поэтому я использую «левое соединение». Проблема в том, что в TableA есть столбец XML. XML в этом столбце имеет следующую структуру

<x:main xmlns:x="x-elements">
  <x:rules>
    <x:obj>
        <ruleName>name1</ruleName>
        <createdBy>userA</createdBy>
        <type>bbb</type>
    </x:obj>
    <x:obj>
        <ruleName>name2</ruleName>
        <createdBy>userA</createdBy>
        <type>ccc</type>
    </x:obj>
   </x:rules>
   <x:info>
    <x:obj>
        <target>ftp:1</target>
        <user>userB</user>
    </x:obj>
    <x:obj>
        <target>ftp:3</target>
        <user>userA</user>
    </x:obj>
  </x:info>
</x:main>

Я хочу получить createdBy из столбца XML для каждой строки, где эквивалентным type является 'ccc'.

Ниже мои усилия

with xmlnamespaces ('x-elements' as x),
res1 as (select x.xmlCol.value('(createdBy)[1]', 'varchar(500)') prop1
from TableC c 
left join TableA a 
cross apply a.xCol.nodes('x:main/x:rules/x:obj') x(xmlCol)
on c.Id = a.Id 
where x.xmlCol.value('(type)[1]', 'varchar(500)') = 'ccc')
select
c.Name,
(select prop1 from res1) prop1
from TableC c 
left join TableA a 
on c.Id = a.Id 

Однако я получаю сообщение об ошибке

Подзапрос вернул более одного значения. Это не разрешено, если подзапрос следует за =,! =, <, <=,>,> = Или когда подзапрос используется как выражение.

Может ли кто-нибудь рассказать, как достичь того, что я пытаюсь сделать здесь?

P.S Позже я также хотел бы получить «цель» из столбца XML для каждой строки, где эквивалент user - «userA».

0
fredzyadi 26 Апр 2016 в 18:21

2 ответа

Лучший ответ
(select prop1 from res1) prop1

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

select
c.Name,
(select prop1 from res1) prop1
from TableC c 
left join TableA a 
on c.Id = a.Id

Я ничего не знаю о запросах XML, но для того, чтобы этот запрос работал, вам нужно будет добавить идентификатор в CTE res1.

res1 as (select x.xmlCol.value('(prop1)[1]', 'varchar(500)') prop1
,c.Id
from TableC c 
left join TableA a 
cross apply a.xCol.nodes('x:main/x:sub/x:obj') x(xmlCol)
on c.Id = a.Id 
where x.xmlCol.value('(prop2)[1]', 'varchar(500)') = 'ccc')

А затем измените свой подзапрос на:

(select prop1 from res1 where res1.Id = c.Id) prop1

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

2
Arthur D 26 Апр 2016 в 15:37

Если я понял это правильно, вы создаете CTE, думая, что вам это нужно, чтобы получить свой prop1. А затем вы делаете те же самые объединения и снова фильтруете ...

Разве не было бы достаточно сократить это до:

with xmlnamespaces ('x-elements' as x)
select x.xmlCol.value('(prop1)[1]', 'varchar(500)') prop1
from TableC c 
left join TableA a 
cross apply a.xCol.nodes('x:main/x:sub/x:obj') x(xmlCol)
on c.Id = a.Id 
where x.xmlCol.value('(prop2)[1]', 'varchar(500)') = 'ccc'

Как указал Артур Дэниэлс, проблема заключается в том, что (select prop1 from res1) prop1 будет возвращать более одного элемента и, следовательно, не может быть вызван как столбец в подвыборке ...

РЕДАКТИРОВАТЬ: как измельчить ваш XML

Удалено ....

РЕДАКТИРОВАТЬ 2: Я должен признать, что вам действительно стоит потренироваться как мне объяснить, что мне нужно ...

Возможно, вы ищете это:

Это объединит TableS и TableS, как вы это сделали сами, а затем выберите значение «created By», где «type» = «ccc».

Следующий XQuery сначала выбирает имя пользователя, которое мы нашли в первом шаге в «ccc», и находит подходящую «цель».

WITH XMLNAMESPACES('x-elements' AS x)
SELECT c.*
      ,a.*
      ,a.xCol.value('(//x:rules/x:obj[type="ccc"]/createdBy)[1]','varchar(500)') AS CreatedBy
      ,a.xCol.value('let $user:=(//x:rules/x:obj[type="ccc"]/createdBy)[1] return (//x:info/x:obj[user=$user]/target)[1]','varchar(500)') AS Target
FROM TableC AS c 
LEFT JOIN TableA AS a on c.Id = a.Id 
2
Community 20 Июн 2020 в 09:12