I have a table which has several columns (Int, Bool) as nullable. I have one stored procedure which is taking XML as the input parameter. I'm trying to pass null values of some of these columns, But its inserting 0 instead of null.
declare #temp XML;
set #temp = '
<ArrayOfTestFileEntity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TestFileEntity>
<TestId xsi:nil="true" />
<MainTestNo xsi:nil="true" />
<TestCode xsi:nil="true" />
<TestCode1 />
<FlgTemp xsi:nil="true" />
</TestFileEntity>
</ArrayOfTestFileEntity>'
declare #xmlInput as XML = #temp;
declare #xmlOutput as XML;
BEGIN
SET NOCOUNT ON;
DECLARE #insertedTable table(Id int);
MERGE INTO Test AS Trg USING (
SELECT
d.x.value('TestId[1]', 'int') AS TestId,
d.x.value('MainTestNo[1]', 'int') AS MainTestNo,
d.x.value('TestCode[1]', 'int') AS TestCode,
d.x.value('TestCode1[1]', 'int') AS TestCode1,
d.x.value('FlgTemp[1]', 'bit') AS FlgTemp
FROM
#xmlInput.nodes('/ArrayOfTestFileEntity/TestFileEntity') AS d(x)
) AS Src ON Trg.Id = Src.Id
WHEN Matched THEN
UPDATE SET
Trg.TestId = Src.TestId,
Trg.MainTestNo = Src.MainTestNo,
Trg.TestCode = Src.TestCode,
Trg.TestCode1 = Src.TestCode1,
Trg.FlgTemp = Src.FlgTemp,
WHEN NOT matched BY TARGET THEN
INSERT
([TestId]
,[MainTestNo]
,[TestCode]
,[TestCode1]
,[FlgTemp])
VALUES
(Src.TestId,
Src.MainTestNo,
Src.TestCode,
Src.TestCode1,
Src.FlgTemp)
OUTPUT INSERTED.Id INTO #insertedTable;
set #xmlOutput = (SELECT * FROM #insertedTable for XML AUTO, ROOT('RowsUpserted'));
select #xmlOutput;
END
Basically you need to use [not(#xsi:nil = "true")] when you select value from xml.
So i have applied it in your query. So if you observed the xml you have noticed that i have added value 16 for <TestCode1> tag and keep rest of the tag as it is.
declare #temp XML;
set #temp = '
<ArrayOfTestFileEntity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TestFileEntity>
<TestId xsi:nil="true" />
<MainTestNo xsi:nil="true" />
<TestCode xsi:nil="true" />
<TestCode1>16</TestCode1>
<FlgTemp xsi:nil="true" />
</TestFileEntity>
</ArrayOfTestFileEntity>'
DECLARE #xmlInput as XML = #temp;
--MERGE INTO Test AS Trg USING (
SELECT
d.x.value('TestId[1][not(#xsi:nil = "true")]', 'int') AS TestId,
d.x.value('MainTestNo[1][not(#xsi:nil = "true")]', 'int') AS MainTestNo,
d.x.value('TestCode[1][not(#xsi:nil = "true")]', 'int') AS TestCode,
d.x.value('TestCode1[1][not(#xsi:nil = "true")]', 'int') AS TestCode1,
d.x.value('FlgTemp[1][not(#xsi:nil = "true")]', 'bit') AS FlgTemp
FROM
#xmlInput.nodes('/ArrayOfTestFileEntity/TestFileEntity') AS d(x)
Related
I'm having an issue to create XML nodes. Help is much appreciated !
This is a sample code
declare #tbl as table
(
employeeName nvarchar(50),
payFrequency nvarchar(50)
)
insert into #tbl
select 'John', 'Monthly'
union
select 'Carl', 'Biweekly'
select
employeeName AS 'Company/Employee',
payFrequency AS 'Company/PayFrequency'
from #tbl
for xml path ('employees'), root('paySchedule')
above code creates this output:
<paySchedule>
<employees>
<Company>
<Employee>John</Employee>
<PayFrequency>Monthly</PayFrequency>
</Company>
</employees>
<employees>
<Company>
<Employee>Carl</Employee>
<PayFrequency>Biweekly</PayFrequency>
</Company>
</employees>
</paySchedule>
I want to get the "paymentFrequency" values as a node. Is there a way to do this?
<paySchedule>
<employees>
<Company>
<Employee>John</Employee>
<PayFrequency>
<Monthly/>
</PayFrequency>
</Company>
</employees>
<employees>
<Company>
<Employee>Carl</Employee>
<PayFrequency>
<Biweekly/>
</PayFrequency>
</Company>
</employees>
</paySchedule>
You can use a CASE conditional for each possibility, returning an empty string when you want that node, and null otherwise.
SELECT
t.employeeName AS [Company/Employee],
CASE WHEN t.payFrequency = 'Monthly' THEN '' END AS [Company/PayFrequency/Monthly],
CASE WHEN t.payFrequency = 'Biweekly' THEN '' END AS [Company/PayFrequency/Biweekly]
FROM #tbl t
FOR XML PATH('employees'), ROOT('paySchedule'), TYPE;
You can do this also in a nested FOR XML.
SELECT
t.employeeName AS [Company/Employee],
(
SELECT
CASE WHEN t.payFrequency = 'Monthly' THEN '' END AS Monthly,
CASE WHEN t.payFrequency = 'Biweekly' THEN '' END AS Biweekly
FOR XML PATH(''), TYPE
) AS [Company/PayFrequency]
FROM #tbl t
FOR XML PATH('employees'), ROOT('paySchedule'), TYPE;
db<>fiddle
Note that <Monthly></Monthly> and <Monthly /> are semantically equivalent.
Please try the following solution.
It is using XQuery's FLWOR expression to compose the desired XML.
SQL
-- DDL and sample data population, start
DECLARE #tbl as table (employeeName NVARCHAR(50), payFrequency NVARCHAR(50));
INSERT INTO #tbl VALUES
('John', 'Monthly'),
('Carl', 'Biweekly');
-- DDL and sample data population, end
SELECT (
SELECT * FROM #tbl
FOR XML PATH('r'), TYPE, ROOT('root')
).query('<paySchedule>
{
for $r in /root/r
return <employees>
<Company>
<Employee>{data($r/employeeName)}</Employee>
<PayFrequency>
{
if ($r/payFrequency/text()="Monthly") then <Monthly/>
else <Biweekly/>
}
</PayFrequency>
</Company>
</employees>
}
</paySchedule>');
Output
<paySchedule>
<employees>
<Company>
<Employee>John</Employee>
<PayFrequency>
<Monthly />
</PayFrequency>
</Company>
</employees>
<employees>
<Company>
<Employee>Carl</Employee>
<PayFrequency>
<Biweekly />
</PayFrequency>
</Company>
</employees>
</paySchedule>
Follow-up of this question
I have a table with with column, that contains XML. This XML values can have different roots, for example:
<MyAuthenticationParams xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AlsoParams>
<SecretKey>MVHXAQA5kF4Ab9siV4vPA4aVPn1EKhbqIBrpCZx2Hg</SecretKey> <DynamicDescriptor />
</AlsoParams>
<myParams>
<AccountName>Acc1</AccountName>
<Username>testUsername</Username>
</myParams>
</MyAuthenticationParams>
or
<Sm1AuthenticationParams xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<AccountName>XGwzJ6RR</AccountName>
<SomeNumber>123456780</SomeNumber>
</Sm1AuthenticationParams>
How can I select records with root MyAuthenticationParams only?
This is what you need?
DECLARE #xml1 varchar(8000) = '<MyAuthenticationParams xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AlsoParams>
<SecretKey>MVHXAQA5kF4Ab9siV4vPA4aVPn1EKhbqIBrpCZx2Hg</SecretKey>
<DynamicDescriptor />
</AlsoParams>
<myParams>
<AccountName>Acc1</AccountName>
<Username>testUsername</Username>
</myParams>
</MyAuthenticationParams>';
DECLARE #xml2 varchar(8000) = '<Sm1AuthenticationParams xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<AccountName>XGwzJ6RR</AccountName>
<SomeNumber>123456780</SomeNumber>
</Sm1AuthenticationParams>';
DECLARE #temp TABLE (IsProper bit, XmlData varchar(8000));
INSERT #temp VALUES (1, #xml1), (0, #xml2);
SELECT *
FROM #temp
WHERE cast(XmlData AS xml).value('count(/MyAuthenticationParams/*)', 'int') > 0
Output:
IsProper XmlData
-------- ---------------------------
1 <MyAuthenticationParams ...
It may help you:
declare #xml xml ='<MyAuthenticationParams xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AlsoParams>
<SecretKey>MVHXAQA5kF4Ab9siV4vPA4aVPn1EKhbqIBrpCZx2Hg</SecretKey> <DynamicDescriptor />
</AlsoParams>
<myParams>
<AccountName>Acc1</AccountName>
<Username>testUsername</Username>
</myParams>
</MyAuthenticationParams>'
select
a.b.query('AlsoParams/SecretKey').value('.','nvarchar(max)')SecretKey,
a.b.query('myParams/AccountName').value('.','nvarchar(max)')AccountName
from #xml.nodes('MyAuthenticationParams')as a(b)
Or else if xml is stored in colum then try this
select
a.b.query('AlsoParams/SecretKey').value('.','nvarchar(max)')SecretKey,
a.b.query('myParams/AccountName').value('.','nvarchar(max)')AccountName
from yourtable cross apply xmlcolumn.nodes('MyAuthenticationParams')as a(b)
In the following code, I want to check if column is a Number. If it is - fill it with leading zeros.
Is there anyway using XML XQuery to check the original data type (int)
of the column?
declare #T table (string nchar(10), id int)
insert #T
select 'test1', 1
insert #T
select 'test2', 2
declare #X xml
SET ANSI_WARNINGS ON
set #X = (select * from #T order by id for xml path('row'), root('root'))
SELECT (
STUFF(
(
SELECT ';' + v.value('.','nvarchar(max)')
FROM r.nodes('*') AS B(v)
FOR XML PATH('')
),1,1,'')
) as [OUTPUT]
FROM #x.nodes('/root/row') AS A(r)
As you know your table you should not use a generical approach, if you do not have a good reason to do so.
What you probably should do is this
Include the formatted value into your XML. This allows you, to carry both information within the structure: typed and formatted.
declare #T table (string nvarchar(10), id int)
insert #T values
('test1', 1)
,('test2', 22)
declare #x xml = (select string
,REPLACE(STR(id,8),' ','0') AS [id/#formatted]
,id
from #T
order by id for xml path('row'), root('root'),TYPE)
SELECT (
STUFF(
(
SELECT ';' + r.value('(string/text())[1]','nvarchar(max)')
+ ';' + r.value('(id/#formatted)[1]','nvarchar(max)')
FOR XML PATH('')
),1,1,'')
) as [OUTPUT]
FROM #x.nodes('/root/row') AS A(r);
If you need the dynamic approach - look at this
declare #T table (string nchar(10), id int)
insert #T values
('test1', 1)
,('test2', 2)
declare #X xml = (select * from #T order by id for xml path('row'), root('root'))
select #x;
The first thing you see, that you - probably want to use NVARCHAR(10) instead of NCHAR(10). You might use LTRIM() too:
<root>
<row>
<string>test1 </string>
<id>1</id>
</row>
<row>
<string>test2 </string>
<id>2</id>
</row>
</root>
Now I start from scratch with NVARCHAR(10)
declare #T2 table (string nvarchar(10), id int);
insert #T2 values
('test1', 1)
,('test2', 22);
declare #X2 xml = (select * from #T2 order by id for xml path('row'), root('root'));
--First try is with ISNUMERIC and CASE WHEN
SELECT (
STUFF(
(
SELECT ';' + CASE WHEN ISNUMERIC(v.value('.','nvarchar(max)'))=1
THEN REPLACE(STR(v.value('.','int'),8),' ','0')
ELSE v.value('.','nvarchar(max)') END
FROM r.nodes('*') AS B(v)
FOR XML PATH('')
),1,1,'')
) as [OUTPUT]
FROM #x2.nodes('/root/row') AS A(r);
But: There are some character formats (scientific notations), which can be taken as numeric incidentically.
Add this to your table and try again
,('3d2',333) --breaks, because SELECT ISNUMERIC('3d2'),ISNUMERIC('1e1') returns 1 (for both)
Other/better approaches
If you are using SQL-Server 2012 or higher, you can use TRY_CAST, which will return NULL instead of an error
SELECT (
STUFF(
(
SELECT ';' + CASE WHEN TRY_CAST(v.value('.','nvarchar(max)') AS INT) IS NOT NULL
THEN REPLACE(STR(v.value('.','int'),8),' ','0')
ELSE v.value('.','nvarchar(max)') END
FROM r.nodes('*') AS B(v)
FOR XML PATH('')
),1,1,'')
) as [OUTPUT]
FROM #x2.nodes('/root/row') AS A(r);
Another option might the the explicit XQuery cast (Find possible XQuery functions here)
SELECT (
STUFF(
(
SELECT ';' + v.query('let $nd:=string(./text()[1])
,$nr:=concat("00000000",string($nd cast as xs:int?))
return if(string-length($nr)=8)
then $nd
else substring($nr,string-length($nr)-7)').value('.','nvarchar(max)')
FROM r.nodes('*') AS B(v)
FOR XML PATH('')
),1,1,'')
) as [OUTPUT]
FROM #x2.nodes('/root/row') AS A(r);
UPDATE
XML is not aware of an underlying type unless you specify a schema. Look at this:
declare #T2 table (string nvarchar(10), id int)
insert #T2 values
('test1', 1)
,('test2', 22)
,('1',333)
declare #x2 xml = (select * from #T2 order by id for xml raw('row'), root('root'),xmlschema)
select #x2;
<root>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sqltypes="http://schemas.microsoft.com/sqlserver/2004/sqltypes" targetNamespace="urn:schemas-microsoft-com:sql:SqlRowSet1" elementFormDefault="qualified">
<xsd:import namespace="http://schemas.microsoft.com/sqlserver/2004/sqltypes" schemaLocation="http://schemas.microsoft.com/sqlserver/2004/sqltypes/sqltypes.xsd" />
<xsd:element name="row">
<xsd:complexType>
<xsd:attribute name="string">
<xsd:simpleType>
<xsd:restriction base="sqltypes:nvarchar" sqltypes:localeId="1033" sqltypes:sqlCompareOptions="IgnoreCase IgnoreKanaType IgnoreWidth">
<xsd:maxLength value="10" />
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="id" type="sqltypes:int" />
</xsd:complexType>
</xsd:element>
</xsd:schema>
<row xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1" string="test1" id="1" />
<row xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1" string="test2" id="22" />
<row xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1" string="1" id="333" />
</root>
In this case you might ask the schema for the underlying type. But - if I get you correctly - you want to look at any value if it might be a number. This works as shown, but is extremely dangerous...
i am just trying to compare two xml and try to store the difference into another variable called #DiffXML but getting error called Incorrect syntax near the keyword 'SET'.
just guide what to fix. thanks
DECLARE #XML1 XML
DECLARE #XML2 XML
DECLARE #DiffXML nvarchar(max)
SET #DiffXML=''
SET #XML1 =
'<NewDataSet>
<Employee>
<EmpID>1005</EmpID>
<Name> keith </Name>
<DOB>12/02/1981</DOB>
<DeptID>ACC001</DeptID>
<Salary>10,500</Salary>
</Employee>
</NewDataSet>'
SET #XML2 =
'<NewDataSet>
<Employee>
<EmpID>1006</EmpID>
<Name> keith </Name>
<DOB>05/02/1981</DOB>
<DeptID>ACC002</DeptID>
<Salary>10,900</Salary>
</Employee>
</NewDataSet>'
;with XML1 as
(
select T.N.value('local-name(.)', 'nvarchar(100)') as NodeName,
T.N.value('.', 'nvarchar(100)') as Value
from #XML1.nodes('/NewDataSet/Employee/*') as T(N)
),
XML2 as
(
select T.N.value('local-name(.)', 'nvarchar(100)') as NodeName,
T.N.value('.', 'nvarchar(100)') as Value
from #XML2.nodes('/NewDataSet/Employee/*') as T(N)
)
SET #DiffXML=(select * from
(
select coalesce(XML1.NodeName, XML2.NodeName) as FieldName,
XML1.Value as OldValue,
XML2.Value as NewValue
from XML1
full outer join XML2
on XML1.NodeName = XML2.NodeName
where coalesce(XML1.Value, '') <> coalesce(XML2.Value, '')
) x FOR xml AUTO,elements XSINIL)
print #DiffXML
Change that from
SET #DiffXML=(select * from
to
SELECT #DiffXML=(select * from
and you will get your result printed
Given the following from an XML field in a table:
<View>
<Criminal xmlns="http://tempuri.org/crimes.xsd">
<Person>
<PersonID>1234</PersonID>
<LastName>SMITH</LastName>
<FirstName>KEVIN</FirstName>
<Cases>
<PersonID>1234</PersonID>
<CaseNumber>12CASE34</CaseNumber>
</Cases>
</Person>
</Criminal>
</View>
How would I pull the Person/PersonID, LastName, Firstname info? Same goes for the CaseNumber.
My next issue is similar to above but lets add a second namespace:
<MessageContent xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Content>Content in here!!</Content>
<Type>Empty</Type>
</MessageContent>
Notice I have 2 namespaces in there AND they also have ":xsi" and ":xsd" in there too. I think those are referred to as schemas.
Try this:
DECLARE #table TABLE (ID INT NOT NULL, XmlContent XML)
INSERT INTO #table VALUES(1, '<View>
<Criminal xmlns="http://tempuri.org/crimes.xsd">
<Person>
<PersonID>1234</PersonID>
<LastName>SMITH</LastName>
<FirstName>KEVIN</FirstName>
<Cases>
<PersonID>1234</PersonID>
<CaseNumber>12CASE34</CaseNumber>
</Cases>
</Person>
</Criminal>
</View>')
;WITH XMLNAMESPACES('http://tempuri.org/crimes.xsd' AS ns)
SELECT
PersonID = XmlContent.value('(/View/ns:Criminal/ns:Person/ns:PersonID)[1]', 'int'),
FirstName = XmlContent.value('(/View/ns:Criminal/ns:Person/ns:FirstName)[1]', 'varchar(50)'),
LastName = XmlContent.value('(/View/ns:Criminal/ns:Person/ns:LastName)[1]', 'varchar(50)')
FROM #table
WHERE ID = 1
Returns an output of:
And for your second part of the question: yes, you have two namespaces defined - but they're not being used at all - so you can basically just ignore them:
INSERT INTO #table VALUES(2, '<MessageContent xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Content>Content in here!!</Content>
<Type>Empty</Type>
</MessageContent>')
SELECT
Content = XmlContent.value('(/MessageContent/Content)[1]', 'varchar(50)'),
Type = XmlContent.value('(/MessageContent/Type)[1]', 'varchar(50)')
FROM #table
WHERE ID = 2
Returns: