Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 8 years ago.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Improve this question
My table structure is as follows:
date Id Value
--------------------------------
01/01/2005 50000 5
01/01/2006 50000 6
01/01/2007 50000 7
01/01/2005 50001 8
01/01/2006 50001 9
01/01/2007 50001 10
I would like to output the xml using for xml in SQL Server in the following format:
<Date date = "01/01/2005"> <Id dealId=50000" value="5"/> <Id dealId=50001" value="8"/> </Date> <Date date = "01/01/2006"> <Id dealId=50000" value="6"/> <Id dealId=50001" value="9"/> </Date>
It would be of great help if someone can help me out with the query in SQL Server. I have tried couple of them myself but I am not getting the exact output.
-- using your provided values:
DECLARE #table TABLE ([date] DATE, id INT, value INT);
INSERT #Table VALUES ('01/01/2005',50000,5);
INSERT #Table VALUES ('01/01/2006',50000,6);
INSERT #Table VALUES ('01/01/2007',50000,7);
INSERT #Table VALUES ('01/01/2005',50001,8);
INSERT #Table VALUES ('01/01/2006',50001,9);
INSERT #Table VALUES ('01/01/2007',50001,10);
-- XML Query
-- the outer query groups by date, ensuring each date only shows up once
SELECT
[date] AS '#date',
(
-- The inner query selects all Ids for each date found in the outer query
SELECT id as '#dealId',
value as '#value'
FROM #Table B
WHERE B.date=A.date -- join on date from outer query
FOR XML PATH ('Id'), TYPE
)
FROM #Table A
GROUP BY date
FOR XML PATH ('Date');
-- Produces:
<Date date="2005-01-01">
<Id dealId="50000" value="5"/>
<Id dealId="50001" value="8"/>
</Date>
<Date date="2006-01-01">
<Id dealId="50000" value="6"/>
<Id dealId="50001" value="9"/>
</Date>
<Date date="2007-01-01">
<Id dealId="50000" value="7"/>
<Id dealId="50001" value="10"/>
</Date>
Usually, if I need such a simple xml transformation, I'm using for xml raw:
select
t1.[date],
(
select
t2.[Id] as dealId, t2.[Value]
from Table1 as t2
where t2.[date] = t1.[date]
for xml raw('Id'), type
)
from Table1 as t1
group by t1.[date]
for xml raw('Date')
----------------------
<Date date="2005-01-01">
<Id dealId="50000" Value="5"/>
<Id dealId="50001" Value="8"/>
</Date>
<Date date="2006-01-01">
<Id dealId="50000" Value="6"/>
<Id dealId="50001" Value="9"/>
</Date>
<Date date="2007-01-01">
<Id dealId="50000" Value="7"/>
<Id dealId="50001" Value="10"/>
</Date>
Try it yourself in the sql fiddle demo
Some notes:
You don't have to use # in column names, because for xml raw is attribute-centric by default.
Subquery here is necessary, because you want nested xml and for now it's the only way to get it.
You also have to use type modifier in the subquery to get inner data as xml.
Related
Here is my XML:
<Triggers>
<Trigger>
<Name>DrugName</Name>
<Values>
<Value>Meclofenamate</Value>
<Value>Meloxicam</Value>
<Value>Vimovo</Value>
<Value>Nabumetone</Value>
<Value>Qmiiz</Value>
<Value>Tolmetin</Value>
</Values>
</Trigger>
<Trigger>
<Name>State</Name>
<Values>
<Value>MI</Value>
</Values>
</Trigger>
<Trigger>
<Name>BenefitType</Name>
<Values>
<Value>Pharmacy</Value>
</Values>
</Trigger>
<Trigger>
<Name>LineOfBusiness</Name>
<Values>
<Value>Medicaid</Value>
</Values>
</Trigger>
</Triggers>
My goal is to get output that looks like this:
ID DrugName State BenefitType LineOfBusiness
6500 Meclofenamate MI Pharmacy Medicaid
6501 Meloxicam MI Pharmacy Medicaid
6502 Vimovo MI Pharmacy Medicaid
6503 Nabumetone MI Pharmacy Medicaid
6504 Qmiiz MI Pharmacy Medicaid
6505 Tolmetin MI Pharmacy Medicaid
I can't find any examples on stackoverflow after extensive searches where XML is organized this way, and the examples I have found, tweaked and applied result in my getting a list of all the Values in one column (State values, BenefitType values, etc. mixed in with DrugName values).
The ID column is not part of the XML, but I need to have that in my output.
Here what the table looks like that has the XML column.
You needs the .nodes XML function to break out the Trigger nodes, then again for Values rows.
To get the value of a node instead of it's name, we use text().
To verify we are grabbing the right Trigger node for each column, we use the [] predicate to check (a bit like a where).
.value requires a single value, so we use [1] to get the first node.
SELECT
DrugName = drugs.DrugName.value('text()[1]','nvarchar(100)'),
State = tr.Trigg.value('Trigger[Name/text()="State"][1]/Values[1]/Value[1]/text()[1]', 'nvarchar(100)'),
BenefitType = tr.Trigg.value('Trigger[Name/text()="BenefitType"][1]/Values[1]/Value[1]/text()[1]', 'nvarchar(100)'),
LineOfBusiness = tr.Trigg.value('Trigger[Name/text()="LineOfBusiness"][1]/Values[1]/Value[1]/text()[1]', 'nvarchar(100)')
FROM #xml.nodes('/Triggers') tr(Trigg)
OUTER APPLY tr.Trigg.nodes('Trigger[Name/text()="DrugName"][1]/Values/Value') drugs(DrugName)
I have the table "client" with:
id name registered_on status
-- ------- ------------- ------
1 Alice 2020-03-04 a
2 Vincent 2020-03-05 p
3 Anne 2020-03-06 a
And the table "account" with:
client_id account_number type balance
--------- -------------- ---- -------
1 300-1 CHK 100
2 307-5 SAV 24
2 307-6 CHK 350
I created them in DB Fiddle (for a similar question I asked before about producing JSON).
Now, I need a SQL query to produce the 1:n XML document:
<client id="1" name="Alice" registered_on="2020-03-04" status="a">
<account account_number="300-1" type="CHK" balance="100" />
</client>
<client id="2" name="Vincent" registered_on="2020-03-05" status="p">
<account account_number="307-5" type="SAV" balance="24" />
<account account_number="307-6" type="CHK" balance="350" />
</client>
<client id="3" name="Anne" registered_on="2020-03-06" status="a" />
There's a 1:n relationship between the tables and some clients may not have an account (such as "Anne"). The result is a simple join (probably an outer join) that I know how to do. I just don't get how to produce a XML document from it.
If it's makes it easier/shorter I'm open to an alternative XML result, as long as it represents the same data; using tags, instead of attributes, for example.
After trying a bunch of options I was able to find the answer(s).
Original Format: With Attributes
It's possible to produce the XML result using an outer join:
select
xmlserialize(content -- remove this line to keep as XML instead of VARCHAR
xmlagg(r)
as text) -- remove this line to keep as XML instead of VARCHAR
from (
select
xmlelement(name client,
xmlattributes(c.id, c.name, c.registered_on, c.status),
case when count(a.client_id) > 0 then
xmlagg(xmlelement(name account,
xmlattributes(a.account_number, a.type, a.balance) ))
end
) as r
from client c
left join account a on a.client_id = c.id
group by c.id
) s
Or using subqueries (shorter but less performant):
select
xmlserialize(content -- remove this line to keep as XML instead of VARCHAR
xmlagg(
xmlelement(name client, xmlattributes(id, name, registered_on, status),
( select xmlagg(xmlelement(name account,
xmlattributes(a.account_number, a.type, a.balance)
)) from account a where a.client_id = c.id
)
))
as text) -- remove this line to keep as XML instead of VARCHAR
from client c;
Result:
<client id="1" name="Alice" registered_on="2020-03-04" status="a">
<account account_number="300-1" type="CHK" balance="100.00" />
</client>
<client id="2" name="Vincent" registered_on="2020-03-05" status="p">
<account account_number="307-5" type="SAV" balance="24.00" />
<account account_number="307-6" type="CHK" balance="350.00" />
</client>
<client id="3" name="Anne" registered_on="2020-03-06" status="a" />
Alternative Format: Without Attributes
Some people prefer to avoid attributes altogether and always use tags. That can also be done, using:
select
xmlserialize(content -- remove this line to keep as XML instead of VARCHAR
xmlagg(xmlelement(name client,
xmlforest(id, name, registered_on, status),
( select xmlagg(xmlelement(name account,
xmlforest(a.account_number, a.type, a.balance)))
from account a where a.client_id = c.id
)
))
as text) -- remove this line to keep as XML instead of VARCHAR
from client c;
Result:
<client>
<id>1</id>
<name>Alice</name>
<registered_on>2020-03-04</registered_on>
<status>a</status>
<account>
<account_number>300-1</account_number>
<type>CHK</type>
<balance>100.00</balance>
</account>
</client>
<client>
<id>2</id>
<name>Vincent</name>
<registered_on>2020-03-05</registered_on>
<status>p</status>
<account>
<account_number>307-5</account_number>
<type>SAV</type>
<balance>24.00</balance>
</account>
<account>
<account_number>307-6</account_number>
<type>CHK</type>
<balance>350.00</balance>
</account>
</client>
<client>
<id>3</id>
<name>Anne</name>
<registered_on>2020-03-06</registered_on>
<status>a</status>
</client>
I'm trying to get the value of the node from XML. Here is a short sample of the XML
<cpCollection moduleId="cc5005f4-f1ea-433e-b187-8b769170eae4" dataId="0a0e2ddf-2a38-4739-9a52-000f9698978f">
<group id="Serialize" name="Serialize">
<property id="Headline">
<value>One, Two, Three</value>
</property>
<property id="Credit">
<value>0.25</value>
</property>
</group>
</cpCollection>
Some of my query is below:
select TOP 1000 I.Attributes.value('#id', 'nvarchar(32)') as item_name,
F.X.value('#id', 'nvarchar(32)') as field_id,
F.X.value('data(.)', 'nvarchar(256)') as field_value,
F.X.value('Deck[1]','NVarChar(512)') AS Deck,
F.X.value('Credit[1]', 'Nvarchar(8)') As Credit
from cpsys_DataCurrent as T
cross apply T.Attributes.nodes('/cpCollection/group') as I(attributes)
cross apply I.attributes.nodes('property') as F(X)
I am not getting the value for Headline or Credit. Just NULL values.
In XPath /cpCollection/group/property[#id='Credit'] returns
<property id="Credit">
<value>0.25</value>
</property>
Hence, try it in this way
F.X.value('[#id="Credit"]', 'Nvarchar(8)') As Credit
ZLK's answer worked.
F.X.value('(.[#id="Credit"]/value/text())[1]','nvarchar(8)') As Credit
DECLARE #cpsys_DataCurrent TABLE ( xmltext XML);
INSERT INTO #cpsys_DataCurrent (xmltext)
VALUES
( N'<cpCollection moduleId="cc5005f4-f1ea-433e-b187-8b769170eae4" dataId="0a0e2ddf-2a38-4739-9a52-000f9698978f">
<group id="Serialize" name="Serialize">
<property id="Headline">
<value>One, Two, Three</value>
</property>
<property id="Credit">
<value>0.25</value>
</property>
</group>
</cpCollection>');
SELECT TOP 1000
T.xmltext.value('(cpCollection/#dataId)[1]', 'nvarchar(32)') as item_name,
T.xmltext.value('(cpCollection/group/#id)[1]', 'nvarchar(32)') as field_id,
T.xmltext.value('data(cpCollection/group/property)[1]', 'nvarchar(256)') as field_value,
T.xmltext.value('(cpCollection/group/property[#id="Headline"]/value)[1]','NVarChar(512)') AS Deck,
T.xmltext.value('(cpCollection/group/property[#id="Credit"]/value)[1]', 'Nvarchar(8)') As Credit
from #cpsys_DataCurrent as T
Result:
item_name field_id field_value Deck Credit
0a0e2ddf-2a38-4739-9a52-000f9698 Serialize One, Two, Three One, Two, Three 0.25
I have a table called customer, and I have the columns id, sortcode, and name.
I have one more table called tblxml, it has one column called xmlData of type CLOB and it stores the XML data.
I have stored XML data as follows:
<root>
<nd>
<id>1</id>
<sc>001</sc>
</nd>
<nd>
<id>2</id>
<sc>001001</sc>
</nd>
<nd>
<id>11</id>
<sc>001001001</sc>
</nd>
<nd>
<id>16</id>
<sc>001001001001</sc>
</nd>
<nd>
<id>13</id>
<sc>001001002</sc>
</nd>
<nd>
<id>9</id><sc>001002</sc>
</nd>
<nd>
<id>14</id>
<sc>001002001</sc>
</nd>
</root>
I have aligned it so it is easy to understand.
I need to:
extract this XML data from tblxml and update it in customer
table.
extract Id from tblxml table and update the sortcode in customer table which matches to id.
loop the XML data in update the sortcode according to id.
I have used <nd> to separate the rows.
As I'm new to Oracle, I haven't tried coding for this. Expecting some suggestions.
Sounds like XMLTABLE is what you're after:
with sample_data as (select '<root><nd>
<id>1</id>
<sc>001</sc>
</nd>
<nd>
<id>2</id>
<sc>001001</sc>
</nd>
<nd>
<id>11</id>
<sc>001001001</sc>
</nd>
<nd>
<id>16</id>
<sc>001001001001</sc>
</nd>
<nd>
<id>13</id>
<sc>001001002</sc>
</nd>
<nd>
<id>9</id><sc>001002</sc>
</nd>
<nd>
<id>14</id>
<sc>001002001</sc>
</nd></root>' xdata from dual)
-- end of mimicking a table called "sample_data" containing your xml data. See the main SQL below:
select id,
sc
from sample_data sd
cross join xmltable('/root/nd'
passing xmltype(sd.xdata)
columns id number path 'id',
sc varchar2(10) path 'sc');
ID SC
---------- ----------
1 001
2 001001
11 001001001
16 0010010010
13 001001002
9 001002
14 001002001
Once you have the query to pull the results from the xml column, you can then use that as part of an update or merge statement to do the necessary updating. This is left as an exercise for you to do - there are plenty of examples in StackOverflow and Google! If you get stuck, feel free to update your question with what you've tried.
I have solved this problem.
Created new Temporary table called TMP_XML it has one column as hold_xml
Inserting xml content from tblxml to TMP_XML table
Then extracting value from TMP_XML as follows and updating sortcode based on matched Id.
Insert InTo TMP_XML
Select XmlType(PkgCBDmlXml.ExtractNodeValues(xmlData, 'root'))
From tblxml;
update customer
set sortcode = ( Select ExtractValue(Value(E),'//sc') Sc_New
From TMP_XML A,
TABLE(XmlSequence(Extract(A.hold_xml, '//nd'))) E
Where ExtractValue(Value(E),'//id') = id
)
The scope of this project is much larger than this one question. I've been tasked with a project and I'll not bore you with the intimate details. Ultimately what I'm needing to do is get the data out of the database and into XML so I can convert to JSON and create a simple web app that will allow me to parse and format the data in way that will meet the customer's needs.
I'm sure there's a better way to do this, but this is the path I've settled on..
I have about 46,000 records dumped into a Temp Table. To Give you an idea of how this data is structured running the following query:
SELECT
TransactionID,
OwnerID,
Date,
TransactionType,
ChargeCode,
Description,
DebitAmount
FROM #OwnerHistoryTemp
WHERE OwnerID = '11111111'
Returns this:
TransactionID OwnerID Date TransactionType ChargeCode Description DebitAmount
28727 11111111 2014-12-01 E A1 APPLY CHARGES 210.00
28728 11111111 2014-12-03 C A1 DB11111111 210.00
28729 11111111 2015-01-01 E A1 APPLY CHARGES 183.37
What I'm looking to do here is use the SQL FOR XML PATH (open to any other suggestions) to output the data like so:
<OwnerHistory>
<OwnerID OwnerID="11111111">
<Transactions>
<TransactionID ID="28727">
<Date>2014-12-01</Date>
<TransactionType>E</TransactionType>
<ChargeCode>A1</ChargeCode>
<Description>APPLY CHARGES</Description>
<DebitAmount>210.00</DebitAmount>
</TransactionID>
<TransactionID ID="28728">
<Date>2014-12-03</Date>
<TransactionType>C</TransactionType>
<ChargeCode>A1</ChargeCode>
<Description>DB11111111</Description>
<DebitAmount>210.00</DebitAmount>
</TransactionID>
<TransactionID ID="28729">
<Date>2015-1-01</Date>
<TransactionType>E</TransactionType>
<ChargeCode>A1</ChargeCode>
<Description>APPLY CHARGES</Description>
<DebitAmount>183.37</DebitAmount>
</TransactionID>
</Transactions>
</OwnerID>
</OwnerHistory>
The Query I have, gets me close, but not QUITE there. Because the same OwnerID appears multiple times (once for each TransactionID), running the following query:
SELECT
OwnerID AS "#OwnerID",
TransactionID AS "Transaction/#RecordID",
Date AS "Transaction/Date",
TransactionType AS "Transaction/TransactionType",
ChargeCode AS "Transaction/ChargeCode",
Description AS "Transaction/Description",
DebitAmount AS "Transaction/DebitAmount"
FROM #OwnerHistoryTemp
WHERE OwnerID = '11111111'
GROUP BY OwnerID, RecordID, Date, ChargeCode, Description, DebitAmount
order by OwnerID
FOR XML PATH ('OwnerID'), ROOT('OwnerHistory')
Returns the folowing:
<OwnerHistory>
<OwnerID OwnerID="11111111">
<Transaction RecordID="28727">
<Date>2014-12-01</Date>
<TransactionType>E</TransactionType>
<ChargeCode>A1</ChargeCode>
<Description>APPLY CHARGES</Description>
<DebitAmount>210.0000</DebitAmount>
</Transaction>
</OwnerID>
<OwnerID OwnerID="11111111">
<Transaction RecordID="28728">
<Date>2014-12-03</Date>
<TransactionType>C</TransactionType>
<ChargeCode>A1</ChargeCode>
<Description>DB11111111</Description>
<DebitAmount>210.0000</DebitAmount>
</Transaction>
</OwnerID>
<OwnerID OwnerID="11111111">
<Transaction RecordID="28729">
<Date>2015-01-01</Date>
<TransactionType>E</TransactionType>
<ChargeCode>A1</ChargeCode>
<Description>APPLY CHARGES</Description>
<DebitAmount>183.3700</DebitAmount>
</Transaction>
</OwnerID>
</OwnerHistory>
Any thoughts on how to only pull the OwnerID one time as the Parent and group all transactions underneath it?
Might be something simple I'm just not understanding, or it might be impossible. Feel free to publicly cane me if the former is the case...
Ready for your caning?
Nest your queries to get nested XML, something like this:
SELECT TOP 1
OwnerID AS "#ID",
(SELECT
TransactionID AS "Transaction/#ID",
[Date] AS "Transaction/Date",
TransactionType AS "Transaction/Type",
ChargeCode AS "Transaction/ChargeCode",
[Description] AS "Transaction/Description",
DebitAmount AS "Transaction/DebitAmount"
FROM OwnerHistory
WHERE OwnerID = [Owner].OwnerID
FOR XML PATH(''), TYPE) Transactions
FROM OwnerHistory [Owner]
WHERE OwnerID = '11111111'
FOR XML PATH('Owner'), ROOT('OwnerHistory'), TYPE
Resulting XML:
<OwnerHistory>
<Owner ID="11111111">
<Transactions>
<Transaction ID="28727">
<Date>2015-03-26</Date>
<Type>E</Type>
<ChargeCode>A1</ChargeCode>
<Description>APPLY CHARGES</Description>
<DebitAmount>210.0000</DebitAmount>
</Transaction>
<Transaction ID="28728">
<Date>2015-03-26</Date>
<Type>C</Type>
<ChargeCode>A1</ChargeCode>
<Description>DB11111111</Description>
<DebitAmount>210.0000</DebitAmount>
</Transaction>
<Transaction ID="28729">
<Date>2015-03-26</Date>
<Type>E</Type>
<ChargeCode>A1</ChargeCode>
<Description>APPLY CHARGES</Description>
<DebitAmount>183.3700</DebitAmount>
</Transaction>
</Transactions>
</Owner>
</OwnerHistory>
Note that the TOP 1 is only included to avoid repeating the entire set of transactions for each row containing the OwnerID. This could be handled in a number of ways; normally this sort of nesting would be the result of a join on two normalized tables so that only one instance of Owner would occur.
MSDN has some good examples to demonstrate this technique.