XML query error in SQL Server 2005 - sql

For updating a mass records, I used xml query. From the front end (C#.net) I populate xml and pass it to a stored procedure as a parameter (like #Master_rows_xml_Update).
I face a situation.. when I go for this command in the stored procedure
declare #i INTEGER,#Master_rows_xml_Update XML;
SET #Master_rows_xml_Update= '<root>
<row Id="1" FinYearId="5" EmployeeId="55" EnteredOn="7/1/2011 12:00:00 AM" EnteredBy="1" HouseRentPaid="False" HouseRentAmount="3500.00" officeId="9"/>
<row Id="2" FinYearId="5" EmployeeId="55" EnteredOn="7/1/2011 12:00:00 AM" EnteredBy="1" HouseRentPaid="False" HouseRentAmount="3500.00" officeId="9"/>
</root>';
exec sp_xml_preparedocument #i output, #Master_rows_xml_Update;
update EmployeeFinYearInvestment
set EmployeeFinYearInvestment.FinYearId = ox.FinYearId,
EmployeeFinYearInvestment.EmployeeId = ox.EmployeeId,
EmployeeFinYearInvestment.EnteredOn = ox.EnteredOn,
EmployeeFinYearInvestment.EnteredBy = ox.EnteredBy,
EmployeeFinYearInvestment.HouseRentPaid = ox.HouseRentPaid,
EmployeeFinYearInvestment.HouseRentAmount = ox.HouseRentAmount,
EmployeeFinYearInvestment.officeId=ox.officeId
from OpenXml(#i, 'root/row')
with (Id INT,FinYearId INT, EmployeeId INT,EnteredOn DATETIME,EnteredBy INT , HouseRentPaid BIT,HouseRentAmount DECIMAL ,officeId INT ) ox
where EmployeeFinYearInvestment.Id = ox.Id;
exec sp_xml_removedocument #i;
..IT CAN NOT UPDATE ACTION TO FOLLOWING TABLE...
Procedure also not returning any error...
ANY IDEA...
Thanks..
Anirban

I would try to get rid of OpenXml and those function - use the new XQuery/XPath stuff in SQL Server 2005 !
Try this - this should give you the values from your XML:
DECLARE #XmlUpdate XML
SET #XmlUpdate =
'<root>
<row Id="1" FinYearId="5" EmployeeId="55" EnteredOn="7/1/2011 12:00:00 AM" EnteredBy="1" HouseRentPaid="False" HouseRentAmount="3500.00" officeId="9"/>
<row Id="2" FinYearId="5" EmployeeId="55" EnteredOn="7/1/2011 12:00:00 AM" EnteredBy="1" HouseRentPaid="False" HouseRentAmount="3500.00" officeId="9"/>
</root>';
SELECT
UpdRow.value('(#Id)[1]', 'int') AS 'ID',
UpdRow.value('(#FinYearId)[1]', 'int') AS 'FinYearID',
UpdRow.value('(#EmployeeId)[1]', 'int') AS 'EmployeeID',
UpdRow.value('(#EnteredOn)[1]', 'datetime') AS 'EnteredOn',
UpdRow.value('(#EnteredBy)[1]', 'int') AS 'EnteredBy',
UpdRow.value('(#HouseRentPaid)[1]', 'bit') AS 'RentPaid',
UpdRow.value('(#HouseRentAmount)[1]', 'decimal(20,4)') AS 'RentAmount',
UpdRow.value('(#OfficeId)[1]', 'int') AS 'OfficeID'
FROM
#XmlUpdate.nodes('/root/row') AS R(UpdRow)
And of course, if you can select it, you can also use it to update!
UPDATE
dbo.EmployeeFinYearInvestment
SET
EmployeeFinYearInvestment.FinYearId = UpdRow.value('(#FinYearId)[1]', 'int'),
EmployeeFinYearInvestment.EmployeeId = UpdRow.value('(#EmployeeId)[1]', 'int'),
..... (and so on). ......
EmployeeFinYearInvestment.EnteredOn = ox.EnteredOn,
FROM
#XmlUpdate.nodes('/root/row') AS R(UpdRow)
WHERE
EmployeeFinYearInvestment.Id = UpdRow.value('(#Id)[1]', 'int');

Related

Extracting attributes from XML column in SQL Server 2012

I have a column called MetaXML, which has XML stored inside. I only want to extract information relating to the URLpanel tag. How do I access the node/element down to the URLPanel tag. I also want to separate out the Cap, CaptionText, Height, HeightType, ID and URL (all contained in URLPanel). Is that possible?
Note: I've removed the schema info from the first line...
<DASHBOARD>
<COLUMNS>
<COLUMN Width="19" WidthType="Percent">
<PANELS>
<ACTIONLISTPANEL Cap="8fdd05da-c6b9-41c3-9e4d-5b661c3e134a"
CaptionText="My Action List" Height="14" HeightType="Percent"
ID="CL_Front9.2_c0_r0" />
<PROCESSPANEL Cap="11e934f8-1aed-4fd3-ad95-42049343a390" CaptionText="My
Processes" Height="19" HeightType="Percent" ID="CL_Front9.2_c0_r1">
<APPOBJECTS>
<APPOBJECT AppObjectID="CL_WfMyMattersActList" ID="8a690a0f-268c-
4eb0-8536-6ac09605b7f8" Type="Process" />
<APPOBJECT AppObjectID="CL_ModifyTimeNarrative" ID="d2f320e9-2797-
4631-b63a-5bdcb241d633" Type="Process" />
<APPOBJECT AppObjectID="Invoice Inquiry" ID="7749f30c-acdf-4c09-
b841-60026e274284" Type="Dashboard" />
</APPOBJECTS>
</PROCESSPANEL>
<DASHBOARDENTRY Cap="13d4cd47-a3cf-4222-b03b-932bd8ff6850"
CaptionText="Inquiry" DBID="CliMattInquiry2" Height="50"
HeightType="Percent" ID="CL_Front9.2_c0_r2" />
<REPORTPANEL Cap="9596ced2-76d2-4ebb-a7a3-4f2bd2450fde" CaptionText="My
Details" Height="17" HeightType="Percent" ID="CL_Front9.2_c0_r3">
<VIEWS CurrentViewID="CL_TkprStaticUser">
<VIEW Cap="5f99bd4b-1509-4a65-a54c-07bbc444aceb" CaptionText="My
Details" ReportLayoutID="CL_TkprStaticUser" Type="Report" />
</VIEWS>
</REPORTPANEL>
</PANELS>
</COLUMN>
<COLUMN Width="77" WidthType="Percent">
<PANELS>
<URLPANEL Cap="de940bd4-80f8-47f4-b721-76eed63c650f" CaptionText="Fee
Earner Dashboard" Height="100" HeightType="Percent" ID="CL_Front9.2_c1_r0"
Url="http://ccdc1sql119/ReportServer_SQLR_FINDW_PRD1/Pages/ReportViewer.aspx?%2fDWR%2fMIP+Phase+3%2fMIP_Landing_Indiv&rs:Command=Render&rc:Parameters=Collapsed" />
</PANELS>
</COLUMN>
</COLUMNS>
<SHORTLIST />
</DASHBOARD>
The XML above is quite small, but I cannot seem to extract the URLPanel tag. I've created the following SQL to try to extract the URLPanel tag:
DECLARE #DataID UNIQUEIDENTIFIER
SET #DataID = 'AEFFF874-5980-4340-92C1-ED6B292AA667'
BEGIN
SET NOCOUNT ON;
IF OBJECT_ID('tempdb..#TempXML') IS NOT NULL
DROP TABLE #TempXML
DECLARE #X XML
SET #X = (SELECT D.MetaXML AS 'MetaXML'
FROM NxFWKAppObjectData AS D
WHERE NxFWKAppObjectDataID = #DataID)
DECLARE #iX INT
EXEC sp_xml_preparedocument #ix output, #x
CREATE TABLE #TempXML (URLPanel NVARCHAR(MAX))
INSERT INTO #TempXML(URLPanel)
SELECT
URLPanel
FROM
OPENXML(#iX, '//DASHBOARD/COLUMNS/COLUMN/PANELS/URLPANEL', 2)
WITH
(URLPanel NVARCHAR(MAX))
SELECT *
FROM #TempXML AS DT
EXEC sp_xml_removedocument #iX
DROP TABLE #TempXML
END
I would recommend to use the built-in native XQuery support (instead of the legacy OPENXML approach which has its problems with memory leaks etc.).
Given your #X variable - try this:
SELECT
Cap = XC.value('#Cap', 'varchar(50)'),
CaptionText = XC.value('#CaptionText', 'varchar(250)'),
Height = XC.value('#Height', 'int'),
HeightType = XC.value('#HeightType', 'varchar(50)'),
ID = XC.value('#ID', 'varchar(50)'),
Url = XC.value('#Url', 'varchar(500)')
FROM
#X.nodes('/DASHBOARD/COLUMNS/COLUMN/PANELS/URLPANEL') AS XT(XC)
This hopefully should return the bits of data you're looking for.

dc:creator from XML into SQL table

I am trying to store an XML file (Code below) but the the dc:creator is causing an error. I have found from other related questions on here stating that I should use ;WITH XMLNAMESPACES(''http://purl.org/dc/elements/1.1/'' AS dc) but this has not worked either any ideas on what might be the problem/solution ? .
XML file:
<?xml version="1.0" encoding="UTF-8"?>
-<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:base="http://talksport.com/rss/sports-news/football/feed" version="2.0">
-<channel>
<title>Football</title>
<link>http://talksport.com/rss/sports-news/football/feed</link>
<description/>
<language>en</language>
<atom:link type="application/rss+xml" rel="self" href="http://talksport.com/rss/sports-news/football/feed"/>
-<item>
<title>Hillsborough families 'back introduction of rail seating' as bereaved family says 'standing did not kill our 96'</title>
<link>http://talksport.com/football/hillsborough-families-back-introduction-rail-seating-bereaved-family-says-standing-did-not</link>
<description/>
<pubDate>Wed, 19 Jul 2017 08:18:37 +0000</pubDate>
<dc:creator>talkSPORT</dc:creator>
<guid isPermaLink="false">247276 at http://talksport.com</guid>
</item>
</rss>
This is the stored procedure:
CREATE PROCEDURE feed.usp_importXML(#file VARCHAR(8000))
AS
BEGIN
DECLARE #Query VARCHAR(8000)
SET #Query ='
DECLARE #xmlFile as XML
SET #xmlFile = ( cast
SELECT CONVERT(XML,BulkColumn) as BulkColumn
FROM OPENROWSET (BULK '''+#file+''', SINGLE_BLOB) AS t)
INSERT INTO feed.tempXML (title,link,source)
SELECT
title = t.value (''title[1]'', ''NVARCHAR(300)''),
link = t.value (''link[1]'', ''NVARCHAR(300)''),
source = t.value(''(dc:creator)[1]'',''NVARCHAR(30)'')
FROM #xmlFile.nodes(''/rss/channel/item'') AS xTable(t);'
EXEC(#Query)
END
GO
In your case it might be enough to replace the dc: with a wildcard: *:. Assuming your XML is written into an XML file already, you might try as such:
DECLARE #xmlFile XML=
N'<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:base="http://talksport.com/rss/sports-news/football/feed" version="2.0">
<channel>
<title>Football</title>
<link>http://talksport.com/rss/sports-news/football/feed</link>
<description />
<language>en</language>
<atom:link type="application/rss+xml" rel="self" href="http://talksport.com/rss/sports-news/football/feed" />
<item>
<title>Hillsborough families ''back introduction of rail seating'' as bereaved family says ''standing did not kill our 96''</title>
<link>http://talksport.com/football/hillsborough-families-back-introduction-rail-seating-bereaved-family-says-standing-did-not</link>
<description />
<pubDate>Wed, 19 Jul 2017 08:18:37 +0000</pubDate>
<dc:creator>talkSPORT</dc:creator>
<guid isPermaLink="false">247276 at http://talksport.com</guid>
</item>
</channel>
</rss>';
SELECT
title = t.value ('title[1]', 'NVARCHAR(300)'),
link = t.value ('link[1]', 'NVARCHAR(300)'),
source = t.value('(*:creator)[1]','NVARCHAR(30)')
FROM #XmlFile.nodes('/rss/channel/item') AS xTable(t);
This will work too:
WITH XMLNAMESPACES('http://purl.org/dc/elements/1.1/' AS dc)
SELECT
title = t.value ('title[1]', 'NVARCHAR(300)'),
link = t.value ('link[1]', 'NVARCHAR(300)'),
source = t.value('(dc:creator)[1]','NVARCHAR(30)')
FROM #XmlFile.nodes('/rss/channel/item') AS xTable(t);
And you can declare the namespace within .value too:
SELECT
title = t.value ('title[1]', 'NVARCHAR(300)'),
link = t.value ('link[1]', 'NVARCHAR(300)'),
source = t.value('declare namespace dc="http://purl.org/dc/elements/1.1/";(dc:creator)[1]','NVARCHAR(30)')
FROM #XmlFile.nodes('/rss/channel/item') AS xTable(t);

Converting XML node values to comma separated values in SQL

I am trying to convert XML node values to comma separated values but, getting a
Incorrect syntax near the keyword 'SELECT'.
error message
declare #dataCodes XML = '<Root>
<List Value="120" />
<List Value="110" />
</Root>';
DECLARE #ConcatString VARCHAR(MAX)
SELECT #ConcatString = COALESCE(#ConcatString + ', ', '') + Code FROM (SELECT T.Item.value('#Value[1]','VARCHAR(MAX)') as Code FROM #dataCodes.nodes('/Root/List') AS T(Item))
SELECT #ConcatString AS Result
GO
I tried to follow an article but not sure how to proceed further. Any suggestion is appreciated.
Expectation:
Comma separated values ('120,110') stored in a variable.
Try this;
DECLARE #dataCodes XML = '<Root>
<List Value="120" />
<List Value="110" />
</Root>';
DECLARE #ConcatString VARCHAR(MAX)
SELECT #ConcatString = COALESCE(#ConcatString + ', ', '') + Code
FROM (
SELECT T.Item.value('#Value[1]', 'VARCHAR(MAX)') AS Code
FROM #dataCodes.nodes('/Root/List') AS T(Item)
) as TBL
SELECT #ConcatString AS Result
GO
You just need to add an alias to your sub SQL query.
For future readers, XML data can be extracted into arrays, lists, vectors, and variables for output in comma separated values more fluidly using general purpose languages. Below are open-source solutions using OP's needs taking advantage of XPath.
Python
import lxml.etree as ET
xml = '<Root>\
<List Value="120" />\
<List Value="110" />\
</Root>'
dom = ET.fromstring(xml)
nodes = dom.xpath('//List/#Value')
data = [] # LIST
for elem in nodes:
data.append(elem)
print((", ").join(data))
120, 110
PHP
$xml = '<Root>
<List Value="120" />
<List Value="110" />
</Root>';
$dom = simplexml_load_string($xml);
$node = $dom->xpath('//List/#Value');
$data = []; # Array
foreach ($node as $n){
$data[] = $n;
}
echo implode(", ", $data);
120, 110
R
library(XML)
xml = '<Root>
<List Value="120" />
<List Value="110" />
</Root>'
doc<-xmlInternalTreeParse(xml)
data <- xpathSApply(doc, "//List", xmlGetAttr, 'Value') # LIST
print(paste(data, collapse = ', '))
120, 110
To do this without a variable, you can use the nodes method to convert the xml nodes into a table format with leading commas, then use FOR XML PATH('') to collapse it into a single line of XML, then wrap that in STUFF to convert it to varchar and strip off the initial leading comma:
DECLARE #dataCodes XML = '<Root>
<List Value="120" />
<List Value="110" />
</Root>';
SELECT STUFF(
(
SELECT ', ' + T.Item.value('#Value[1]', 'VARCHAR(MAX)')
FROM #dataCodes.nodes('/Root/List') AS T(Item)
FOR XML PATH('')
), 1, 2, '')

XQuery insert/update value

I want to update xml like this one
<?xml version="1.0" encoding="utf-16"?>
<Properties xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<BackupPath>MyLocalPath</BackupPath>
</Properties>
using stored procedure
declare #PropertyName nvarchar(500)
declare #PropertyValue nvarchar(MAX)
set #PropertyName = 'BackupPath'
set #PropertyValue = 'MyTestPath'
DECLARE #ExistingSettings XML;
DECLARE #NewSettings nvarchar(MAX);
SET #ExistingSettings = (SELECT TOP(1) CAST(SettingsDefinition AS XML) FROM ApplicationSettings)
SET #ExistingSettings.modify('replace value of (/Properties/*[local-name() = sql:variable("#PropertyName")]/text())[1] with sql:variable("#PropertyValue")')
UPDATE ApplicationSettings
SET SettingsDefinition = CAST(#ExistingSettings AS nvarchar(MAX)), LastUpdate = GetDate()
WHERE ID = (SELECT TOP(1) ID FROM ApplicationSettings)
and this works fine as long as 'BackupPath' contains value
<BackupPath>MyLocalPath</BackupPath>
How to modify this query to insert value (or validate if needed) if
<BackupPath />
Just don't require a text node in your XPath.
SET #ExistingSettings.modify(
'replace value of (/Properties/' + #PropertyName + ')[1] with sql:variable("#PropertyValue")'
)

Modify several xml attributes, based on a list

From a previous post:
SQL Server XML add attribute if non-existent
What I would like to do is be able to modify multiple tags. Below is the code that shows what I would like to do, but cannot, since I get the error: The argument 1 of the XML data type method "exist" must be a string literal. Is there a way to modify the XML using variables, rather than literals?
ALTER FUNCTION [dbo].[ConvertXmlData](#xmlData XML)
RETURNS XML
AS
BEGIN
DECLARE #tags TABLE (
ID INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
TAG VARCHAR(25)
)
INSERT INTO #tags
SELECT 'xxx' UNION
SELECT 'yyy'
DECLARE #counter INT
DECLARE #count INT
DECLARE #id INT
DECLARE #tag VARCHAR(25)
DECLARE #exist VARCHAR(100)
DECLARE #insert VARCHAR(100)
DECLARE #existX VARCHAR(100)
DECLARE #insertX VARCHAR(100)
SET #exist = 'descendant::{0}[not(#runat)]'
SET #insert = 'insert attribute runat { "server" } into descendant::{0}[not(#runat)][1]'
SET #counter = 1
SELECT #count = COUNT(*) FROM #tags
WHILE #counter <= #count BEGIN
SELECT #tag = TAG FROM #tags WHERE ID = #counter
SET #existX = REPLACE(#existX, '[0]', #tag)
WHILE #xmlData.exist(#existX) = 1 BEGIN
SET #xmlData.modify(REPLACE(#insertX, '[0]', #tag));
END
SET #counter = #counter + 1
END
RETURN #xmlData
END
You can't use a variable as an argument to the xml-functions but you can use variables (and table columns) in the literal expression.
I guess this does what you want. At least it should give you an idea of what you can do.
declare #xmlData xml
set #xmlData =
'<something>
<xxx id="1"/>
<xxx id="2" runat="server" />
<xxx id="3"/>
<yyy id="3" />
<zzz id="1"/>
</something>'
declare #tags table
(
id int identity(1,1) primary key,
tag varchar(25)
)
insert into #tags
select 'xxx' union
select 'yyy'
declare #tag varchar(25)
declare #id int
select top 1
#id = id,
#tag = tag
from #tags
order by id
while ##rowcount > 0
begin
while #xmlData.exist('descendant::*[local-name() = sql:variable("#tag") and not(#runat)]') = 1
begin
set #xmlData.modify('insert attribute runat { "server" } into descendant::*[local-name() = sql:variable("#tag") and not(#runat)][1]');
end
select top 1
#id = id,
#tag = tag
from #tags
where id > #id
order by id
end
select #xmlData
Result:
<something>
<xxx id="1" runat="server" />
<xxx id="2" runat="server" />
<xxx id="3" runat="server" />
<yyy id="3" runat="server" />
<zzz id="1" />
</something>