2015-10-09 1 views
1

Случай. В таблице есть поле с некоторым кодом XML.T-SQL - XML-SQL Server Таблица

-- Some XML 
'<DTS:ConnectionManager DTS:refId="Package.ConnectionManagers[MTS]" DTS:CreationName="FLATFILE" DTS:DTSID="{296732CC-7D91-4E49-ACD4-384E03BC032E}" DTS:ObjectName="MTS"> 
    <DTS:PropertyExpression DTS:Name="ConnectionString">@Something</DTS:PropertyExpression> 
    <DTS:ObjectData> 
     <DTS:ConnectionManager DTS:Format="Delimited" DTS:LocaleID="1033" DTS:HeaderRowDelimiter="_x000D__x000A_" DTS:ColumnNamesInFirstDataRow="True" DTS:RowDelimiter="" DTS:TextQualifier="_x0022_" DTS:CodePage="1252" DTS:ConnectionString="C:\Folder\\File.csv"> 
      <DTS:FlatFileColumns> 
       <DTS:FlatFileColumn DTS:ColumnType="Delimited" DTS:ColumnDelimiter="_x002C_" DTS:MaximumWidth="50" DTS:DataType="129" DTS:TextQualified="True" DTS:ObjectName="MC" DTS:DTSID="{E87E7707-B7F7-4EC6-A2CB-98AD637A3985}" DTS:CreationName="" /> 
       <DTS:FlatFileColumn DTS:ColumnType="Delimited" DTS:ColumnDelimiter="_x002C_" DTS:DataType="6" DTS:TextQualified="True" DTS:ObjectName="PP" DTS:DTSID="{C7B97962-3B43-40C5-82B1-F6136906CD84}" DTS:CreationName="" /> 
      </DTS:FlatFileColumns> 
     </DTS:ConnectionManager> 
    </DTS:ObjectData> 
</DTS:ConnectionManager>' 
-- Some more XML 

Хотелось бы вытащить некоторую информацию и сохранить ее в виде табличного формата.

Желаемой выход

CreationName ObjectName ConnectionString  MaximumWidth DataType FieldName 
FLATFILE  MTS   C:\Folder\\File.csv  50    129   MC 
FLATFILE  MTS   C:\Folder\\File.csv  NULL   6   PP 

Объяснение подключения входа с выходом

CreationName - DTS:CreationName from DTS:ConnectionManager. i.e. FLATFILE 
ObjectName - DTS:ObjectName from DTS:ConnectionManager. i.e. MTS 
ConnectionString - DTS:ConnectionString from DTS:ObjectData\DTS:ConnectionManager. i.e. "C:\Folder\\File.csv" 
MaximumWidth - DTS:MaximumWidth from DTS:FlatFileColumns i.e. 50 -- NOTE: MaximumWidth might not always exist 
DataType - DTS:DataType from DTS:FlatFileColumns i.e. 129 
FieldName - DTS:ObjectName from DTS:FlatFileColumns i.e. MC 

действительно не имеет большой опыт работы с XML в SQL Server. (Я буду делать некоторые из моих собственных игры вокруг и разместить его здесь, если я где-то смысл. :))

ОБНОВЛЕНО Пример XML

<DTS:Executable xmlns:DTS="www.microsoft.com/SqlServer/Dts" DTS:refId="P" DTS:CreationDate="10/01/2015 12:00:00"> 
    <DTS:ConnectionManagers> 
    <DTS:ConnectionManager DTS:refId="Package.ConnectionManagers[FF]" DTS:CreationName="FLATFILE" DTS:DTSID="{123}" DTS:ObjectName="FF"> 
     <DTS:ObjectData> 
     <DTS:ConnectionManager DTS:Format="Delimited" DTS:LocaleID="1033" DTS:HeaderRowDelimiter="_x000D__x000A_" DTS:ColumnNamesInFirstDataRow="True" DTS:RowDelimiter="" DTS:TextQualifier="_x0022_" DTS:CodePage="1252" DTS:ConnectionString="Test.csv"> 
      <DTS:FlatFileColumns> 
      <DTS:FlatFileColumn DTS:ColumnType="Delimited" DTS:ColumnDelimiter="_x002C_" DTS:DataType="11" DTS:TextQualified="True" DTS:ObjectName="TestCN" DTS:DTSID="{012}" DTS:CreationName="" /> 
      </DTS:FlatFileColumns> 
     </DTS:ConnectionManager> 
     </DTS:ObjectData> 
    </DTS:ConnectionManager> 
    <DTS:ConnectionManager DTS:refId="Package.ConnectionManagers[FF2]" DTS:CreationName="FLATFILE" DTS:DTSID="{123}" DTS:ObjectName="FF2"> 
     <DTS:ObjectData> 
     <DTS:ConnectionManager DTS:Format="Delimited" DTS:LocaleID="1033" DTS:HeaderRowDelimiter="_x000D__x000A_" DTS:ColumnNamesInFirstDataRow="True" DTS:RowDelimiter="" DTS:TextQualifier="_x0022_" DTS:CodePage="1252" DTS:ConnectionString="Test2.csv"> 
      <DTS:FlatFileColumns> 
      <DTS:FlatFileColumn DTS:ColumnType="Delimited" DTS:ColumnDelimiter="_x002C_" DTS:DataType="11" DTS:TextQualified="True" DTS:ObjectName="TestCN2" DTS:DTSID="{012}" DTS:CreationName="" /> 
      </DTS:FlatFileColumns> 
     </DTS:ConnectionManager> 
     </DTS:ObjectData> 
    </DTS:ConnectionManager> 
    </DTS:ConnectionManagers> 
</DTS:Executable> 

ответ

2

Вы не объявляя пространства имен в корневом элементе так Я подменил это. Это должно быть само экстракцию и бежать в чем я угадывание 2008 и выше, хотя я написал ее в 2014 г. Просто вставьте его в SQL Server Management Studio:

ОБНОВЛЕНО 1:45 PM PST:

Благодаря Shnugo для упрощения «с пространством имен XMLNames».

DECLARE @XML XML = ' 
<DTS:Executable xmlns:DTS="www.microsoft.com/SqlServer/Dts" DTS:refId="P" DTS:CreationDate="10/01/2015 12:00:00"> 
    <DTS:ConnectionManagers> 
    <DTS:ConnectionManager DTS:refId="Package.ConnectionManagers[FF]" DTS:CreationName="FLATFILE" DTS:DTSID="{123}" DTS:ObjectName="FF"> 
     <DTS:ObjectData> 
     <DTS:ConnectionManager DTS:Format="Delimited" DTS:LocaleID="1033" DTS:HeaderRowDelimiter="_x000D__x000A_" DTS:ColumnNamesInFirstDataRow="True" DTS:RowDelimiter="" DTS:TextQualifier="_x0022_" DTS:CodePage="1252" DTS:ConnectionString="Test.csv"> 
      <DTS:FlatFileColumns> 
      <DTS:FlatFileColumn DTS:ColumnType="Delimited" DTS:ColumnDelimiter="_x002C_" DTS:DataType="11" DTS:TextQualified="True" DTS:ObjectName="TestCN" DTS:DTSID="{012}" DTS:CreationName="" /> 
      </DTS:FlatFileColumns> 
     </DTS:ConnectionManager> 
     </DTS:ObjectData> 
    </DTS:ConnectionManager> 
    </DTS:ConnectionManagers> 
</DTS:Executable>' 
; 

WITH XMLNAMESPACES (N'www.microsoft.com/SqlServer/Dts' as DTS) 
SELECT 
    y.vals.query('.') AS NodesAsExtracted 
, x.vals.value('@DTS:CreationName', 'Varchar(255)') AS CreationName 
, x.vals.value('@DTS:ObjectName', 'Varchar(255)') AS ObjectName 
, y.vals.value('@DTS:ConnectionString', 'Varchar(255)') AS ConnectionString 
, x.vals.value('@DTS:ColumnType', 'Varchar(255)') AS ColumnType 
, x.vals.value('@DTS:MaximumWidth', 'Varchar(255)') AS MaximumWidth 
FROM @XML.nodes('/DTS:Executable/DTS:ConnectionManagers/DTS:ConnectionManager/DTS:ObjectData/DTS:ConnectionManager') AS y(vals) 
    CROSS APPLY @XML.nodes('/DTS:Executable/DTS:ConnectionManagers/DTS:ConnectionManager/DTS:ObjectData/DTS:ConnectionManager/DTS:FlatFileColumns/DTS:FlatFileColumn') AS x(vals) 


/* 
The key piece is you are extracting data with a namespace, which makes things harder when querying. 
You need to repeat certain 'nodes' so there is a syntax for that called originally enough 'nodes' that breaks up a 3d object like xml into multiple bits 
I do one for the high level and one for the lower and then cross apply them which really is a whole world into itself I won't mention here 
It should be represented as a parent 'x' and the values found 'vals' 
I showed an example as is first when I query '('.')' which is everything in essence. 
My namespace declaration must match on the xml that exists and the declaration. 

more on nodes https://msdn.microsoft.com/en-us/library/ms188282.aspx 
more on query https://msdn.microsoft.com/en-us/library/ms191474.aspx 
more on value https://msdn.microsoft.com/en-us/library/ms178030.aspx 
*/ 
+0

спасибо приятелю. Этот sln работает для строки в исходном сообщении. Но когда я добавляю еще один «узел» (узел корневого уровня, я думаю, является правильным термином ?!), он не дает мне выхода. 007

+0

Попробуйте установить имя корневого элемента в? последняя строка «CROSS APPLY ...» после точки с запятой. Узлы считываются по пути (называемому «XPATH»), и этот путь начинается с вашего корня ... – Shnugo

+0

Если я изменю последнюю строку на «CROSS APPLY @XML .nodes ('declare namespace DTS = "http: // DTS";/DTS: Исполняемый/DTS: ConnectionManager/DTS: ObjectData/DTS: ConnectionManager') AS y (vals) ", он все равно не дает никакого вывода (Я также добавил его в строку prev, а также нет вывода) :( – 007

2

Это является дополнением к ответу djangojazz. Не принимайте это, это просто копия (но вы можете проголосовать вверх, если вам это нравится ;-) ...

С помощью С XMLNAMESPACES вы можете избежать многократного объявление пространства имен:

WITH XMLNAMESPACES (N'http://DTS' as DTS) 
SELECT 
    x.vals.query('.') AS NodesAsExtracted 
, x.vals.value('@DTS:CreationName', 'Varchar(255)') AS CreationName 
, x.vals.value('@DTS:ObjectName', 'Varchar(255)') AS ObjectName 
, y.vals.value('@DTS:ConnectionString', 'Varchar(255)') AS ConnectionString 
, x.vals.value('@DTS:ColumnType', 'Varchar(255)') AS ColumnType 
, x.vals.value('@DTS:MaximumWidth', 'Varchar(255)') AS MaximumWidth 
from @XML.nodes('/DTS:ConnectionManager/DTS:ObjectData/DTS:ConnectionManager/DTS:FlatFileColumns/DTS:FlatFileColumn') AS x(vals) 
    CROSS APPLY @XML.nodes('/DTS:ConnectionManager/DTS:ObjectData/DTS:ConnectionManager') AS y(vals) 
+0

Спасибо за это, я не знал, что вы можете смешать их. Вы узнаете что-то каждый день. – djangojazz

+0

Спасибо за улучшение. Я попытался изменить последнюю строку на CROSS APPLY @ XML.nodes ('/ DTS: Executable/DTS: ConnectionManager/DTS: ObjectData/DTS: ConnectionManager') AS y (vals), но все равно не дает никакого вывода. :( – 007

+0

Просьба опубликовать новый XML – Shnugo

0

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

Отлично, так как SQL - это язык специального назначения, а не как жидкий или динамический, как Java, C#, Python, PHP, Perl, VB и другие, которые также несут библиотеки для запуска XPath, XSLT и других Задачи, специфичные для XML. Кроме того, эти языки могут подключаться к любой базе данных для извлечения данных BLOB.

Для будущих читателей ниже приведены примеры с открытым исходным кодом с использованием потребностей данных OP. Вы заметите, использование позиционных скобок с [] в XPaths, что позволяет более DTS:ConnectionManager элементы:

Python (с использованием библиотеки LXML)

import os 
import lxml.etree as ET 

cd = os.path.dirname(os.path.abspath(__file__)) 

xmlfile = 'DTSfile.xml' 
dom = ET.parse(os.path.join(cd, xmlfile)) 
root = dom.getroot() 

nodexpath = dom.xpath("//DTS:ConnectionManager", namespaces=root.nsmap) 
dataline = [] 

def checkPath(xpathstr):  
    if dom.xpath(xpathstr, namespaces=root.nsmap) == []: 
     return '' 
    else: 
     return dom.xpath(xpathstr, namespaces=root.nsmap)[0] 

for i in range(1,len(nodexpath)+1): 
    if i % 2 == 0: continue 

    dataline = []  
    dataline.append(checkPath('//DTS:ConnectionManager[{0}]/@DTS:CreationName'.format(i))) 
    dataline.append(checkPath('//DTS:ConnectionManager[{0}]/@DTS:ObjectName'.format(i))) 
    dataline.append(checkPath('//DTS:ConnectionManager[{0}]/DTS:ObjectData/DTS:ConnectionManager/@DTS:ConnectionString'.format(i)))  
    dataline.append(checkPath('//DTS:ConnectionManager[{0}]/DTS:ObjectData/DTS:ConnectionManager/DTS:FlatFileColumns/DTS:FlatFileColumn/@DTS:MaximumWidth'.format(i))) 
    dataline.append(checkPath('//DTS:ConnectionManager[{0}]/DTS:ObjectData/DTS:ConnectionManager/DTS:FlatFileColumns/DTS:FlatFileColumn/@DTS:DataType'.format(i))) 
    dataline.append(checkPath('//DTS:ConnectionManager[{0}]/DTS:ObjectData/DTS:ConnectionManager/DTS:FlatFileColumns/DTS:FlatFileColumn/@DTS:ObjectName'.format(i))) 

    print(dataline) 

['FLATFILE', 'FF', 'Test.csv', '', '11', 'TestCN'] 
['FLATFILE', 'FF2', 'Test2.csv', '', '11', 'TestCN2'] 

PHP (с использованием simple_xml объекта)

$cd = dirname(__FILE__); 

$xml = simplexml_load_file($cd.'/DTSfile.xml'); 
$xml->registerXPathNamespace('DTS', 'www.microsoft.com/SqlServer/Dts'); 

$values = []; 
$node = $xml->xpath('//DTS:ConnectionManager'); 

function checkPath($xml, $xpathstr) {   
    $path = $xml->xpath($xpathstr); 
    foreach ($path as $value) { 
     if (count($path) > 0) { 
      foreach($path as $value) {   
      return $value;   
      } 
     } 
     else { 
      return ''; 
     }  
    }   
} 
$i = 1; 

foreach ($node as $n){   
    $values[] = checkPath($xml, '//DTS:ConnectionManager['.$i.']/@DTS:CreationName'); 
    $values[] = checkPath($xml, '//DTS:ConnectionManager['.$i.']/@DTS:ObjectName'); 
    $values[] = checkPath($xml, '//DTS:ConnectionManager['.$i.']/DTS:ObjectData/DTS:ConnectionManager/@DTS:ConnectionString');  
    $values[] = checkPath($xml, '//DTS:ConnectionManager['.$i.']/DTS:ObjectData/DTS:ConnectionManager/DTS:FlatFileColumns/DTS:FlatFileColumn/@DTS:MaximumWidth'); 
    $values[] = checkPath($xml, '//DTS:ConnectionManager['.$i.']/DTS:ObjectData/DTS:ConnectionManager/DTS:FlatFileColumns/DTS:FlatFileColumn/@DTS:DataType'); 
    $values[] = checkPath($xml, '//DTS:ConnectionManager['.$i.']/DTS:ObjectData/DTS:ConnectionManager/DTS:FlatFileColumns/DTS:FlatFileColumn/@DTS:ObjectName');  

    if ($values[1] != "") { 
     echo implode(", ", $values)."\n"; 
    } 
    $i++; 
    $values = [];   
} 

FLATFILE, FF, Test.csv, , 11, TestCN 
FLATFILE, FF2, Test2.csv, , 11, TestCN2 

R (с использованием библиотеки xml)

library(XML) 

setwd("C:\\Path\\To\\Working\\Directory") 

doc<-xmlParse("DTSfile.xml") 

nodes <- as.list(xpathSApply(doc, '//DTS:ConnectionManager')) 

checkPath <- function (xpathstr) { 
    if (length(as.list(xpathSApply(doc, xpathstr))) > 0) { 
    return(as.list(xpathSApply(doc, xpathstr))) 
    } else { 
    return("") 
    } 
} 

for (i in (1:length(nodes))) { 
    if (i %% 2) { 
    data <- checkPath(sprintf('//DTS:ConnectionManager[%d]/@DTS:CreationName', i)) 
    data <- append(data, checkPath(sprintf('//DTS:ConnectionManager[%d]/@DTS:ObjectName', i))) 
    data <- append(data, checkPath(sprintf('//DTS:ConnectionManager[%d]/DTS:ObjectData/DTS:ConnectionManager/@DTS:ConnectionString', i))) 
    data <- append(data, checkPath(sprintf('//DTS:ConnectionManager[%d]/DTS:ObjectData/DTS:ConnectionManager/DTS:FlatFileColumns/DTS:FlatFileColumn/@DTS:MaximumWidth', i))) 
    data <- append(data, checkPath(sprintf('//DTS:ConnectionManager[%d]/DTS:ObjectData/DTS:ConnectionManager/DTS:FlatFileColumns/DTS:FlatFileColumn/@DTS:DataType', i))) 
    data <- append(data, checkPath(sprintf('//DTS:ConnectionManager[%d]/DTS:ObjectData/DTS:ConnectionManager/DTS:FlatFileColumns/DTS:FlatFileColumn/@DTS:ObjectName', i))) 

    print(paste(data, collapse = ', ')) 
    } 

} 

[1] "FLATFILE, FF, Test.csv, , 11, TestCN" 
[1] "FLATFILE, FF2, Test2.csv, , 11, TestCN2" 
+0

Хороший обзор! Одна из причин, почему я предпочитаю чистый SQL во многих случаях : Вы можете использовать те же функции для (голых) отчетов и для вашего приложения. – Shnugo

Смежные вопросы