Adding array to array with JSON_MODIFY - sql

DECLARE #JSON_CurrentArray NVARCHAR(MAX) = '{"Some List":
[{"Name":"Item1","Id":"2"},{"Name":"Item2","Id":"3"}]}';
DECLARE #JSON_TopLevel NVARCHAR(MAX) = '{"OverAll":[{"Product Section":[]}]}';
SET #JSON_TopLevel = JSON_MODIFY(#JSON_TopLevel, 'append $."Overall"."Product Selection"', JSON_QUERY(#JSON_CurrentArray));
SELECT #JSON_TopLevel;
Ive been trying to stick CurrentArray into TopLevel,
Tried some crazy append/lax/strict combinations... but im new to JSON Manipulation and am almost at 'liquid brain stage' over this item.
I also thought about adding a blank array, but to no avail (i might be doing that wrong also)
Right now im code blind, so, If you can somehow inject one array into another... #foreverindebted.

If I understand you correctly you want something like this:
DECLARE #JSON_CurrentArray NVARCHAR(MAX) = '{"Some List":
[{"Name":"Item1","Id":"2"},{"Name":"Item2","Id":"3"}]}';
DECLARE #JSON_TopLevel NVARCHAR(MAX) = '{"OverAll":[{"Product Section":[]}]}';
SET #JSON_TopLevel = JSON_MODIFY(#JSON_TopLevel, 'append $."OverAll"[0]."Product Section"', JSON_QUERY(#JSON_CurrentArray));
SELECT #JSON_TopLevel;
DBFiddle
Result:
{"OverAll":[{"Product Section":[{"Some List": [{"Name":"Item1","Id":"2"},{"Name":"Item2","Id":"3"}]}]}]}
To check if your path is correct you could use JSON_QUERY and strict mode:
SELECT JSON_QUERY(#JSON_TopLevel, 'strict $."OverAll"."Product Section"')
--Msg 13608 Level 16 State 5 Line 7
--Property cannot be found on the specified JSON path.
-- vs
SELECT JSON_QUERY(#JSON_TopLevel, 'strict $."OverAll"[0]."Product Section"')
-- []

Related

Pulling GUIDs out of a JSON string in SQL Server

I need to pull some GUIDs out of a json string in SQL Server. An example of what the string might look like is as follows:
{"priorityArea":"a273b556-f0ab-4d7a-97ac-ddb7dab06130","priority":"Ensure best possible provision for pupils with specific behaviour issues","startDatePicker":"10/05/2019","deadlineDatePicker":"18/09/2019","userPicker":"48698,48693","actionWidget-1555338252504":"85e3ad8f-2586-4612-a9e7-e1c9d3f66181,6b66328f-c13f-4d8c-81ec-fccb8c1caa6e","resourceWidget-1557502650616":"98714348-cf7d-4583-89d5-c7d61cafea72","sdpGrade-1555338253145":"4"}
The GUID(s) I need is the one that comes after 'resourceWidget-[number]'. I would struggle with this even if the json string looked the same everytime, but there are further challenges:
The position of resourceWidget changes in the string depending on front-end behaviour
The unique number that comes after 'resourceWidget-' changes in every string
Sometimes more than one resource GUID is returned in the string, e.g.
resourceWidget-1555338252504":"98714348-cf7d-4583-89d5-c7d61cafea72, 87ea276b-5b7f-4b44-b05e-775e9fd2690c
If anyone is able to help, it would be much appreciated.
Seems like a simple OPENJSON call and a WHERE would work:
DECLARE #JSON nvarchar(MAX) = N'{
"priorityArea": "a273b556-f0ab-4d7a-97ac-ddb7dab06130",
"priority": "Ensure best possible provision for pupils with specific behaviour issues",
"startDatePicker": "10/05/2019",
"deadlineDatePicker": "18/09/2019",
"userPicker": "48698,48693",
"actionWidget-1555338252504": "85e3ad8f-2586-4612-a9e7-e1c9d3f66181,6b66328f-c13f-4d8c-81ec-fccb8c1caa6e",
"resourceWidget-1557502650616": "98714348-cf7d-4583-89d5-c7d61cafea72",
"sdpGrade-1555338253145": "4"
}';
SELECT TRY_CONVERT(uniqueidentifier,[value]) AS resourceWidget
FROM OPENJSON(#JSON)
WHERE [key] LIKE N'resourceWidget-%';
If the JSON can contain a delimited string, add a STRING_SPLIT:
DECLARE #JSON nvarchar(MAX) = N'{
"priorityArea": "a273b556-f0ab-4d7a-97ac-ddb7dab06130",
"priority": "Ensure best possible provision for pupils with specific behaviour issues",
"startDatePicker": "10/05/2019",
"deadlineDatePicker": "18/09/2019",
"userPicker": "48698,48693",
"actionWidget-1555338252504": "85e3ad8f-2586-4612-a9e7-e1c9d3f66181,6b66328f-c13f-4d8c-81ec-fccb8c1caa6e",
"resourceWidget-1555338252504":"98714348-cf7d-4583-89d5-c7d61cafea72, 87ea276b-5b7f-4b44-b05e-775e9fd2690c",
"sdpGrade-1555338253145": "4"
}';
SELECT TRY_CONVERT(uniqueidentifier,TRIM(SS.[value])) AS resourceWidget --TRIM because your example has a leading space
FROM OPENJSON(#JSON) OJ
CROSS APPLY STRING_SPLIT(OJ.[value],',') SS
WHERE OJ.[key] LIKE N'resourceWidget-%';

Searching through XML in T-SQL with conditions

I am trying to get the correct info from an XML data type into regular scalar variables based on conditions, however I am having trouble getting the correct info back.
Here is the XML I am searching through:
<Loop2420>
<NM1>
<F98_1>PW</F98_1>
<F1065>2</F1065>
</NM1>
<N3>
<F166>81715 DOCTOR CARRE</F166>
</N3>
<N4>
<F19>INDIO</F19>
<F156>CA</F156>
<F116>92201</F116>
</N4>
</Loop2420>
<Loop2420>
<NM1>
<F98_1>45</F98_1>
<F1065>2</F1065>
</NM1>
<N3>
<F166>51250 MECCA AVE</F166>
</N3>
<N4>
<F19>COACHELLA</F19>
<F156>CA</F156>
<F116>92236</F116>
</N4>
</Loop2420>
Basically I need to get the numbers from <'F116'> but only if <'F98_1'> is equal to 'PW'.
I have tried:
declare #zip varchar(30)
select #zip = T.value('(F116)[1]','varchar(30)')
from #TransactionXML.nodes('/Loop2420/N4') Trans(T)
where T.value('(/Loop2420/NM1/F98_1)[1]','varchar(30)') = 'PW'
But that sometimes returns the value from <'F116'> even if <'F98_1'> is equal to '45'.
Any suggestions? Thanks.
Put the test in the XQuery itself and clamp it to the node you're checking:
SELECT #zip = T.value('(N4/F116)[1]', 'varchar(30)')
FROM #TransactionXML.nodes('/Loop2420') Trans(T)
WHERE T.exist('NM1/F98_1[text()="PW"]') = 1
If PW is not a static value, use the sql:variable() or sql:column() function to incorporate it in the query.

Sql Server 2012 How to Get List on Function

Function Details:
alter FUNCTION siralama_puan (#suggestion_id int)
RETURNS int
AS
Begin
Declare #comment_count int,#like_count int,#favorite_count int,#date_point int,#suggestion_point int,#suggestion_date datetime,#fark int
set #comment_count=(select [Suggestion].CommentCount from [Suggestion] where [Suggestion].Id= #suggestion_id)
set #like_count=(select [Suggestion].LikeCount from [Suggestion] where [Suggestion].Id=#suggestion_id)
set #favorite_count=(select [Suggestion].FavoriteCount from [Suggestion] where [Suggestion].Id=#suggestion_id)
set #suggestion_date=(select [Suggestion].Crtm from [Suggestion] where [Suggestion].Id=#suggestion_id)
set #fark =(select DATEDIFF(day,#suggestion_date,GETDATE()))
if #fark<6
set #date_point=30
else if #fark<10 and #fark>=6
set #date_point=20
else
set #date_point=10
set #suggestion_point=(#comment_count*2)+(#like_count)+(#favorite_count*3)+#date_point
RETURN #suggestion_point
End
Calling Function:
select dbo.siralama_puan (122280,1) as puan order by puan desc
but it didn't work.Error:Procedure or function dbo.siralama_puan has too many arguments specified.Multiple arguments not working.
It is not possible to define one parameter of type INT and pass in a list. But there are several approaches:
TYPE
Create your own type with CREATE TYPE read here.... This allows you to pass in a list very similar to a (read only) table
XML
Let your parameter be of type XML and pass in something like
<root>
<prm value="122280"/>
<prm value="1"/>
</root>
It's quite easy to transform this XML within your function to a list with something like
SELECT Prm.value('#value','int') AS PrmValue
FROM #prm.nodes('/root/prm') AS One(Prm)
CSV
A parameter of type VARCHAR(MAX) and a comma separated list like "122280,1". Find a way to split this here: section "Dynamic IN-statement"

Using xml with SQL to evaluate a dynamic variable

This is a unique problem..I think. So my goal is to input a variable and get a row from my column. Let me explain a little with the code im doing.
SELECT
pref.query('Database/text()') as PersonSkills,
pref.query('FillQuery/text()') as PersonSkills,
pref.query('TabText/text()') as PersonSkills,
pref.query('TooltipText/text()') as PersonSkills
FROM table CROSS APPLY
Tag.nodes('/Root/Configuration/TaskSelectorControl/QueueSelector') AS People(pref)
this works fine. However what I need to do is pass in the last part, the queue selector as a variables.
DECLARE #Xml XML
DECLARE #AttributeName VARCHAR(MAX) = 'QueueSelector'
SELECT
pref.query('Database/text()') as PersonSkills,
pref.query('FillQuery/text()') as PersonSkills,
pref.query('TabText/text()') as PersonSkills,
pref.query('TooltipText/text()') as PersonSkills
FROM table CROSS APPLY
Tag.nodes('/Root/Configuration/TaskSelectorControl[#Name=sql:variable("#AttributeName")]
') AS People(pref)
this doesnt work, any ideas why?
Well, I kinda lied. the bottom works, however it returns an empty dataset
/Root/Configuration/TaskSelectorControl/QueueSelector
is not equivalent to:
/Root/Configuration/TaskSelectorControl[#Name='QueueSelector']
The above XPath selects <TaskSelectorControl Name="QueueSelector">, not <QueueSelector> children of <TaskSelectorControl>.
You could either do this in XPath:
/Root/Configuration/TaskSelectorControl/*[local-name(.)=sql:variable("#AttributeName")]
Or it might be simpler to concat prior to evaluating:
'/Root/Configuration/TaskSelectorControl/' + #AttributeName

SQL, Find node value in xml variable, if it exists insert additional nodes into xml variable

I've got a Stored Procedure in SQL, where I have the following declaration:
Declare #fields xml
My SP gets passed values from the front end and then gets executed. The values it gets passed looks like this depending on what the user selects from the front end. For the purpose of this example I have included only 3 ID's.
'<F><ID>979</ID><ID>1000</ID><ID>989</ID></F>'
My question is this:
How can I find the node = 1000 and if that is present (exists) then insert (add) to 2 additional nodes,
<ID>992</ID><ID>993</ID>
to my existing '<F><ID>979</ID><ID>1000</ID><ID>989</ID></F>' xml.
If <ID>1000</ID> isn't present do nothing.
So, end result should be something like this if 1000 is present.
<F><ID>979</ID><ID>1000</ID><ID>989</ID><ID>992</ID><ID>993</ID></F>
If not, the result should stay:
<F><ID>979</ID><ID>1000</ID><ID>989</ID></F>
I just can't get my head around this?
Check this:
declare #fields xml = '<F><ID>979</ID><ID>1000</ID><ID>989</ID></F>'
, #add xml = '<ID>992</ID><ID>993</ID>'
;
if #fields.exist('/F[1]/ID[text()="1000"]') = 1
set #fields.modify('insert sql:variable("#add") as last into /F[1]');
select #fields