2015-11-06 37 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),然後處理結果。這是不可能的 - 有時需要從循環體調用其他函數/查詢(可能的原因:可讀性,次優計劃,..),但通常基於遊標的解決方案會比較慢。大約半秒的運行時間非常緩慢,所以有些效果不如可能,但我不能說它是否可以修復。 –