【问题标题】:Efficiently Shred Element Centric XML TSQL高效分解以元素为中心的 XML SQL
【发布时间】:2014-05-15 15:07:01
【问题描述】:

我正在尝试分解具有多层嵌套元素的 XML 文档:

<server>
            <name>111.111.11.11</name>
            <displayName>EVIL SERVER</displayName>
            <comment />
            <logonCredentials inherit="None">
                <userName>user</userName>
                <domain>DOMAIN</domain>
                <password storeAsClearText="True">xxxxxxx</password>
            </logonCredentials>
            <connectionSettings inherit="FromParent" />
            <gatewaySettings inherit="FromParent" />
            <remoteDesktop inherit="FromParent" />
            <localResources inherit="None">
                <audioRedirection>2</audioRedirection>
                <audioRedirectionQuality>2</audioRedirectionQuality>
                <audioCaptureRedirection>0</audioCaptureRedirection>
                <keyboardHook>2</keyboardHook>
                <redirectClipboard>True</redirectClipboard>
                <redirectDrives>True</redirectDrives>
                <redirectPorts>False</redirectPorts>
                <redirectPrinters>False</redirectPrinters>
                <redirectSmartCards>False</redirectSmartCards>
            </localResources>
            <securitySettings inherit="FromParent" />
            <displaySettings inherit="FromParent" />
        </server>
        <server>
            <name>111.12.11.11</name>
            <displayName>NICE SERVER</displayName>
            <comment />
            <logonCredentials inherit="None">
                <userName>user2</userName>
                <domain>DOMAIN2</domain>
                <password storeAsClearText="True">xxxxxxx</password>
            </logonCredentials>
            <connectionSettings inherit="FromParent" />
            <gatewaySettings inherit="FromParent" />
            <remoteDesktop inherit="FromParent" />
            <localResources inherit="FromParent" />
            <securitySettings inherit="FromParent" />
            <displaySettings inherit="FromParent" />
        </server>

我想将它们返回到一个表格中,其中包含从服务器名称到登录凭据的所有值。

我已经能够像这样使用 CROSS APPLY 做到这一点:(不完整)

 SELECT
    name = n.value('name[1]','varchar(15)'),
    displayName = dn.value('.','varchar(8000)'),
    comment = c.value('.','varchar(8000)'),
    userName = un.value('.','varchar(8000)'),
    domain = d.value('.','varchar(8000)'),
    [password] = p.value('.','varchar(8000)'),
    connectionSettings = cs.value('.','varchar(8000)')
    audioRedirection = ar.value('.', 'varchar(8000)')
FROM @RDCM.nodes('/group/server') AS s(n)
    CROSS APPLY s.n.nodes('displayName') s1(dn)
    CROSS APPLY s.n.nodes('comment') s2(c)
    CROSS APPLY s.n.nodes('logonCredentials') s4(lc)
        CROSS APPLY s4.lc.nodes('userName') lc1(un)
        CROSS APPLY s4.lc.nodes('domain') lc2(d)
        CROSS APPLY s4.lc.nodes('password') lc3(p)
    CROSS APPLY s.n.nodes('connectionSettings') s3(cs)
    CROSS APPLY s.n.nodes('localResources') s8(lr)
        OUTER APPLY s8.lr.nodes('audioRedirection') lr1(ar)

但是,我才刚刚开始构建这个查询,很明显它需要很长时间才能运行。

我也尝试过 OPENXML,但我只能获得一个级别。

【问题讨论】:

  • 永远是一段很长的时间。你的 XML 有多大?你需要解析多少个服务器节点?需要多长时间?
  • 而且看起来您实际上根本不需要任何交叉申请。在 values() 函数中使用路径表达式是完全可以的。 openxml 也是如此。您正在服务器上粉碎,剩下的只是找到您需要的价值。

标签: sql-server xml performance tsql xquery-sql


【解决方案1】:

我不会在 Transact-SQL 中这样做。虽然您可以在 SQL Server 中处理 XML,但如果您无法在拥有更好(和更快)工具的更高级别处理 XML 时,您只想在那里执行此操作。

如果我是你,我会在 C# 中编写一组简单的类,将 XElement 子类化,并公开一个名为 StuffParameters() 的方法以方便您的插入/更新语句。因为 XML 粉碎在 .Net 中非常容易做到......

// Typed in the editor -- code needs to be eval'd before attempting to compile!
public class ServerData: XElement
{
    public ServerData(string strServer): base(strServer)
    {
        // Base class initializes XElement with XML, throws error if XML not well-formed
    }

    // Properties
    public string Name    // One example of the two dozen properties you'll need
    {
        get()
        {
            return GetAttribute("name");
        }
    }

    // StuffParameters
    public bool StuffParameters(SqlCommand objCommand)
    {
        // In real life you'd use try...catch, but this is prototype code, right?
        objCommand.Parameters["@ServerName"].Value = Name;
        // ... and so forth
    }
}

从您的服务器 XDocument 列表开始;为每个创建此类的一个实例。创建一个到您的数据库的连接,并使用您的存储过程创建一个 SqlCommand 对象。遍历这些类的列表,将命令传递给每个类实例的 StuffParameters() 方法。

当函数返回时,在您的命令上调用 ExecuteNonQuery(),并且您已将记录插入到您的表中。

编码更少,处理 XML 的方式更正统——因此,如果您不得不更改它,您不会最终得到一个需要数小时思考的令人头疼的方法。

希望对你有所帮助....

【讨论】:

    【解决方案2】:

    这实际上比您预期的要简单得多。您可以使用单个 .nodes() 函数轻松遍历 XML,然后在 .value() 函数中指定子路径:

    SELECT n.value('(./name/text())[1]','varchar(15)') AS [name],
           n.value('(./displayName/text())[1]','varchar(15)') AS [displayName],
           n.value('(./comment/text())[1]','varchar(15)') AS [comment],
      n.value('(./logonCredentials/userName/text())[1]','varchar(15)') AS [userName],
          n.value('(./logonCredentials/domain/text())[1]','varchar(15)') AS [domain],
      n.value('(./logonCredentials/password/text())[1]','varchar(15)') AS [password],
           n.value('(./connectionSettings/text())[1]','varchar(15)')
                  AS [connectionSettings],
         n.value('(./gatewaySettings/text())[1]','varchar(15)') AS [gatewaySettings],
           n.value('(./remoteDesktop/text())[1]','varchar(15)') AS [remoteDesktop],
           n.value('(./localResources/audioRedirection/text())[1]','varchar(15)')
                  AS [audioRedirection],
        n.value('(./localResources/audioRedirectionQuality/text())[1]','varchar(15)')
                  AS [audioRedirectionQuality],
        n.value('(./localResources/audioCaptureRedirection/text())[1]','varchar(15)')
                  AS [audioCaptureRedirection],
       n.value('(./securitySettings/text())[1]','varchar(15)') AS [securitySettings],
          n.value('(./displaySettings/text())[1]','varchar(15)') AS [displaySettings]
    FROM @RDCM.nodes('/group/server') AS s(n)
    

    全面测试:

    DECLARE @RDCM XML
    SET @RDCM = N'<group>
    <server>
                <name>111.111.11.11</name>
                <displayName>EVIL SERVER</displayName>
                <comment />
                <logonCredentials inherit="None">
                    <userName>user</userName>
                    <domain>DOMAIN</domain>
                    <password storeAsClearText="True">xxxxxxx</password>
                </logonCredentials>
                <connectionSettings inherit="FromParent" />
                <gatewaySettings inherit="FromParent" />
                <remoteDesktop inherit="FromParent" />
                <localResources inherit="None">
                    <audioRedirection>2</audioRedirection>
                    <audioRedirectionQuality>2</audioRedirectionQuality>
                    <audioCaptureRedirection>0</audioCaptureRedirection>
                    <keyboardHook>2</keyboardHook>
                    <redirectClipboard>True</redirectClipboard>
                    <redirectDrives>True</redirectDrives>
                    <redirectPorts>False</redirectPorts>
                    <redirectPrinters>False</redirectPrinters>
                    <redirectSmartCards>False</redirectSmartCards>
                </localResources>
                <securitySettings inherit="FromParent" />
                <displaySettings inherit="FromParent" />
            </server>
            <server>
                <name>111.12.11.11</name>
                <displayName>NICE SERVER</displayName>
                <comment />
                <logonCredentials inherit="None">
                    <userName>user2</userName>
                    <domain>DOMAIN2</domain>
                    <password storeAsClearText="True">xxxxxxx</password>
                </logonCredentials>
                <connectionSettings inherit="FromParent" />
                <gatewaySettings inherit="FromParent" />
                <remoteDesktop inherit="FromParent" />
                <localResources inherit="FromParent" />
                <securitySettings inherit="FromParent" />
                <displaySettings inherit="FromParent" />
            </server>
            </group>'
    
    
    SELECT n.value('(./name/text())[1]','varchar(15)') AS [name],
           n.value('(./displayName/text())[1]','varchar(15)') AS [displayName],
           n.value('(./comment/text())[1]','varchar(15)') AS [comment],
    n.value('(./logonCredentials/userName/text())[1]','varchar(15)') AS [userName],
        n.value('(./logonCredentials/domain/text())[1]','varchar(15)') AS [domain],
    n.value('(./logonCredentials/password/text())[1]','varchar(15)') AS [password],
           n.value('(./connectionSettings/text())[1]','varchar(15)')
                  AS [connectionSettings],
       n.value('(./gatewaySettings/text())[1]','varchar(15)') AS [gatewaySettings],
           n.value('(./remoteDesktop/text())[1]','varchar(15)') AS [remoteDesktop],
           n.value('(./localResources/audioRedirection/text())[1]','varchar(15)')
                  AS [audioRedirection],
      n.value('(./localResources/audioRedirectionQuality/text())[1]','varchar(15)')
                  AS [audioRedirectionQuality],
      n.value('(./localResources/audioCaptureRedirection/text())[1]','varchar(15)')
                  AS [audioCaptureRedirection],
     n.value('(./securitySettings/text())[1]','varchar(15)') AS [securitySettings],
        n.value('(./displaySettings/text())[1]','varchar(15)') AS [displaySettings]
    FROM @RDCM.nodes('/group/server') AS s(n)
    

    【讨论】:

      猜你喜欢
      • 2014-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多