如何在PL / pgSQL中增量构建XML文档

时间:2015-11-06 18:55:49

标签: sql xml postgresql plpgsql postgresql-8.4

使用PL / pgSQL逐步构建XML文档/字符串的最佳方法是什么?考虑以下所需的XML输出:

<Directory>
  <Person>
    <Name>Bob</Name>
    <Address>1234 Main St</Address>
    <MagicalAddressFactor1>3</MagicalAddressFactor1>
    <MagicalAddressFactor2>8</MagicalAddressFactor2>
    <MagicalAddressFactor3>1</MagicalAddressFactor3>
    <IsMagicalAddress>Y</IsMagicalAddress>
  </Person>
  <Person>
    <Name>Joshua</Name>
    <Address>100 Broadway Blvd</Address>
    <MagicalAddressFactor1>2</MagicalAddressFactor1>
    <MagicalAddressFactor2>1</MagicalAddressFactor2>
    <MagicalAddressFactor3>4</MagicalAddressFactor3>
    <IsMagicalAddress>Y</IsMagicalAddress>
  </Person>
</Directory>

其中:

  • 人名和地址基于简单的人员表。
  • MagicalAddressFactor 1,2和3都基于对Person表中其他表的一些复杂链接和计算。
  • IsMagicalAddress基于三个MagicalAddressFactors的总和大于10.

我如何使用XML函数生成PL / pgSQL以确保格式良好的XML元素?不使用XML函数,代码看起来像这样:

DECLARE
  v_sql text;
  v_rec RECORD;
  v_XML xml;
  v_factor1 integer;
  v_factor2 integer;
  v_factor3 integer;
  v_IsMagical varchar;
BEGIN
  v_XML := '<Directory>';
  v_sql := 'select * from person;'
  FOR v_rec IN v_sql LOOP
    v_XML := v_XML || '<Name>' || v_rec.name || '</Name>' ||
                      '<Address>' || v_rec.Address || '</Address>';
    v_factor1 := get_factor_1(v_rec);
    v_factor2 := get_factor_2(v_rec);
    v_factor3 := get_factor_3(v_rec);
    v_IsMagical := case
                     when (v_factor1 + v_factor2 + v_factor3) > 10 then
                       'Y'
                     else
                       'N'
                   end;
    v_XML := v_XML || '<MagicalAddressFactor1>' || v_factor1 || '</MagicalAddressFactor1>' ||
                      '<MagicalAddressFactor2>' || v_factor2 || '</MagicalAddressFactor2>' ||
                      '<MagicalAddressFactor3>' || v_factor3 || '</MagicalAddressFactor3>' ||
                      '<IsMagicalAddress>' || v_IsMagical || '</IsMagicalAddress>';
  v_XML := v_XML || '</Person>'
END LOOP;
  v_XML := v_XML || '</Directory>'
END;

2 个答案:

答案 0 :(得分:1)

您的代码有三个问题:

  1. FOR IN variable LOOP无法正常工作 - 如果您确实需要动态SQL,那么您必须使用表单FOR IN EXECUTE variable,但更好的是直接编写SQL查询,

    < / LI>
  2. 但是,如果人数不多,那就不能快速了。

    • 迭代昂贵的循环体很慢,
    • 字符串连接很昂贵
  3. 输出XML可能是错误的,因为您缺少转义。

  4. 最后两点通过 SQL/XML 函数很好地解决了 - 我只编写了一个简单的例子 - 但它确实非常强大的ANSI / SQL功能(Postgres支持) 。

    SELECT xmlelement(NAME "Directory",
              xmlagg(xmlelement(NAME "Person",
                         xmlforest(name AS "Name", 
                                   address AS "Address"))))
       FROM persons;
    

答案 1 :(得分:1)

对于OP和未来的读者,可以在需要时将数据库内容迁移到XML文档时考虑使用通用语言。只需通过ODBC / OLEDB驱动程序连接,检索查询并输出到XML文档。使用OP的需求,可以将计算合并到一个选择查询或存储过程中,该过程返回结果集并具有用于文档构建的编码语言导入记录。

下面是包含Java的开源解决方案,其中每个解决方案都使用相应的PostgreSQL驱动程序进行连接(需要安装)。 SQL查询假设get_factor1()get_factor2()get_factor3()是内联数据库函数,而Persons在第一列中保留唯一ID。

Java (使用Postgre JDBC driver

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.OutputKeys;

import java.sql.* ;
import java.util.ArrayList;
import java.io.IOException;
import java.io.File;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class SQLtoXML {       

    public static void main(String[] args) {

        String currentDir = new File("").getAbsolutePath();

            try {                
                String url = "jdbc:postgresql://localhost/test";
                Properties props = new Properties();
                props.setProperty("user","sqluser");
                props.setProperty("password","secret");
                props.setProperty("ssl","true");
                Connection conn = DriverManager.getConnection(url, props);

                String url = "jdbc:postgresql://localhost/test?user=sqlduser&password=secret&ssl=true";
                Connection conn = DriverManager.getConnection(url);

                Statement stmt = conn.createStatement();
                ResultSet rs = stmt.executeQuery("SELECT name, address, " 
                               + "get_factor_1(v_rec) As v_factor1, " 
                               + "get_factor_2(v_rec) As v_factor2, " 
                               + "get_factor_3(v_rec) As v_factor3, " 
                               + " CASE WHEN (get_factor_1(v_rec) + "
                               + "   get_factor_2(v_rec) + "
                               + "   get_factor_3(v_rec)) > 10 " 
                               + " THEN 'Y' ELSE 'N' END As v_isMagical " 
                               + " FROM Persons;");

                // Write to XML document
                DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();            
                DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
                Document doc = docBuilder.newDocument();

                // Root element
                Element rootElement = doc.createElement("Directory");
                doc.appendChild(rootElement);

                // Export table data
                ResultSetMetaData rsmd = rs.getMetaData();
                int columnsNumber = rsmd.getColumnCount();
                while (rs.next()) {

                    // Data rows            
                    Element personNode = doc.createElement("Person");
                    rootElement.appendChild(personNode);    

                    Element nameNode = doc.createElement("name");
                    nameNode.appendChild(doc.createTextNode(rs.getString(2)));
                    personNode.appendChild(nameNode);

                    Element addressNode = doc.createElement("address");
                    addressNode.appendChild(doc.createTextNode(rs.getString(3)));
                    personNode.appendChild(addressNode);

                    Element magicaladd1Node = doc.createElement("MagicalAddressFactor1");
                    magicaladd1Node.appendChild(doc.createTextNode(rs.getString(4)));
                    personNode.appendChild(magicaladd1Node);

                    Element magicaladd2Node = doc.createElement("MagicalAddressFactor2");
                    magicaladd2Node.appendChild(doc.createTextNode(rs.getString(5)));
                    personNode.appendChild(magicaladd2Node);

                    Element magicaladd3Node = doc.createElement("MagicalAddressFactor3");
                    magicaladd3Node.appendChild(doc.createTextNode(rs.getString(6)));
                    personNode.appendChild(magicaladd3Node);

                    Element isMagicalNode = doc.createElement("IsMagicalAddress");
                    isMagicalNode.appendChild(doc.createTextNode(rs.getString(7)));
                    personNode.appendChild(isMagicalNode);                       

                }                    

                rs.close();
                stmt.close();
                conn.close();

                // Output content to xml file
                TransformerFactory transformerFactory = TransformerFactory.newInstance();                
                Transformer transformer = transformerFactory.newTransformer();
                transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

                DOMSource source = new DOMSource(doc);
                StreamResult result = new StreamResult(new File(currentDir + "\\PostgreXML_java.xml"));     
                transformer.transform(source, result);

                System.out.println("Successfully created xml file!");

            } catch (ParserConfigurationException pce) {
                System.out.println(pce.getMessage());            
            } catch (TransformerException tfe) {
                System.out.println(tfe.getMessage());            
            } catch (SQLException err) {            
                System.out.println(err.getMessage());
            }                     
    }
}

Python (使用Psycopg模块)

import psycopg2
import os
import lxml.etree as ET

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

# DB CONNECTION AND QUERY
db = psycopg2.connect("dbname=test user=postgres")
cur = db.cursor()
cur.execute("SELECT name, address, \
               get_factor_1(v_rec) As v_factor1, \
               get_factor_2(v_rec) As v_factor2, \
               get_factor_3(v_rec) As v_factor3, \
               CASE WHEN (get_factor_1(v_rec) + \
                  get_factor_2(v_rec) + \
                  get_factor_3(v_rec)) > 10 \
               THEN 'Y' ELSE 'N' END As v_isMagical \
             FROM Persons;")

# WRITING XML FILE
root = ET.Element('Directory')

for row in cur.fetchall():
    personNode = ET.SubElement(root, "Person")
    ET.SubElement(personNode, "Name").text = row[1]
    ET.SubElement(personNode, "Address").text = row[2]   
    ET.SubElement(personNode, "MagicalAddressFactor1").text = row[3]
    ET.SubElement(personNode, "MagicalAddressFactor2").text = row[4]    
    ET.SubElement(personNode, "MagicalAddressFactor3").text = row[5]
    ET.SubElement(personNode, "IsMagicalAddress").text = row[6]        

# CLOSE CURSOR AND DATABASE
cur.close()
db.close()

# OUTPUT XML
tree_out = (ET.tostring(root, pretty_print=True, xml_declaration=True, encoding="UTF-8"))

xmlfile = open(os.path.join(cd, 'PostgreXML_py.xml'),'wb')
xmlfile.write(tree_out)
xmlfile.close()       

print("Successfully migrated SQL to XML data!")

PHP (使用Postgre PDO驱动程序)

<?php

$cd = dirname(__FILE__);

// create a dom document with encoding utf8 
$domtree = new DOMDocument('1.0', 'UTF-8');
$domtree->formatOutput = true;
$domtree->preserveWhiteSpace = false;

# Opening db connection
$host="root";
$dbuser = "*****";

try {
    $dbh = new PDO("pgsql:dbname=$dbname;host=$host", $dbuser, $dbpass);    
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    $sql = "SELECT name, address, 
               get_factor_1(v_rec) As v_factor1, 
               get_factor_2(v_rec) As v_factor2, 
               get_factor_3(v_rec) As v_factor3, 
               CASE WHEN (get_factor_1(v_rec) + 
                  get_factor_2(v_rec) + 
                  get_factor_3(v_rec)) > 10 
               THEN 'Y' ELSE 'N' END As v_isMagical 
            FROM Persons;";    
    $STH = $dbh->query($sql);    
    $STH->setFetchMode(PDO::FETCH_ASSOC); 
}

catch(PDOException $e) {  
    echo $e->getMessage();
    exit;
}

/* create the root element of the xml tree */
$xmlRoot = $domtree->createElement("Directory");
$xmlRoot = $domtree->appendChild($xmlRoot);

/* loop query results through child elements */
while($row = $STH->fetch()) {  

     $personNode = $xmlRoot->appendChild($domtree->createElement('Person'));

     $nameNode = $personNode->appendChild($domtree->createElement('Name', $row['name']));
     $addNode = $personNode->appendChild($domtree->createElement('Address', $row['address']));
     $magadd1Node = $personNode->appendChild($domtree->createElement('MagicalAddressFactor1', $row['v_factor1']));
     $magadd2Node = $personNode->appendChild($domtree->createElement('MagicalAddressFactor2', $row['v_factor2']));
     $magadd3Node = $personNode->appendChild($domtree->createElement('MagicalAddressFactor3', $row['v_factor3']));
     $ismagicalNode = $personNode->appendChild($domtree->createElement('IsMagicalAddress', $row['v_isMagical']));

}

file_put_contents($cd. "/PostgreXML_php.xml", $domtree->saveXML());

echo "\nSuccessfully migrated SQL data into XML!\n";

# Closing db connection
$dbh = null;
exit;    

?>

R (使用RPostgreSQL包)

library(RPostgreSQL)
library(XML)

#setwd("C:/path/to/working/folder")

# OPEN DATABASE AND QUERY
drv <- dbDriver("PostgreSQL")
conn <- dbConnect(drv, dbname="tempdb")

df <- sqlQuery(conn, "SELECT name, address, 
                         get_factor_1(v_rec) As v_factor1, 
                         get_factor_2(v_rec) As v_factor2, 
                         get_factor_3(v_rec) As v_factor3, 
                         CASE WHEN (get_factor_1(v_rec) + 
                            get_factor_2(v_rec) + 
                            get_factor_3(v_rec)) > 10 
                         THEN 'Y' ELSE 'N' END As v_isMagical 
                      FROM Persons;")
close(conn)

# CREATE XML FILE
doc = newXMLDoc()
root = newXMLNode("Directory", doc = doc)

# WRITE XML NODES AND DATA
for (i in 1:nrow(df)){
  personNode = newXMLNode("Person", parent = root)

  nameNode = newXMLNode("name", df$name[i], parent = personNode)
  addressNode = newXMLNode("address", df$address[i], parent = personNode)
  magicaladdress1Node = newXMLNode("MagicalAddressFactor1", df$v_factor1[i], parent = personNode)
  magicaladdress2Node = newXMLNode("MagicalAddressFactor2", df$v_factor2[i], parent = personNode)
  magicaladdress3Node = newXMLNode("MagicalAddressFactor3", df$v_factor3[i], parent = personNode)
  ismagicalNode = newXMLNode("IsMagicalAddress", df$v_isMagical[i], parent = personNode)

}

# OUTPUT XML CONTENT TO FILE
saveXML(doc, file="PostgreXML_R.xml")

print("Successfully migrated SQL to XML data!")