Transform JSON element to SQL Table Format - sql

This question is in relation to my previous problem which was solved.
Link to Previous Problem
Now I need to access columns and values of the Tag "GetCustomReportResult" and transform into a SQL Table format.
The JSON String is actually stored in a column in a SQL Table as seen below and I am trying to transform the elements in the tag "GetCustomReportResult" in a table format with columns and values for each of the "ApplicationID":
Here is what I was trying to access the columns and values within the Tag "GetCustomReportResult":
SELECT
y.cijreport,
y.ApplicationId,
JSON_VALUE(x.value, '$.CIP') as CIP,
JSON_VALUE(x.value, '$.CIQ') as CIQ
--other fields
FROM table as y
CROSS APPLY OPENJSON (cijreport) as x
where cijreport is not null
I now get this error when I execute:
Msg 13609, Level 16, State 2, Line 1 JSON text is not properly formatted. Unexpected character 'o' is found at position 0.

Firstly, you are missing the JSON path '$.data.response'.
Next, you can't use JSON_VALUE on a whole object, it's only good for scalar values. You can either use JSON_QUERY:
SELECT
y.cijreport,
y.ApplicationId,
JSON_QUERY(x.value, '$.CIP') as CIP,
JSON_QUERY(x.value, '$.CIQ') as CIQ, x.VALUE
--other fields
FROM YourTable as y
CROSS APPLY OPENJSON (cijreport, '$.data.response') as x;
Or you can specify property names in OPENJSON
SELECT
y.cijreport,
y.ApplicationId,
x.CIP,
x.CIQ
--other fields
FROM YourTable as y
CROSS APPLY OPENJSON (cijreport, '$.data.response')
WITH (
CIP nvarchar(max) AS JSON,
CIQ nvarchar(max) AS JSON
) AS x;
db<>fiddle
Note that the where cijreport is not null filter is not necessary, because CROSS APPLY OPENJSON will return 0 rows in such a case, and CROSS APPLY acts like an inner join.

Related

Get rows from comma separated list

I want to convert a comma separated list back into a table.
For eg.
I have a table which looks like this
Sid roleid
500 1,5,
501 1,5,6,
I want output like this
Sid roleid
500 1
500 5
501 1
501 5
501 6
Please help.
Create table #temp(Sid int,roleid varchar(100))
Insert into #temp values(500,'1,5,'),(501,'1,5,6,')
Using STRING_SPLIT() means, that you are working on SQL Server 2016 (oder higher).
However, STRING_SPLIT() has a huge draw back: It is not guaranteed to return the items in the expected order (see the docs, section "Remarks"). In my eyes this is an absolut show stopper...
But - luckily - there is a fast and easy-to-use workaround in v2016+:
Create table #temp(Sid int,roleid varchar(100))
Insert into #temp values(500,'1,5,'),(501,'1,5,6,');
SELECT t.[Sid]
,A.[key] AS position
,A.[value] AS roleid
FROM #temp t
CROSS APPLY OPENJSON(CONCAT('["',REPLACE(t.roleid,',','","'),'"]')) A
WHERE A.[value]<>'';
A simple number array 1,3,5 needs nothing more than brackets to be a JSON array ([1,3,5]). In your case, due to the trailing comma, I deal with it as strings. 1,3,5, will be taken as array of strings: ["1","3","5",""]. The final empty string is taken away by the WHERE clause. The rest is easy...
Other than STRING_SPLIT() the docs proof, that OPENJSON will reflect an item's position in the [key] column:
When OPENJSON parses a JSON array, the function returns the indexes of the elements in the JSON text as keys.
General hint: avoid STRING_SPLIT() as lons as there is no additional key/position column added to its result set.
Use string_split() :
select t.sid, spt.value
from table t cross apply
string_split(t.roleid, ',') as spt
order by t.sid, spt.value;
Use sring_split():
select t.sid, value rid
from t
cross apply string_split(t.roleid, ',') rid
order by t.sid, rid

XML column Data into rows

I have a requirement where I have XML data column in database, which I need to pull in the form of values from rows XML data column from database. My XML is like
<ListID><ID>169346</ID><ID>289492</ID><ID>315264</ID><ID>415265</ID></ListID>
<ListID><ID>169356</ID><ID>299492</ID><ID>315264</ID><ID>415265</ID></ListID>
And I want data to be pulled up like
ID
169346
289492
315264
415265
169356
299492
315264
415265
You can use something like this:
SELECT XC.value('.', 'int')
FROM dbo.YourTableHere
CROSS APPLY XmlColumn.nodes('//ID') AS XT(XC)
This basically takes every <ID> element that exists in the XML column, and extract the values as int and shows them in a result set.
Update: from your question, mentioning I have XML data column, I assumed that your column in the SQL Server table is in fact of type XML. And it should be, if you're storing XML in it!
But if it's not - then you need to cast your column to XML first, before using the function .nodes() in my code sample:
SELECT XC.value('.', 'int')
FROM dbo.YourTableHere
CROSS APPLY CAST(YourColumn AS XML).nodes('//ID') AS XT(XC)
Here is one other way is to convert it to XML & then convert it to row :
SELECT split.a.value('.', 'varchar(max)') ID
FROM
(
SELECT CAST(ID AS XML) AS String from <table_name>
) a
CROSS APPLY String.nodes('//ID') AS split(a);
Result :
ID
169346
289492
315264
415265
169356
299492
315264
415265

SQL return list of ntext and convert it to XML

I have a query that will return and a list of ntext, and in these ntext they contain XML value.
my question is how to convert each of ntext to xml and do logic with it
Query:
select a.content
from dbo.content as a
inner join dbo.xml_collection_tbl as b on a.xml_fg_id = b.xml_collection_id
where a.inherit_from='val1' and b.collection_title='val2' and a.content_table= 'val3'
result:
what I want to do here is to check rather the Query returns contain the value that I looking for. lets say the page title = "hello World"
I tried below.But it returns many empty rows and with one correct row
select cast(a.content_html as xml).query('(//root[pagetitle/text()="AAA"])') content_html1
from dbo.content as a
inner join dbo.xml_collection_tbl as b on a.xml_fg_id = b.xml_collection_id
where a.inherit_from='val1' and b.collection_title='val2' and a.content_table= 'val3'
expected result is: return only one row where it's not empty (row 54)
First of all: NTEXT, TEXT and IMAGE are deprecated for centuries and will not be supported in future versions! Get rid of this type as soon as possible!
SQL-Server does not store the XML as the text you see, but as a hierarchically stuctured tree. This makes the handling of an XML astonishingly fast (no parsing on string level!). Your approach has to parse each and every XML over and over, which is a very expensive operation! Change your XML's storage to the native XML type and you will be very happy with the new performance!
If you have to stick with this, you can try as such:
DECLARE #t TABLE (ID INT IDENTITY, YourXML NTEXT);
INSERT INTO #t VALUES('<root><pagetitle>111</pagetitle></root>')
,('<root><pagetitle>aaa</pagetitle></root>')
,('<root><pagetitle>222</pagetitle></root>')
SELECT A.CastedXML
,B.pt.query('.')
FROM #t AS t
CROSS APPLY(SELECT CAST(YourXML AS XML) AS CastedXML) AS A
CROSS APPLY A.CastedXML.nodes('/root/pagetitle[text()="aaa"]') AS B(pt);
Demo of XQuery expression https://learn.microsoft.com/en-us/sql/xquery/xquery-language-reference-sql-server to filter data
with sd as (
select cast(content_html as xml) as col
from (
values
('<root><pagetitle>FFF</pagetitle></root>')
,('<root><pagetitle>AAA</pagetitle></root>')
) as a(content_html)
)
select t.n.value('.[1]', 'varchar(100)') as content_html1
from sd
cross apply col.nodes('root/pagetitle[text()="AAA"]') t(n)

Compare Xml data in SQL

I have two tables with same NVARCHAR field that really contains XML data.
in some cases this really-XML-field is really same as one row in other table but differs in attributes order and therefor string comparison does not return the correct result!!!
and to determining the same XML fields ,I need to have a comparison like:
cast('<root><book b="" c="" a=""/></root>' as XML)
= cast('<root><book a="" b="" c=""/></root>' as XML)
but I get this Err Msg:
The XML data type cannot be compared or sorted, except when using the
IS NULL operator.
then what is the best solution to determine the same XML without re-casting them to NVARCHAR?
Why cast it at all? Just plug them into an XML column in a temp table and run Xquery to compare them to the other table. EDIT: Included example of the comparison. There are many, many ways to run the query against the XML to get the rows that are the same - exactly how that query is written is going to depend on preference, requirements, etc. I went with a simple group by/count, but a self join could be used, WHERE EXISTS against the columns that are being searched for duplicates, you name it.
CREATE TABLE #Test (SomeXML NVARCHAR(MAX))
CREATE TABLE #XML (SomeXML XML)
INSERT #Test (SomeXML)
VALUES('<root><book b="b" c="c" a="a"/></root>')
,('<root><book a="a" b="b" c="c"/></root>')
INSERT #XML (SomeXML)
SELECT SomeXML FROM #Test;
WITH XMLCompare (a,b,c)
AS
(
SELECT
x.c.value('#a[1]','char(1)') AS a
,x.c.value('#b[1]','char(1)') AS b
,x.c.value('#c[1]','char(1)') AS c
FROM #XML
CROSS APPLY SomeXMl.nodes('/root/book') X(C)
)
SELECT
a
,b
,c
FROM XMLCompare as a
GROUP BY
a
,b
,c
HAVING COUNT(*) >1

Conversion failed when converting the varchar value to data type int

i have this query and its work fine for me
SELECT STUFF(
(SELECT ','+SLT_SubListName FROM sublists where SLT_SubListId in (1,2) FOR XML PATH('')),1,1,'');
but when i change the in parameters (1,2) into the 'select SBS_SubListId from subscriber where SBS_SubscriberId=1'
which also return the 1,2
SELECT STUFF(
(SELECT ','+SLT_SubListName FROM sublists where SLT_SubListId in (select SBS_SubListId from subscriber where SBS_SubscriberId=1
) FOR XML PATH('')),1,1,'');
its giving me the error which is the following
Conversion failed when converting the varchar value '1,2,4,5' to data type int.
if anybody needs i can also post my table schema here.
thanks
you have to first split these comma sepered values on comma bases, and then apply converrsion.
I suspect tht your subquery is returning one entry of "1,2,4,5" which is not an integer. You need to get it to return 4 rows with one of these in each.
If you are doing this - show the results of the subquery - then you may have type differences.
Since your subquery seems to be returning the varchar '1,2,4,5', you can try the query using CONTAINS:
SELECT STUFF(
(SELECT ','+SLT_SubListName FROM sublists where CONTAINS((select SBS_SubListId from subscriber where SBS_SubscriberId=1), SLT_SubListId
) FOR XML PATH('')),1,1,'');
That way it treats the results from the subquery as text.