How to parse XML column in SQL Server 2012? - sql-server

I have never used XML parsing in SQL Server, I would like to extract the fields in its own column, get the correct data.
I have a column called CustomerHeaderUncompressed in a Customer table that looks something like below, how do I extract the fields and the data in SQL Server 2012?
<CustomerHeaderData>
<CustomerHeader>
<shippingmethod Value="00000000-0000-0000-0000-000000000000" Name="" />
<discount Value="" />
<customdiscount Value="0" />
<ponumber Value="9909933793" />
<tax1 Value="-1" />
<tax2 Value="-1" />
<tax3 Value="0" />
<tax3name Value="" />
<tax4 Value="0" />
<Freight />
<ClientExtraField6 Value="5" />
<ClientExtraField7 Value="3" />
<dateneeded Value="01/01/0001 00:00:00" />
<ClientTaxCodeSource>0</ClientTaxCodeSource>
<shippingbranch />
<dropnumber Value="" />
<comment Value="" />
<shippingzone Value="" />
<salespersonID Value="704e78d4-cdbb-4963-bcc2-2c83a1d5f3fd" />
<salesperson Value="Salesrep, XYZ" />
<installation Value="False" />
<salesterms Value="18" />
<HeldItemDeliveryMethod Value="0" />
<customcontrol>
<CustomCustomerHeader CultureInfo="en-US">
<BusinessSegment>TR</BusinessSegment>
<BusinessSegmentID>1</BusinessSegmentID>
<OrderType>2</OrderType>
<MarketSegment>S3</MarketSegment>
<CustomerDeliveryDate>2010-01-21</CustomerDeliveryDate>
<BuildingPermitNumber />
<FinalWallDepth />
<PricingType>2</PricingType>
<HouseBuiltBefore1978>False</HouseBuiltBefore1978>
<AttributePricing>False</AttributePricing>
<UndeterminedAttributes>False</UndeterminedAttributes>
<EventIDStatus>VerifyFailed</EventIDStatus>
<EventIDEnabled>False</EventIDEnabled>
<CustomerDiscount>0</CustomerDiscount>
<PreparedBy />
<RequestedShipDate>01/14/2010</RequestedShipDate>
<UserTestDate>01/01/0001</UserTestDate>
</CustomCustomerHeader>
</customcontrol>
</CustomerHeader>

Basically something like this:
select from your Customer table
use CROSS APPLY and the XQuery .nodes() function to grab the XML as a "on-the-fly" pseudo table of XML fragments (table alias XT, single column aliassed as XC)
"reach" into those XML fragments and pull out the values you need, using the .value() XQuery function; use element names as such, and attributes need to be prefixed with a # sign
Try this and extend it to your needs:
SELECT
ShippingMethodValue = XC.value('(shippingmethod/#Value)[1]', 'varchar(50)'),
ShippingMethodName = XC.value('(shippingmethod/#Name)[1]', 'varchar(50)'),
DiscountValue = XC.value('(discount/#Value)[1]', 'varchar(50)'),
CustomDiscountValue = XC.value('(customdiscount/#Value)[1]', 'varchar(50)'),
PONumber= XC.value('(ponumber/#Value)[1]', 'bigint' )
FROM
Customer
CROSS APPLY
CustomerHeaderUncompressed.nodes('/CustomerHeaderData/CustomerHeader') AS XT(XC)

Related

How to replace a tag with another in sql server XML file

I am working on a table in sql server which stores xml file in a column. In that xml file I am doing some changes. The XML file looks like:
<Report version=1>
<Title>
<Student>
<InputNumber type="int" min="0" max="100" name="age" description="Age
of student">
<Value>20</Value>
</InputNumber>
<InputNumber type="int" min="0" max="100" name="height"
description="height of student">
<Value>170</Value>
</InputNumber>
</Student>
</Title>
</Report>
I understand the usage of modify function for updating attributes or text present between tags as:
UPDATE student
SET dataxml.modify('replace value of (/Report/#version)[1] with "2"')
WHERE id=10
or
UPDATE student
SET dataxml.modify('replace value of (/Report/Title/Student/InputNumber[1]/Value[1]/text())[1] with "21"')
WHERE id=10
But now I want to replace entire tag with another tag i.e.
<InputNumber type="int" min="0" max="100" name="height"
description="height of student">
<Value>170</Value>
</InputNumber>
with
<InputText name="height"
description="height of student">
<Value>170 cm</Value>
</InputText>
I found something on internet like this and tried.
Update Student
set dataxml = replace(cast(dataxml as nvarchar(max)),'/Report/Title/Student/InputNumber[2]>','InputText>')
WHERE id=10
It says updated successfully. But I don't see the change in XML.
How can I do that?
First of all: Your XML is not valid. The attribute version=1 must be version="1".
Second: The verb tag is just one markup like <Student> or </Student>, but the whole node with attributes and nested sub-nodes is called node or - as a special type of node - element.
Now to your issue:
We need a declared table to simulate your issue:
DECLARE #student TABLE(ID INT IDENTITY, dataxml XML);
INSERT INTO #student VALUES
(N'<Report version="1">
<Title>
<Student>
<InputNumber type="int" min="0" max="100" name="age" description="Age of student">
<Value>20</Value>
</InputNumber>
<InputNumber type="int" min="0" max="100" name="height" description="height of student">
<Value>170</Value>
</InputNumber>
</Student>
</Title>
</Report>');
--This is the new element we want to insert (better: want to use to replace another)
DECLARE #newElement XML=
N'<InputText name="height" description="height of student">
<Value>170 cm</Value>
</InputText>';
--approach one calls `.modify()` twice:
UPDATE #student SET dataxml.modify('insert sql:variable("#newElement") after (/Report/Title/Student/InputNumber[#name="height"])[1]');
UPDATE #student SET dataxml.modify('delete (/Report/Title/Student/InputNumber[#name="height"])[1]');
SELECT * FROM #student;
--approach two uses FLWOR-XQuery
UPDATE #student SET dataxml=dataxml.query('<Report version="{/Report/#version}">
{<Title>
<Student>
{
for $elmt in /Report/Title/Student/*
return
if(local-name($elmt)="InputNumber" and $elmt[#name="height"]) then
<InputText name="height" description="height of student">
<Value>{$elmt/Value/text()} cm</Value>
</InputText>
else
$elmt
}
</Student>
</Title>}
</Report>');
Both ideas in short:
1) We insert the new element right after the one which should be replaced and remove it in a separate step.
2) We re-create the XML via XQuery by running through the inner list of nodes within <Student> and insert the new content instead of the existing node.

How to loop xml in store procedure

I have a store procedure that returns the below XML:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header />
<SOAP-ENV:Body>
<ns2:ReportResponse>
<ns2:responseTitle />
<ns2:responseBody>
<ns2:resultRow>
<ns2:result Name="country" Value="United Kingdom" />
<ns2:result Name="code" Value="7360" />
</ns2:resultRow>
<ns2:resultRow>
<ns2:result Name="country" Value="France" />
<ns2:result Name="code" Value="7340" />
</ns2:resultRow>
</ns2:responseBody>
</ns2:ReportResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I want to be able to save the 2 records in the table, how can I get a loop to get the data?
Record 1:
Country=United Kingdom
Code=7360
Record 2:
Country=France
Code=7340
I tried to use this select but it's not returning anything.
SELECT
Record.value('#Name','VARCHAR')
FROM #XmlResponse.nodes('/Envelope/Body/ReportResponse/responseBody/resultRow')AS TEMPTABLE(Record)
Thanks.
Like this:
declare #doc xml = '
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header />
<SOAP-ENV:Body>
<ns2:ReportResponse xmlns:ns2="http://whatever">
<ns2:responseTitle />
<ns2:responseBody>
<ns2:resultRow>
<ns2:result Name="country" Value="United Kingdom" />
<ns2:result Name="code" Value="7360" />
</ns2:resultRow>
<ns2:resultRow>
<ns2:result Name="country" Value="France" />
<ns2:result Name="code" Value="7340" />
</ns2:resultRow>
</ns2:responseBody>
</ns2:ReportResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
';
WITH XMLNAMESPACES ('http://schemas.xmlsoap.org/soap/envelope/' as soap ,
'http://whatever' as ns2)
SELECT
Record.value('(ns2:result[#Name="country"])[1]/#Value','VARCHAR(20)') Country,
Record.value('(ns2:result[#Name="code"])[1]/#Value','int') Code
FROM #doc.nodes('/soap:Envelope/soap:Body/ns2:ReportResponse/ns2:responseBody/ns2:resultRow')AS TEMPTABLE(Record)
outputs
Country Code
-------------------- -----------
United Kingdom 7360
France 7340
(2 rows affected)

Visual Studio: Updating 2 tables using a grid view

I want to have a gridview which contains Members.firstName, Members.LastName, Team.TeamName. Team.TeamName should be a dropdown containing the possible TeamName's.
My Tables:
Members
--------
MemberID BIGINT
FirstName NVARCHAR(50)
LastName NVARCHAR(50)
TeamID BIGINT
Team
---------
TeamID BIGINT
TeamName NVARCHAR(50)
My Select:
SELECT Members.FirstName, Members.LastName, Team.TeamName
FROM Members
INNER JOIN Team ON Members.TeamId = Team.TeamId
My Update:
CREATE PROCEDURE updateTeamMembers
(
#TeamId BIGINT,
#FirstName NVARCHAR(50),
#LastName NVARCHAR(50),
#TeamName NVARCHAR(50)
)
AS
BEGIN
UPDATE Members SET FirstName=#FirstName, LastName=#LastName WHERE TeamId=#TeamId
UPDATE Team SET TeamName=#TeamName WHERE TeamId=#TeamId
END
RETURN
I believe the error to be with the update procedure, I get this error:
Procedure or function updateTeamMembers has too many arguments specified.
My HTML5:
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConflictDetection="CompareAllValues" ConnectionString="<%$ ConnectionStrings:RegistrationConnectionString %>" DeleteCommand="DELETE FROM [Members] WHERE [MemberId] = #original_MemberId AND (([FirstName] = #original_FirstName) OR ([FirstName] IS NULL AND #original_FirstName IS NULL)) AND (([LastName] = #original_LastName) OR ([LastName] IS NULL AND #original_LastName IS NULL)) AND (([TeamId] = #original_TeamId) OR ([TeamId] IS NULL AND #original_TeamId IS NULL))" InsertCommand="INSERT INTO [Members] ([FirstName], [LastName], [TeamId]) VALUES (#FirstName, #LastName, #TeamId)" OldValuesParameterFormatString="original_{0}" SelectCommand="SELECT Members.FirstName, Members.LastName, Team.TeamName FROM Members INNER JOIN Team ON Members.TeamId = Team.TeamId" UpdateCommand="updateTeamMembers" UpdateCommandType="StoredProcedure">
<DeleteParameters>
<asp:Parameter Name="original_MemberId" Type="Int32" />
<asp:Parameter Name="original_FirstName" Type="String" />
<asp:Parameter Name="original_LastName" Type="String" />
<asp:Parameter Name="original_TeamId" Type="Int64" />
</DeleteParameters>
<InsertParameters>
<asp:Parameter Name="FirstName" Type="String" />
<asp:Parameter Name="LastName" Type="String" />
<asp:Parameter Name="TeamId" Type="Int64" />
</InsertParameters>
<UpdateParameters>
<asp:Parameter Name="TeamId" Type="Int64" />
<asp:Parameter Name="FirstName" Type="String" />
<asp:Parameter Name="LastName" Type="String" />
<asp:Parameter Name="TeamName" Type="String" />
</UpdateParameters>
</asp:SqlDataSource>
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" BackColor="White" BorderColor="#CCCCCC" BorderStyle="None" BorderWidth="1px" CellPadding="3" DataSourceID="SqlDataSource1" Height="217px" Width="596px">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="First Name" SortExpression="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="Last Name" SortExpression="LastName" />
<asp:BoundField DataField="TeamName" HeaderText="Team Name" SortExpression="TeamName" />
<asp:CommandField ShowEditButton="True" />
</Columns>
Update command when I configure through datasource (Doesn't update):
UPDATE [Members] SET [FirstName] = #FirstName, [LastName] = #LastName, [TeamId] = #TeamId WHERE [MemberId] = #original_MemberId AND (([FirstName] = #original_FirstName) OR ([FirstName] IS NULL AND #original_FirstName IS NULL)) AND (([LastName] = #original_LastName) OR ([LastName] IS NULL AND #original_LastName IS NULL)) AND (([TeamId] = #original_TeamId) OR ([TeamId] IS NULL AND #original_TeamId IS NULL))
When you execute the stored procedure, you specify the arguments comma separated, something like this:
EXEC updateTeamMembers #ParamValue1, #ParamValue2, #ParamValue3, #ParamValue4, #ParamValue5...
First of all, it's a good practice to specify the arguments as key value pair, where the key is the stored procedure parameter name and the value is the value you want to pass to that parameter. So, the above query will look like this:
EXEC updateTeamMembers
#TeamId = #ParamValue1,
#FirstName = #ParamValue2,
#LastName = #ParamValue3,
#TeamName = #ParamValue4
If you do this, will observe that you don't have a fifth parameter.
To help you more, you should paste the code from VS.

Get elements of same name from XML using XQuery in SQL server

I am having following XML
DECLARE #ruleXML XML
SET #RuleXML = '<questionnaire xmlns:xsi="http://schema1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schem2" title="Sample">
<sections>
<section id="24" title="Section Title" help="" url="">
<questions />
</section>
<section id="23" title="Information" help="" url="">
<questions />
</section>
<section id="25" title="Section Title1" help="" url="">
<questions>
<question id="3" title="Question Text">
<display-rules />
<questions />
</question>
<question id="4" title="Question Text" >
<response-set type="inline" />
<display-rules />
<questions />
</question>
</questions>
</section>
</sections>
</questionnaire>'
How to get a table with question id and title from all the question nodes regardless of their level using XQUERY in SQL server?
; with xmlnamespaces (default 'http://schem2')
select tbl.col1.value('#id', 'int')
, tbl.col1.value('#title', 'varchar(100)')
from #RuleXML.nodes('//question') tbl(col1)
Working example at SQL FIddle.

How to work in Crystal Report with Object Data Source?

How to work in Winforms Crystal Report with Object Data Source?
So far I have found web-links to use Crystal Report with DataSets or Databases directly.
Can anyone show me how to do it like this?
its the same as before...
reportDocument.SetDataSource(List1);
<CR:CrystalReportViewer ID="crvmyDataReport" runat="server"
ReportSourceID="crsmyData" EnableDatabaseLogonPrompt="False" DisplayGroupTree="False"
EnableParameterPrompt="False" ReuseParameterValuesOnRefresh="True"
BorderStyle="Solid" BorderColor="Black" BorderWidth="1px" />
<br />
<CR:CrystalReportSource ID="crsmyData" runat="server">
<Report FileName="myData.rpt">
<DataSources>
<CR:DataSourceRef DataSourceID="odsmyData" TableName="myData" />
</DataSources>
</Report>
</CR:CrystalReportSource>
<asp:ObjectDataSource ID="odsmyData" runat="server"
OldValuesParameterFormatString="original_{0}" SelectMethod="myDataQuery"
TypeName="myDataAppTableAdapters.myDataTableAdapter">
<SelectParameters>
<asp:ControlParameter ControlID="tb1" ConvertEmptyStringToNull="False"
DbType="String" Name="p1" PropertyName="Text" />
<asp:ControlParameter ControlID="tb2" ConvertEmptyStringToNull="False"
DbType="String" Name="p2" PropertyName="Text" />
<asp:ControlParameter ControlID="tb3" ConvertEmptyStringToNull="False"
DbType="String" Name="p3" PropertyName="Text" />
<asp:ControlParameter ControlID="ddl1" ConvertEmptyStringToNull="False"
DbType="String" Name="p4" PropertyName="SelectedValue" />
</SelectParameters>
</asp:ObjectDataSource>

Resources