Remove Root XML nodes from default output of Sql Server statement - sql-server

I want #MyActualXMLOut to look like #MyDesiredXMLOut... How? Thanks in advance!
#MyDesiredXMLOut =
<MyRequiredRoot>
<Property1>Value1</Property1>
<Property2>Value2</Property2>
</MyRequiredRoot>
#MyActualXMLOut
<_x0040_MyTableVar>
<MyXML>
<MyRequiredRoot>
<Property1>Value1</Property1>
<Property2>Value2</Property2>
</MyRequiredRoot>
</MyXML>
</_x0040_MyTableVar>
The code below can be run as is...
DECLARE #MyDesiredXMLOut XML;
DECLARE #MyActualXMLOut XML;
SELECT #MyDesiredXMLOut =
CONVERT( XML,
'<MyRequiredRoot><Property1>Value1</Property1>
<Property2>Value2</Property2>
</MyRequiredRoot>' );
DECLARE #MyTableVar table( ID int NOT NULL, MyXML XML NOT NULL );
INSERT INTO #MyTableVar VALUES( 1, #MyDesiredXMLOut )
SELECT #MyActualXMLOut =
( SELECT MyXML
FROM #MyTableVar
WHERE ID = 1
FOR XML AUTO )
SELECT #MyDesiredXMLOut;
SELECT #MyActualXMLOut;

FOR XML AUTO is trying to add information about your table name (which likely contains characters that aren't valid XML element names) and the column name it came from.
Change
SELECT #MyActualXMLOut =
( SELECT MyXML
FROM #MyTableVar
WHERE ID = 1
FOR XML AUTO )
to
SELECT #MyActualXMLOut =
( SELECT MyXML as '*'
FROM #MyTableVar
WHERE ID = 1
FOR XML PATH('') )
Explanation: as '*' tells SQL Server that you just want the column value directly, don't use the column name as a tag name; FOR XML PATH('') says you don't want to add any additional root node around the output, just use as is.

You can use query('/')
SELECT #MyActualXMLOut =
( SELECT MyXML.query ('/')
FROM #MyTableVar
WHERE ID = 1
FOR XML PATH('')
)

I don't know if this is real life example, but:
The value in the table is the XML already...
Leave away the FOR XML AUTO (Anyway as pointed out one should prefer PATH):
SELECT #MyActualXMLOut =
( SELECT MyXML
FROM #MyTableVar
WHERE ID = 1);
Or even simpler
SELECT #MyActualXMLOut = MyXML
FROM #MyTableVar
WHERE ID = 1;

Related

Get the Missing and Excess tags from a XML field

I have a table, Customer(Id int,Name nvarchar(100),Detail xml)
Sample Data:
1,'Abc','<ROOT> <TAG1>False</TAG1> <TAG3>value</TAG3> <TAG14>value</TAG14> </ROOT>'
2,'Pqr','<ROOT> <TAG2>False</TAG2> <TAG8>value</TAG8> <TAG11>value</TAG11> </ROOT>'
Also I have XML variable , #v_xml = '<ROOT> <TAG1>value</TAG1> <TAG2>value</TAG2> <TAG8>False</TAG8> <TAG14>False</TAG14> </ROOT>'.
Now I want get the Missing Tags and Excess Tags (in XML format) of each Customer comparing to the XML variable #v_xml (No need to consider the value, what ever it may be)
Expected Result:
Id Name Missing Excess
1,'Abc','<ROOT><TAG2>value</TAG2> <TAG8>value</TAG8> </ROOT>','<ROOT><TAG3>value</TAG3> </ROOT>'
2,'Pqr','<ROOT><TAG1>value</TAG1> <TAG14>False</TAG14> </ROOT>','<ROOT><TAG11>value</TAG11> </ROOT>'
There is no nested nodes/level in the XML. Only direct child elements under ROOT tag. But the number of child tags will vary. I am looking for a simple and common logic to resolve this (with or without SQL query).
Main idea parse tag name (local-name(.)) and concat diffs into xml
DECLARE #t TABLE (
Id INT PRIMARY KEY,
Name VARCHAR(50),
X XML
)
INSERT INTO #t
VALUES
(1, 'Abc', N'<ROOT><TAG1>False</TAG1><TAG3>value</TAG3><TAG14>value</TAG14></ROOT>'),
(2, 'Pqr', N'<ROOT><TAG2>False</TAG2><TAG8>value</TAG8><TAG11>value</TAG11></ROOT>')
DECLARE #x XML = N'<ROOT><TAG1>value</TAG1><TAG2>value</TAG2><TAG8>False</TAG8><TAG14>False</TAG14></ROOT>'
SELECT t.Id, t.Name, t2.val.query('Missing/*'), t2.val.query('Excess/*')
FROM #t t
CROSS APPLY (
SELECT
Missing = Missing.query,
Excess = Excess.query
FROM (
SELECT
query = t.c.query('.'),
tag = t.c.value('local-name(.)', 'SYSNAME')
FROM x.nodes('*/*') t(c)
) Excess
FULL JOIN (
SELECT
query = t.c.query('.'),
tag = t.c.value('local-name(.)', 'SYSNAME')
FROM #x.nodes('*/*') t(c)
) Missing ON Missing.tag = Excess.tag
WHERE Missing.tag IS NULL
OR Excess.tag IS NULL
FOR XML PATH(''), TYPE
) t2 (val)
Output -
----------- ---------- ------------------------- ------------------------------------------
1 Abc <TAG3>value</TAG3> <TAG2>value</TAG2><TAG8>False</TAG8>
2 Pqr <TAG11>value</TAG11> <TAG1>value</TAG1><TAG14>False</TAG14>

TSQL Select Clause with Case Statement

I have a basic select statement that is getting me a list of types that are stored in the database:
SELECT teType
FROM BS_TrainingEvent_Types
WHERE source = #source
FOR XML PATH ('options'), TYPE, ELEMENTS, ROOT ('types')
My table contains a type column and a source column.
There is a record in that table where I need to include it for two separate sources but I can't create a separate record for it.
**Table Data**
type | source
test users
test2 members
test3 admins
I need a case statement to be able to say IF source = admins also give me the type test2.
Does this make sense and is it possible to do with a basic select?
Update
I came up with this temp solution but I still think there is a better way to handle this.:
DECLARE #tmp AS TABLE (
QID VARCHAR (10));
INSERT INTO #tmp (QID)
SELECT DISTINCT qid
FROM tfs_adhocpermissions;
SELECT t.QID,
emp.FirstName,
emp.LastName,
emp.NTID,
(SELECT accessKey
FROM TFS_AdhocPermissions AS p
WHERE p.QID = t.QID
FOR XML PATH ('key'), TYPE, ELEMENTS, ROOT ('keys'))
FROM #tmp AS t
LEFT OUTER JOIN
dbo.EmployeeTable AS emp
ON t.QID = emp.QID
FOR XML PATH ('data'), TYPE, ELEMENTS, ROOT ('root');
try this
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--create temp table for testing
IF OBJECT_ID('Tempdb..#BS_TrainingEvent_Types') IS NOT NULL
DROP TABLE #BS_TrainingEvent_Types
SELECT [type] ,
[source]
INTO #BS_TrainingEvent_Types
FROM ( VALUES ( 'test', 'users'), ( 'test2', 'members'),
( 'test3', 'admins') ) t ( [type], [source] )
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--final query
DECLARE #Source VARCHAR(10) = 'users'
IF #Source = 'admins'
BEGIN
SELECT [Type]
FROM #BS_TrainingEvent_Types
WHERE source = #source
OR [type] = 'test2'
FOR XML PATH('options') ,
TYPE ,
ELEMENTS ,
ROOT('types')
END
ELSE
BEGIN
SELECT [Type]
FROM #BS_TrainingEvent_Types
WHERE source = #source
FOR XML PATH('options') ,
TYPE ,
ELEMENTS ,
ROOT('types')
END
select sq.teType
from (
SELECT t.teType
FROM BS_TrainingEvent_Types t
WHERE t.source = #source
union all
SELECT t.teType
FROM BS_TrainingEvent_Types t
WHERE #source = 'admins' and t.source = 'members'
) sq
FOR XML PATH ('options'), TYPE, ELEMENTS, ROOT ('types');
Though normally it would be better to introduce an additional table that would store these relationships, so that the whole idea would be more expandable.

Setting a SQL variable equal to column in UPDATE with xml

Can anyone help me figure out what is wrong here?
I would like to set the value of the variable #Customer to a value of a column in the same row. I am assuming I am out of scope or something. The tag gets inserted but it is blank. If I manually set #customer to something, then it does get populated in the xml. There HAS to be a way to do this.
Declare #Customer varchar(500)
UPDATE Ore.RuleInfo
set #Customer=RG.[RuleGroupName], UserEntryXML.modify('
insert
<CustomerName>{sql:variable("#Customer")}</CustomerName>
into (UserEntry/Results/Customer)[1]')
From
Ore.RuleInfo RI
inner join ORE.RuleGroup RG on RG.RuleGroupID = RI.RuleGroupID
Instead of using sql:variable, use sql:column
DECLARE #tbl TABLE (x XML, customerName VARCHAR(100));
INSERT INTO #tbl
( x, customerName )
VALUES ( '<id>1</id><name>old1</name>', 'customer1' ),
( '<id>2</id><name>old2</name>', 'customer2' ),
( '<id>3</id><name>old3</name>', 'customer3' );
SELECT *
FROM #tbl
UPDATE #tbl
SET x.modify('
replace value of (/name/text())[1]
with sql:column("customerName")
')
SELECT *
FROM #tbl

Grouping XML Elements in FOR XML Clause

I am trying to create a structure xml document from my temp table .The temp table is in the following format .
CREATE TABLE #Temp1 ( Name Char( 30 ), seqid integer, salary int );
INSERT INTO #Temp1 VALUES('DEAL' ,123,6)
INSERT INTO #Temp1 VALUES('DEAL' ,56,6)
INSERT INTO #Temp1 VALUES('TRACNHE' ,1253,56)
INSERT INTO #Temp1 VALUES('TRACNHE' ,5,65)
INSERT INTO #Temp1 VALUES('ASSET' ,56,23)
I am trying to create an xml format in the following form :
<Response>
<Deal>
<seqid="123" salary="6" />
<seqid="56" salary="6" />
<Deal>
<TRACNHE>
<seqid="1253" salary="56"/>
<seqid="5" salary="65"/>
</TRACNHE>
<ASSET>
<seqid="56" salary="23"/>
</ASSET>
</Response>
SELECT Name, (SELECT SEQID FROM #TEMP1 T WHERE T.Name = T1.Name)
FROM (SELECT DISTINCT NAME FROM #TEMP1 ) T1
FOR XML PATH('rEPONSE')
DROP TABLE #Temp1
DROP TABLE #Temp1
I tried the above query but says that subquery returned more than 1 value
Could you let me know as to what i am missing in this query .
Is there a better way to handle this scenario.
Thanks in advance
based on your requirement, i'm seeing there are 2 types of complexities
You are trying to get the xml with grouped items.
For each group trying to create an xml element with two attributes
without any proper name
<seqid="1253" salary="56"/>
instead of
<ss seqid="1253" salary="56"/>
just look into this below query, it may help
SELECT
(SELECT
seqid 'ss/#seqid'
, salary 'ss/#salary'
FROM Temp1 as t where t.Name = 'Deal'
FOR XML PATH('Deal') , TYPE
) ,
(SELECT
seqid 'ss/#seqid'
, salary 'ss/#salary'
FROM Temp1 as t where t.Name = 'TRACNHE'
FOR XML PATH('TRACNHE') , TYPE
) ,
(SELECT
seqid 'ss/#seqid'
, salary 'ss/#salary'
FROM Temp1 as t where t.Name = 'ASSET'
FOR XML PATH('ASSET') , TYPE
)
FOR XML PATH(''), ROOT('Response');

Using XML.Query for filtering multiple values

I have XML column in my table which can contain XML like these 2 records :
record 1, suppose that ID of record is 1 and XML content is
<content>
<node1 >10</node1>
<node2 >20</node2>
<node3 >30</node3>
<node4 >40</node4>
<node5 >50</node5>
</content>
record 2 , suppose that ID of record is 2 and XML content is
<content>
<node_name_1 >10</node_name_1>
<hello >20</hello>
</content>
I want to know IDs of records with inner node value of 10 .
declare #ids nvarchar(500)
set #ids = '10';
select id from tablename
where
convert(nvarchar(max),xmlfieldname.query('data(/content/* = ( sql:variable("#ids") ) )') ) = 'true'
this is working perfectly , but my problem is when I want to use multiple values for inner nodes . I want to know IDs of records which inner values are either 10 or 20 . This query works perfect :
select id from tablename
where
convert(nvarchar(max),xmlfieldname.query('data(/content/* = ( 10,20 ) )') ) = 'true'
But when I use sql:variable , nothing returned !
declare #ids nvarchar(500)
set #ids = '10,20';
select id from tablename
where
convert(nvarchar(max),xmlfieldname.query('data(/content/* = ( sql:variable("#ids") ) )') ) = 'true'
how can I use something like Select * from mytable where fieldname in ('123','456') in XML ?
You're asking for it to find the entry which has "10,20" for that value, as if you were saying "where fieldname in ('10,20')".
It's the age-old Split question...
So... try:
declare #qry nvarchar(max);
select #qry = 'select id from tablename where convert(nvarchar(max),xmlfieldname.query(''data(/content/* = ( ' + #ids + ' ) )'') ) = ''true''';
exec sp_executesql #qry
Rob

Resources