Помещение разных элементов в один столбец из XML в SQL Server - PullRequest
2 голосов
/ 14 октября 2019

У меня есть следующая часть документа XML:

    <?xml version="1.0" encoding="ISO-8859-1" ?>
    <IPCScheme xmlns="http://depatisnet.dpma.de/ipc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://depatisnet.dpma.de/ipc ipc_scheme_3-1_dpma.xsd" edition="20190101" lang="EN">
       <ipcEntry  symbol="A01D0000000000" kind="u" entryType="K">
           <textBody><title>
               <titlePart>
                   <text> HARVESTING </text>
               </titlePart>
               <titlePart>
                   <text> MOWING</text>
               </titlePart>
           </title></textBody>
       </ipcEntry>
       <ipcEntry  symbol="A01F0000000000" kind="u" entryType="K">
           <textBody><title>
               <titlePart>
                   <text> THRESHING</text>
                   <entryReference> combines 
                       <sref ref="A01D0041000000"></sref>
                   </entryReference>
               </titlePart>
               <titlePart>
                   <text> BALING OF STRAW, HAY OR THE LIKE</text>
               </titlePart>
               <titlePart>
                   <text> STATIONARY APPARATUS OR HAND TOOLS FOR FORMING OR BINDING STRAW, HAY OR THE LIKE INTO BUNDLES</text>
               </titlePart>
               <titlePart>
                   <text> CUTTING OF STRAW, HAY OR THE LIKE</text>
               </titlePart>
               <titlePart>
                   <text> STORING AGRICULTURAL OR HORTICULTURAL PRODUCE</text>
                   <entryReference> arrangements for making or setting stacks in connection with harvesting 
                       <sref ref="A01D0085000000"></sref>
                   </entryReference>
               </titlePart>
           </title></textBody>
       </ipcEntry>
       <ipcEntry symbol="A01B0000000000" kind="u" entryType="K">
           <textBody><title>
               <titlePart>
                    <text> SOIL WORKING IN AGRICULTURE OR FORESTRY</text>
               </titlePart>
               <titlePart>
                   <text> PARTS, DETAILS, OR ACCESSORIES OF AGRICULTURAL MACHINES OR IMPLEMENTS, IN GENERAL</text>
                   <entryReference> making or covering furrows or holes for sowing, planting or manuring 
                       <sref ref="A01C0005000000"></sref>
                   </entryReference>
                   <entryReference> machines for harvesting root crops 
                       <sref ref="A01D0000000000"></sref>
                   </entryReference>
                   <entryReference> mowers convertible to soil working apparatus or capable of soil working
                       <sref ref="A01D0042040000"></sref>
                   </entryReference>
                   <entryReference> mowers combined with soil working implements 
                       <sref ref="A01D0043120000"></sref>
                   </entryReference>
                   <entryReference>soil working for engineering purposes 
                       <sref ref="E01"></sref> , 
                       <sref ref="E02"></sref> , 
                       <sref ref="E21"></sref>
                   </entryReference>
               </titlePart>
           </title></textBody>
       </ipcEntry>
 </IPCScheme>

Цель состоит в том, чтобы иметь столбец с символом и столбец с объединенным текстом, включающим информацию для каждой записи ipc.

Symbol          String
A01B0000000000  SOIL WORKING IN AGRICULTURE OR FORESTRY; PARTS, DETAILS, OR ACCESSORIES OF AGRICULTURAL MACHINES OR IMPLEMENTS, IN GENERAL (making or covering furrows or holes for sowing, planting or manuring A01C 5/00; machines for harvesting root crops A01D; mowers convertible to soil working apparatus or capable of soil working A01D 42/04; mowers combined with soil working implements A01D 43/12; soil working for engineering purposes E01, E02, E21)
A01D0000000000  HARVESTING; MOWING
A01F0000000000  THRESHING (combines A01D 41/00); BALING OF STRAW, HAY OR THE LIKE; STATIONARY APPARATUS OR HAND TOOLS FOR FORMING OR BINDING STRAW, HAY OR THE LIKE INTO BUNDLES; CUTTING OF STRAW, HAY OR THE LIKE; STORING AGRICULTURAL OR HORTICULTURAL PRODUCE (arrangements for making or setting stacks in connection with harvesting A01D 85/00)

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

    DECLARE @xml  XML
    Select @xml =P
    FROM OPENROWSET (BULK 'C:\Users\File.xml', SINGLE_BLOB) AS Products(P)
    ;WITH XMLNAMESPACES(DEFAULT 'http://depatisnet.dpma.de/ipc',
                            'http://www.w3.org/2001/XMLSchema-instance' as xsi)
    SELECT
        MY_XML.IPC.value('@symbol', 'NVARCHAR(max)') as Symbol,
        concat(left(MY_XML.IPC.query('for $i in textBody/title/titlePart/text return concat($i, ";")').value('.','NVARCHAR(max)'), len(MY_XML.IPC.query('for $i in textBody/title/titlePart/text return concat($i, ";")').value('.','NVARCHAR(max)'))-1),' (',My_XML.IPC.query('for $i in textBody/title/titlePart/entryReference return concat($i, ($i/sref/@ref)[1] , ";")').value('.','NVARCHAR(max)'),')') as String
    FROM @xml.nodes('IPCScheme/ipcEntry/ipcEntry/ipcEntry') MY_XML(IPC)

Это был результат:

    Symbol         String
    A01B0000000000 SOIL WORKING IN AGRICULTURE OR FORESTRY; PARTS, DETAILS, OR ACCESSORIES OF AGRICULTURAL MACHINES OR IMPLEMENTS, IN GENERAL (making or covering furrows or holes for sowing, planting or manuring A01C0005000000; ; machines for harvesting root crops A01D0000000000; mowers convertible to soil working apparatus or capable of soil working A01D0042040000; mowers combined with soil working implements A01D0043120000; soil working for engineering purposes  ,  , E01;)
    A01D0000000000 HARVESTING; MOWING ()
    A01F0000000000 THRESHING;  BALING OF STRAW, HAY OR THE LIKE; STATIONARY APPARATUS OR HAND TOOLS FOR FORMING OR BINDING STRAW, HAY OR THE LIKE INTO BUNDLES; CUTTING OF STRAW, HAY OR THE LIKE; STORING AGRICULTURAL OR HORTICULTURAL PRODUCE (combines A01D0041000000; arrangements for making or setting stacks in connection with harvesting A01D0085000000;)

Таким образом, все entryReferences по конструкции добавляются в конец текста, что правильно, когда entryReferences добавляются в последний titlePart, но не вВообще и дополнительно я могу включить только один sref / @ ref. Мне нужен какой-то запрос, который запрашивает для каждой части заголовка различные элементы и добавляет его к полной строке так, как я хочу.

отредактировано: В общем, я хочу для каждого ipcEntry символ атрибутаи для каждого ipcEntry один объединенный текст, который имеет для каждого titlePart текст элемента, за которым следует "(" и запись-ссылка и атрибут @ref из sref, за которым следует ");"и следующий заголовок части. Все элементы могут иметь более одного текста, entryReference или sref, что усложняет задачу. Первая таблица после фрагмента XML - это то, что я хочу. Вторая таблица после sql-кода - вот что я получаю.

Надеюсь, вы можете высказать некоторые соображения по этому поводу. Заранее спасибо

Решение от @Shnugo слегка отредактировано

 ;WITH XMLNAMESPACES(DEFAULT 'http://depatisnet.dpma.de/ipc')
 SELECT entr.value('@symbol','nvarchar(100)') AS Symbol
       ,STUFF(
        (
        SELECT CONCAT('; ',B.tp.value('(text/text())[1]','nvarchar(100)'))
              ,CASE WHEN B.tp.exist('entryReference')=1 THEN
               CONCAT(' ('
                      ,STUFF(
                       (
                        SELECT CONCAT('; ',C.er.value('text()[1]','nvarchar(100)'))
                            ,CASE WHEN C.er.exist('sref')=1 THEN
                            STUFF(
                                (
                                 Select Concat(', ', D.ad.value('@ref[1]','nvarchar(100)'))
                                 From C.er.nodes('sref') D(ad)
                                 For XML PATH('')
                                 ),1,2,'') End
                        FROM B.tp.nodes('entryReference') C(er)
                        FOR XML PATH('')
                       ),1,2,'')
                      ,')') END
        FROM A.entr.nodes('textBody/title/titlePart') B(tp)
        FOR XML PATH('')
        ),1,1,'') AS ConcatenatedTextNodes 
 FROM @xml.nodes('/IPCScheme/ipcEntry') A(entr);

1 Ответ

3 голосов
/ 15 октября 2019

Существует множество подходов для решения этой проблемы:

Предполагается, что ваш XML читается в типизированную переменную xml (Подсказка: избавьтесь от объявления <??>):

DECLARE @xml XML=
N'<IPCScheme xmlns="http://depatisnet.dpma.de/ipc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://depatisnet.dpma.de/ipc ipc_scheme_3-1_dpma.xsd" edition="20190101" lang="EN">
       <ipcEntry  symbol="A01D0000000000" kind="u" entryType="K">
           <textBody><title>
               <titlePart>
                   <text> HARVESTING </text>
               </titlePart>
               <titlePart>
                   <text> MOWING</text>
               </titlePart>
           </title></textBody>
       </ipcEntry>
       <ipcEntry  symbol="A01F0000000000" kind="u" entryType="K">
           <textBody><title>
               <titlePart>
                   <text> THRESHING</text>
                   <entryReference> combines 
                       <sref ref="A01D0041000000"></sref>
                   </entryReference>
               </titlePart>
               <titlePart>
                   <text> BALING OF STRAW, HAY OR THE LIKE</text>
               </titlePart>
               <titlePart>
                   <text> STATIONARY APPARATUS OR HAND TOOLS FOR FORMING OR BINDING STRAW, HAY OR THE LIKE INTO BUNDLES</text>
               </titlePart>
               <titlePart>
                   <text> CUTTING OF STRAW, HAY OR THE LIKE</text>
               </titlePart>
               <titlePart>
                   <text> STORING AGRICULTURAL OR HORTICULTURAL PRODUCE</text>
                   <entryReference> arrangements for making or setting stacks in connection with harvesting 
                       <sref ref="A01D0085000000"></sref>
                   </entryReference>
               </titlePart>
           </title></textBody>
       </ipcEntry>
       <ipcEntry symbol="A01B0000000000" kind="u" entryType="K">
           <textBody><title>
               <titlePart>
                    <text> SOIL WORKING IN AGRICULTURE OR FORESTRY</text>
               </titlePart>
               <titlePart>
                   <text> PARTS, DETAILS, OR ACCESSORIES OF AGRICULTURAL MACHINES OR IMPLEMENTS, IN GENERAL</text>
                   <entryReference> making or covering furrows or holes for sowing, planting or manuring 
                       <sref ref="A01C0005000000"></sref>
                   </entryReference>
                   <entryReference> machines for harvesting root crops 
                       <sref ref="A01D0000000000"></sref>
                   </entryReference>
                   <entryReference> mowers convertible to soil working apparatus or capable of soil working
                       <sref ref="A01D0042040000"></sref>
                   </entryReference>
                   <entryReference> mowers combined with soil working implements 
                       <sref ref="A01D0043120000"></sref>
                   </entryReference>
                   <entryReference>soil working for engineering purposes 
                       <sref ref="E01"></sref> , 
                       <sref ref="E02"></sref> , 
                       <sref ref="E21"></sref>
                   </entryReference>
               </titlePart>
           </title></textBody>
       </ipcEntry>
 </IPCScheme>';

-- Самый простой подход - это XQuery data(). Но это будет использовать пробел в качестве разделителя в любом случае. Жаль, что мы не можем установить разделитель в качестве параметра.

 WITH XMLNAMESPACES(DEFAULT 'http://depatisnet.dpma.de/ipc')
 SELECT entr.value('@symbol','nvarchar(100)') AS Symbol
       ,entr.query('data(.//text)').value('.','nvarchar(max)') AS ConcatenatedTextNodes
 FROM @xml.nodes('/IPCScheme/ipcEntry') A(entr);

- при этом будет использоваться вложенный FOR XML PATH('') для имитации групповой конкатенации и STUFF() для отсечения ведущего ;
- Преимущество: Ее я могу легко использовать TRIM() (LTRIM() и RTRIM() в старых версиях).

 WITH XMLNAMESPACES(DEFAULT 'http://depatisnet.dpma.de/ipc')
 SELECT entr.value('@symbol','nvarchar(100)') AS Symbol
       ,STUFF(
        (
        SELECT CONCAT('; ',TRIM(t.value('text()[1]','nvarchar(100)')))
        FROM entr.nodes('./textBody/title/titlePart/text') B(t)
        FOR XML PATH('')
        ),1,1,'') AS ConcatenatedTextNodes 
 FROM @xml.nodes('/IPCScheme/ipcEntry') A(entr);

- Это примерно то же самое, что и выше, но использует FLWOR XQueryподход:

 WITH XMLNAMESPACES(DEFAULT 'http://depatisnet.dpma.de/ipc')
 SELECT entr.value('@symbol','nvarchar(100)') AS Symbol
       ,STUFF(
        entr.query('for $t in ./textBody/title/titlePart/text/text()
                    return <t>{concat(";",$t)}</t>')
            .value('.','nvarchar(max)'),1,1,'') AS ConcatenatedTextNodes
 FROM @xml.nodes('/IPCScheme/ipcEntry') A(entr);

- И, наконец, если вам уже повезло с использованием v2017, вы можете использовать STRING_AGG()

 WITH XMLNAMESPACES(DEFAULT 'http://depatisnet.dpma.de/ipc')
 ,cte AS
 (
     SELECT entr.value('@symbol','nvarchar(100)') AS Symbol
           ,t.value('text()[1]','nvarchar(max)') AS TextNode
     FROM @xml.nodes('/IPCScheme/ipcEntry') A(entr)
     CROSS APPLY A.entr.nodes('./textBody/title/titlePart/text') B(t)
 )
 SELECT Symbol
       ,STRING_AGG(TextNode,';')
 FROM cte
 GROUP BY Symbol; 

ОБНОВЛЕНИЕ Извините, я должен был прочитать вашу собственную попыткуболее осторожно ...

Вы можете попробовать что-то из этого, чтобы получить объединенные значения:

WITH XMLNAMESPACES(DEFAULT 'http://depatisnet.dpma.de/ipc')
 SELECT entr.value('@symbol','nvarchar(100)') AS Symbol
       ,STUFF(
        (
        SELECT CONCAT('; ',TRIM(B.tp.value('(text/text())[1]','nvarchar(100)')))
              ,CASE WHEN B.tp.exist('entryReference')=1 THEN
               CONCAT(' ('
                      ,STUFF(
                       (
                        SELECT CONCAT('; ',TRIM(C.er.value('text()[1]','nvarchar(100)')))
                        FROM B.tp.nodes('entryReference') C(er)
                        FOR XML PATH('')
                       ),1,2,'')
                      ,')') END
        FROM A.entr.nodes('textBody/title/titlePart') B(tp)
        FOR XML PATH('')
        ),1,1,'') AS ConcatenatedTextNodes 
 FROM @xml.nodes('/IPCScheme/ipcEntry') A(entr);

К сожалению, в XQuery FLWOR у нас нет normalize-space() или trim(), поэтомувстроенные пробелы могут взорвать это.

Приведенное выше решение использует CASE WHEN для добавления () только к titleParts, где есть хотя бы один <entryReference>.

...