2015-11-06 12 views
0

使用PL/pgSQL增量构建XML文档/字符串的最佳方式是什么?考虑以下所需的XML输出:如何在PL/pgSQL中增量构建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

怎么能我PL/pgSQL的使用XML的功能,以确保良好的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; 
+0

最佳方式吗?使用连接到数据库的通用语言(Java,C#,Python,PHP,Perl,R,VB),并使用Person表的所有计算来检索您的选择查询。然后使用前面提到的语言(每个都带有大量的XML库)来跨记录集创建XML文档(验证过的,命名空间,样式表等)。一天结束时的SQL是仅适用于[DDL和DML]的特殊用途语言程序。 – Parfait

回答

1

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

下面是包括Java在内的开源解决方案,每个连接使用相应的PostgreSQL驱动程序(需要安装)。 SQL查询假定为get_factor1()get_factor2(),get_factor3()是内联数据库函数,并且人员在第一列中维护唯一标识。

爪哇(使用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;  

?> 

ř(使用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!") 
1

你的代码有三个问题:

  1. FOR IN variable LOOP不工作 - 如果你真的需要动态SQL,那么你必须使用的形式FOR IN EXECUTE variable ,但更好的是直接写SQL查询,

  2. 但是,它无法通过快,如果人都超过几

    • 遍历所有昂贵的循环体是缓慢的,
    • 字符串连接是昂贵
  3. 输出XML可能是错误的,因为你缺少逃跑。

最后两点都解决了相当不错的SQL/XML功能 - 我只写简单的例子 - 但它确实是蛮强的ANSI/SQL功能(由Postgres的支持)。

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

感谢您的回复,我明显没有在发布之前运行代码。 :)我知道你提到的XML函数,我遇到的真正问题是再次使用因素1,2和3来设置“IsMagicalAddress”的方法。假设每个人都可能需要半秒的时间才能返回,我宁愿不复制代码并导致性能下降,但再次运行相同的代码以将其置于SQL选择内。在这方面有什么想法? – danjuggler

+0

@danjuggler它取决于,当可能时,您应该运行一个查询(您可以使用JOIN,UNION),然后处理结果。这是不可能的 - 有时需要从循环体调用其他函数/查询(可能的原因:可读性,次优计划,..),但通常基于游标的解决方案会比较慢。大约半秒的运行时间非常缓慢,所以有些效果不如可能,但我不能说它是否可以修复。 –