Get element value from XML datatype field - sql

I want to retrieve element value from XML data field. Please check below code snippet for more detail:
SQL Script:
CREATE TABLE #Temp1 (ConfigXSLT XML);
INSERT INTO #Temp1 VALUES('<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes" />
<xsl:variable name="transport">
<transport protocol="FILE" direction="get" filename="file1*.csv" />
</xsl:variable>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>')
-- I want only filename element value 'file1*.csv' in select statement
-- I have tried with below query but it not works
SELECT ConfigXSLT.value('declare namespace PD="http://www.w3.org/1999/XSL/Transform"; (//PD:variable/PD:transport/PD:filename)[1]','VARCHAR(MAX)')
FROM #Temp1
DROP TABLE #Temp1

You went overboard with using PD namespace (why PD btw?), the "transport" telement is without a namespace. Also, to select attribute you use # sign before the attributes name :)
Try this:
SELECT ConfigXSLT.value('declare namespace PD="http://www.w3.org/1999/XSL/Transform"; (//PD:variable/transport/#filename)[1]','VARCHAR(MAX)')
FROM #Temp1
Result:
file1*.csv

select #xml.value('(/stylesheet/variable/transport/#filename)[1]', 'nvarchar(max)')
Maybe something like that?

Related

Creating SQL INSERT statement out of XML file data

I want to generate an SQL INSERT statement from data encoded in XML files, using an XSLT.
My XML files include, for example, the following tags (describing a tombstone):
<?xml version="1.0" encoding="UTF-8"?>
<?xml-model href="http://www.stoa.org/epidoc/schema/latest/tei-epidoc.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?>
<TEI xmlns="http://www.tei-c.org/ns/1.0" xml:lang="en">
<teiHeader>
<fileDesc>
<titleStmt>
<title>Funerary inscription for Marcellus, a smith</title>
</titleStmt>
<publicationStmt>
<authority>I.Sicily</authority>
<idno type="TM">491539</idno>
</publicationStmt>
<sourceDesc>
<msDesc>
<msIdentifier>
<country>Italy</country>
<region>Sicily</region>
<settlement>Catania</settlement>
<repository role="museum" ref="http://sicily.classics.ox.ac.uk/museum/018"
>Museo Civico di Catania </repository>
<idno type="inventory">390</idno>
<altIdentifier>
<settlement/>
<repository/>
<idno type="old"/>
</altIdentifier>
</msIdentifier>
<msContents>
<textLang mainLang="la">Latin </textLang>
</msContents>
<physDesc>
<objectDesc>
<supportDesc>
<support>
<material n="marble"
ref="http://www.eagle-network.eu/voc/material/lod/48.html"
>marble </material>
<objectType n="tabula"
ref="http://www.eagle-network.eu/voc/objtyp/lod/257">tablet </objectType>
<dimensions>
<height unit="cm">29</height>
<width unit="cm">33.5</width>
<depth unit="cm">2.1</depth>
</dimensions>
</support>
</supportDesc>
</objectDesc>
</physDesc>
</msDesc>
</sourceDesc>
</fileDesc>
</teiHeader>
<!-- lots more content that I cut away here -->
</TEI>
I would like to extract information from this and insert it into an SQL table.
My desired output would look like this:
INSERT INTO tblObjects
(ObjectID, Title, TMid, Material, ObjectType, Height)
VALUES
('Funerary inscription for Marcellus, a smith', 491539, 'marble', 'tabula', 29);
My original - now solved - problem was this:
I tried setting the output method to text after some examples I found
online, but this is giving me the error message "Non-whitespace
characters are not allowed in schema elements other than 'xs:appinfo'
and 'xs:documentation'."
I changed the file format to xsl, now it's no longer complaining about the non-whitespace characters. I can now put text into the final output. The following (inspired by your answers) is the current state of my XSLT, for now I want to try to just insert the value of the title, the position of which is "TEI/teiHeader/fileDesc/titleStmt/title":
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"
encoding="UTF-8"
omit-xml-declaration="yes"
indent="no"/>
<xsl:template match="/">
<xsl:text>INSERT INTO tblObjects</xsl:text>
<xsl:text>(ObjectID, Title, TMid, Material, ObjectType, Height) VALUES (</xsl:text>
<xsl:apply-templates select="root"/>
<xsl:text>);</xsl:text>
</xsl:template>
<xsl:template match="root">
<xsl:value-of select="TEI/teiHeader/fileDesc/titleStmt/title"/>
</xsl:template>
</xsl:stylesheet>
This gives me the following output:
INSERT INTO tblObjects(ObjectID, Title, TMid, Material, ObjectType, Height) VALUES ();
Yet, as you can see, it does not insert the value of the title. I am not sure why that's not working (only trying the title for now).
Simply walk down the tree with templates and concatenate values with needed quotes and commas for SQL query's VALUES clause. Due to the many nested structure of XML, ancestor::* and descendant::* paths are used for specific node value extraction.
Note: This solution works for XML files for one teiHeader. You will need to tailor this solution or run other XSLT scripts for other types.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:doc="http://www.tei-c.org/ns/1.0">
<xsl:output method="text" encoding="UTF-8" omit-xml-declaration="yes" indent="no"/>
<xsl:template match="/doc:TEI">
<xsl:text>INSERT INTO tblObjects</xsl:text>
<xsl:text>(ObjectID, Title, TMid, Material, ObjectType, Height)
VALUES
</xsl:text>
<xsl:apply-templates select="doc:teiHeader/doc:fileDesc/doc:sourceDesc/doc:msDesc/doc:physDesc"/>
</xsl:template>
<xsl:template match="doc:physDesc">
<xsl:variable name="quote">&apos;</xsl:variable>
<xsl:value-of select="concat('(',
$quote, ancestor::doc:fileDesc/doc:titleStmt/doc:title, $quote, ', ',
ancestor::doc:fileDesc/doc:publicationStmt/doc:idno[#type='TM'], ', ',
$quote, normalize-space(descendant::doc:material), $quote, ', ',
$quote, normalize-space(descendant::doc:objectType), $quote, ', ',
descendant::doc:dimensions/doc:height,
')'
)"/>
</xsl:template>
</xsl:stylesheet>
Online Demo

xslt : xml transformation, need soql query as output

Thanks for the solution.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns="urn:partner.soap.sforce.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<LimitInfoHeader>
<limitInfo>
<current>18029</current>
<limit>5000000</limit>
<type>API REQUESTS</type>
</limitInfo>
</LimitInfoHeader>
</soapenv:Header>
<soapenv:Body>
<upsertResponse>
<result>
<created>false</created>
<id xsi:nil="true"/>
<success>false</success>
</result>
<result>
<created>false</created>
<id xsi:nil="true"/>
<success>false</success>
</result>
<result>
<created>false</created>
<id xsi:nil="true"/>
<success>false</success>
</result>
</upsertResponse>
</soapenv:Body>
</soapenv:Envelope>
for the above xml as input to xslt, the output is below one
<?xml version="1.0" encoding="utf-8"?><soql>select (select Id from contacts where AccountId in (SELECT AccountId from Opportunity where id in ())), (select Id from opportunities where id in()) from account where id in (SELECT AccountId from Opportunity where id in())</soql>
if all the success tag values are false in the given input xml, the <soql> tag should not be generated. i mean output should be empty
eventually what i need is valid SOQL, so that i can query sales force, for the above given xml input the output generated is not a valid SOQL. henceforth i cannot query sales force.
so where the changes in xslt needs to be done, need your help
Thanks
Why can't you do simply:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns="urn:partner.soap.sforce.com"
exclude-result-prefixes="ns">
<xsl:variable name="ids">
<xsl:for-each select="//ns:result[ns:success='true']">
<xsl:text>'</xsl:text>
<xsl:value-of select="ns:id" />
<xsl:text>'</xsl:text>
<xsl:if test="position()!=last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:template match="/">
<soql>
<xsl:text>select (select Id from contacts where AccountId in (SELECT AccountId from Opportunity where id in (</xsl:text>
<xsl:value-of select="$ids"/>
<xsl:text>))), (select Id from opportunities where id in(</xsl:text>
<xsl:value-of select="$ids"/>
<xsl:text>)) from account where id in (SELECT AccountId from Opportunity where id in(</xsl:text>
<xsl:value-of select="$ids"/>
<xsl:text>))</xsl:text>
</soql>
</xsl:template>
</xsl:stylesheet>

How to sum the amounts in a xslt for same ids appearing in XML

I need to produce a text file in the output. I have to first check if a perosn has aby of the following node PreTaxAmt,MatchAmt,RothAmt, then produce a single line in output with required values and then I have to check if the perosn has LoanAmt node, if they have then I have to create another entry for that person. I was able to code till this point but now I am having a requiremnet where if the same person is repated more than once for example below laurel Alex , I need to produce just one entry for single participant id and the PreTaxAmt,MatchAmt,RothAmt amount should be summed up in the first entry in the output and in the in the loan entry row the LoanAmt should be summed up.
Here is my xml.
<Report_Data >
<Report_Entry>
<Participant_ID>03300</Participant_ID>
<Workers>
<FULL_PART_Time_Indicator>1</FULL_PART_Time_Indicator>
<Last_Name>Mary</Last_Name>
<First_Name>Mehul</First_Name>
</Workers>
<End_Date_from_Pay_Period>2016-01-03-08:00</End_Date_from_Pay_Period>
<PreTaxAmt>100.8</PreTaxAmt>
<MatchAmt>100.8</MatchAmt>
<RothAmt>0</RothAmt>
<LoanAmt>0</LoanAmt>
</Report_Entry>
<Report_Entry>
<Participant_ID>10600</Participant_ID>
<Workers>
<FULL_PART_Time_Indicator>1</FULL_PART_Time_Indicator>
<Last_Name>Laurel</Last_Name>
<First_Name>Alex</First_Name>
</Workers>
<End_Date_from_Pay_Period>2016-01-03-08:00</End_Date_from_Pay_Period>
<PreTaxAmt>61.48</PreTaxAmt>
<MatchAmt>61.47</MatchAmt>
<RothAmt>0</RothAmt>
<LoanAmt>0</LoanAmt>
</Report_Entry>
<Report_Entry>
<Participant_ID>10600</Participant_ID>
<Workers>
<FULL_PART_Time_Indicator>1</FULL_PART_Time_Indicator>
<Last_Name>Laurel</Last_Name>
<First_Name>Alex</First_Name>
<Middle_Name>S</Middle_Name>
</Workers>
<End_Date_from_Pay_Period>2016-01-03-08:00</End_Date_from_Pay_Period>
<PreTaxAmt>0</PreTaxAmt>
<MatchAmt>0</MatchAmt>
<RothAmt>0</RothAmt>
<LoanAmt>0</LoanAmt>
</Report_Entry>
<Report_Entry>
<Participant_ID>13100</Participant_ID>
<Workers>
<FULL_PART_Time_Indicator>1</FULL_PART_Time_Indicator>
<Last_Name>Smita</Last_Name>
<First_Name>Paul</First_Name>
</Workers>
<End_Date_from_Pay_Period>2016-01-03-08:00</End_Date_from_Pay_Period>
<PreTaxAmt>0</PreTaxAmt>
<MatchAmt>0</MatchAmt>
<RothAmt>0</RothAmt>
<LoanAmt>0</LoanAmt>
</Report_Entry>
Here is my xslt :
<xsl:template match="/">
<xsl:for-each select="Report_Data/Report_Entry">
<xsl:if test="(PreTaxAmt) or(MatchAmt) or (RothAmt) ">
<xsl:call-template name="DataRecords"/>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="Report_Data/Report_Entry">
<xsl:if test="LoanAmt">
<xsl:call-template name="LoanDataRecord"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="DataRecords">
<xsl:for-each-group select="." group-by="wd:Participant_ID">
<xsl:variable name="ParticpantLoan" select="current-grouping-key()"/>
!-- Transaction Code -->
<xsl:value-of select="'Contribution'"/>
<!-- Participant Number -->
<xsl:value-of select="Participant_ID,$filler),1,9"/>
<!-- FT/PT Indicator -->
<xsl:value-of select="substring(concat(Workers/FULL_PART_Time_Indicator,$filler),1,1)"/>
<!-- Contribution Amount1 -->
<xsl:value-of select="(sum(current-group()/PreTaxAmt))"/>
<!-- Contribution Amount2 -->
<xsl:value-of select="(sum(current-group()/MatchAmt))"/>
<!-- Contribution Amount3 -->
<xsl:value-of select="(sum(current-group()/RothAmt))"/>
<xsl:value-of select="$linefeed"/>
</xsl:for-each-group>
</xsl:template>
<xsl:template name="LoanDataRecord">
<xsl:for-each-group select="." group-by="Participant_ID">
<xsl:variable name="ParticpantLoan" select="current-grouping-key()"/>
<!-- Transaction Code -->
<xsl:value-of select="substring(concat('LoanData',$filler),1,3)"/>
<!-- Participant Number -->
<xsl:value-of select="substring(concat(Participant_ID,$filler),1,9)"/>
<!-- Loan Amt -->
<xsl:value-of select="(sum(current-group()/LoanAmt))"/>
<xsl:value-of select="$linefeed"/>
</xsl:for-each-group>
</xsl:template>
The output should have six lines.
Also, how can I calculate the total count of the records in my output file. Earlier I was using below code but now since there would be duplicate SSN how should I modify my existing code for counting ..
0 ] or Report_Data/Report_Entry/MatchAmt[number(.) > 0 ] or Report_Data/Report_Entry/RothAmt[number(.) > 0 ])+
count(Report_Data/Report_Entry/LoanAmt[number(.) > 0 ])

Can I alias columns from a cursor in SELECT statement in Oracle?

I am writing a client application that calls a stored procedure from an Oracle database via a select statement. The stored procedure returns a cursor. I need to define aliases for the columns returned by this cursor, and I need to do it within my select statement.
I cannot make any changes to the Oracle database. I cannot write any PLSQL. The only thing I can do with this database is query it.
Please advise.
Background: This stored procedure is one of many called inside an application framework. Currently, all calls return their results in XML format, using this syntax to do the conversion:
select XMLType.createXML(package_name.storedProcName('1', '2', '3')).getClobVal() as sresult from dual;
However, this cursor contains two columns with the same name (specifically "NAME"). When this query is run in TOAD, the column automatically gets appended a "_1", however the XMLType results in illogical XML, like this:
<?xml version="1.0"?>
<ROWSET>
<ROW>
<ID>1</ID>
<NAME>BRUCE WAYNE</NAME>
<NAME>BATMAN</NAME>
</ROW>
</ROWSET>
This is why I must alias the columns before they are converted to XMLType. I want the query output to contain no duplicate column names so that the XML can be like this instead (with no duplicate tags):
<?xml version="1.0"?>
<ROWSET>
<ROW>
<ID>1</ID>
<NAME>BRUCE WAYNE</NAME>
<OTHER_NAME>BATMAN</OTHER_NAME>
</ROW>
</ROWSET>
i would go for a stylesheet for this.
eg:
SQL> select XMLType.createXML(foo()).transform(xmltype('<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
2 <xsl:template match="/ROWSET/ROW/NAME[2]">
3 <NAME_1>
4 <xsl:apply-templates select="#*|node()"/>
5 </NAME_1>
6 </xsl:template>
7 <xsl:template match="#*|node()">
8 <xsl:copy>
9 <xsl:apply-templates select="#*|node()"/>
10 </xsl:copy>
11 </xsl:template>
12 </xsl:stylesheet>')) as sresult from dual
13 /
SRESULT
--------------------------------------------------------------------------------
<ROWSET>
<ROW>
<ID>1</ID>
<NAME>BRUCE WAYNE</NAME>
<NAME_1>BATMAN</NAME_1>
</ROW>
<ROW>
<ID>2</ID>
<NAME>CLARK KENT</NAME>
<NAME_1>SUPERMAN</NAME_1>
</ROW>
</ROWSET>
i.e. we replace the 2nd NAME occurrence (/ROWSET/ROW/NAME[2]) in the ROW element with NAME_1. everything else gets copied as-is.

XSLT variable in predicate not working

my xml looks something like this:
<units>
<unit>
<unitnum></unitnum>
<Year></Year>
<Qty></Qty>
</unit>
</units>
I create a key to capture all the possible years. Like this:
<xsl:key name="yr" match="//Year/text()" use="." />
Then I for-each loop through the keys to get unique years. My ultimate goal is to sum the quantites like this:
<xsl:for-each select="//Year/text()[generate-id()=generate-id(key('yr',.)[1])]">
<li>
<xsl:variable name="varjobyear" select="."/>
<xsl:value-of select="$varjobyear"/> - sum -
<xsl:value-of select="sum(//unit[Year=$varjobyear]/Qty)"/>
</li>
</xsl:for-each>
For some reason my output looks like this:
2010 - sum - 0
2011 - sum - 0
2012 - sum - 0
But what I want is for those 0's to be the actual sum of the quantities for each year.
My best guess is that there is something wrong with the predicate [Year=$varjobyear], but I can't figure out what it is. I have also tried [Year=string($varjobyear)] and also [Year=number($varjobyear)].
What am I doing wrong? Thanks!
You want something like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kYearByVal" match="Year" use="."/>
<xsl:template match="Year[generate-id()=generate-id(key('kYearByVal',.)[1])]">
<li>
<xsl:value-of select="concat(., ' - sum - ', sum(key('kYearByVal',.)/../Qty))"/>
</li>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<units>
<unit>
<unitnum>12</unitnum>
<Year>1922</Year>
<Qty>3</Qty>
</unit>
<unit>
<unitnum>13</unitnum>
<Year>1922</Year>
<Qty>8</Qty>
</unit>
<unit>
<unitnum>14</unitnum>
<Year>1999</Year>
<Qty>5</Qty>
</unit>
<unit>
<unitnum>15</unitnum>
<Year>1999</Year>
<Qty>7</Qty>
</unit>
<unit>
<unitnum>16</unitnum>
<Year>2003</Year>
<Qty>3</Qty>
</unit>
</units>
the wanted, correct result is produced:
<li>1922 - sum - 11</li>
<li>1999 - sum - 12</li>
<li>2003 - sum - 3</li>