SQL Server - XQuery for XML - sql

Just similar other post, I need to retrieve any rows from table applying criteria on Xml column, for instance, supposing you have an xml column like this:
<DynamicProfile xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/WinTest">
<AllData xmlns:d2p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d2p1:KeyValueOfstringstring>
<d2p1:Key>One</d2p1:Key>
<d2p1:Value>1</d2p1:Value>
</d2p1:KeyValueOfstringstring>
<d2p1:KeyValueOfstringstring>
<d2p1:Key>Two</d2p1:Key>
<d2p1:Value>2</d2p1:Value>
</d2p1:KeyValueOfstringstring>
</AllData>
</DynamicProfile>
My query would be able to return all rows where node value <d2p1:Key> = 'some key value' AND node value <d2p1Value = 'some value value'.
Imagine of that just as a dynamic table where KEY node represent the column name and Value node represent column's value.
The following query does not work because key and value nodes are not sequential:
select * from MyTable where
MyXmlField.exist('//d2p1:Key[.="One"]') = 1
AND MyXmlField.exist('//d2p1:Value[.="1"]') = 1

Instead of looking for //d2p1:key[.="One"] and //d2p1:Value[.="1"] as two separate searches, do a single query that looks for both at once, like so:
//d2p1:KeyValueOfstringstring[./d2p1:Key="One"][./d2p1:Value=1]

Related

How to use JSON_MODIFY to change all of the keys in a column that has an array of JSON objects?

I have a column in my database that looks like this (3 separate rows of data)
Columns
[{"header":"C", "value":"A"},{"header":"D","value":"A2"},{"header":"E","value":"A3"}]
[{"header":"C", "value":"B"},{"header":"D","value":"B2"},{"header":"E","value":"B3"}]
[{"header":"C", "value":"C"},{"header":"D","value":"C2"},{"header":"E","value":"C3"}]
I want to null out all of the values of the "header" key and change the name to be test
I also want to change the name all of the "value"'s to be newHeader
I tried running a script like this to change all of the headers inside the array to be test but it doesn't expect the '*' character.
UPDATE Files
SET Columns = JSON_MODIFY(
JSON_MODIFY(Columns,'$.test', JSON_VALUE(Columns,'$[*].header')),
'$[*].header',
NULL
)
The end result I want to be like this:
Columns
[{"test":"", "newHeader":"A"},{"test":"","newHeader":"A2"},{"test":"","newHeader":"A3"}]
[{"test":"", "newHeader":"B"},{"test":"","newHeader":"B2"},{"test":"","newHeader":"B3"}]
[{"test":"", "newHeader":"C"},{"test":"","newHeader":"C2"},{"test":"","newHeader":"C3"}]

TSQL - Need to query a database column which is populated by XML

TSQL - Need to query a database column which is populated by XML.
The Database has an iUserID column with an Application ID and VCKey
TxtValue is the Column name and the contained Data is similar to this
<BasePreferencesDataSet xmlns="http://tempuri.org/BasePreferencesDataSet.xsd">
<ViewModesTable>
<iViewID>1</iViewID>
</ViewModesTable>
<ViewMode_PreferenceData>
<iViewID>1</iViewID>
<iDataID>0</iDataID>
<strValue>False</strValue>
</ViewMode_PreferenceData>
<ViewMode_PreferenceData>
<iViewID>1</iViewID>
<iDataID>5</iDataID>
<strValue>True</strValue>
</ViewMode_PreferenceData>
<ViewMode_PreferenceData>
<iViewID>1</iViewID>
<iDataID>6</iDataID>
<strValue>True</strValue>
</ViewMode_PreferenceData>
<ViewMode_PreferenceData>
<iViewID>1</iViewID>
<iDataID>4</iDataID>
<strValue>False</strValue>
I want to be able to identify any iUserID in which the StrValue for iDataID's 5 and 6 are not set to True.
I have attempted to use a txtValue Like % statement but even if I copy the contents and query for it verbatim it will not yield a result leading me to believe that the XML data cannot be queried in this manner.
Screenshot of Select * query for this DB for reference
You can try XML-method .exist() together with an XPath with predicates:
WITH XMLNAMESPACES(DEFAULT 'http://tempuri.org/BasePreferencesDataSet.xsd')
SELECT *
FROM YourTable
WHERE CAST(txtValue AS XML).exist('/BasePreferencesDataSet
/ViewMode_PreferenceData[iDataID=5 or iDataID=6]
/strValue[text()!="True"]')=1;
The namespace-declaration is needed to address the elements without a namespace prefix.
The <ViewMode_PreferenceData> is filtered for the fitting IDs, while the <strValue> is filtered for a content !="True". This will return any data row, where there is at least one entry, with an ID of 5 or 6 and a value not equal to "True".
So without sample data (including tags; sorry you're having trouble with that) it's tough to craft the complete query, but what you're looking for is XQuery, specifically the .exists method in T-SQL.
Something like
SELECT iUserID
FROM tblLocalUserPreferences
WHERE iApplicationID = 30
AND vcKey='MonitorPreferences'
AND (txtValue.exist('//iDataID[text()="5"]/../strValue[text()="True"]') = 0
OR txtValue.exist('//iDataID[text()="6"]/../strValue[text()="True"]')=0)
This should return all userID's where either iDataID 5 or 6 do NOT contain True. In other words, if both are true, you won't get that row back.

Get all entries for a specific json tag only in postgresql

I have a database with a json field which has multiple parts including one called tags, there are other entries as below but I want to return only the fields with "{"tags":{"+good":true}}".
"{"tags":{"+good":true}}"
"{"has_temps":false,"tags":{"+good":true}}"
"{"tags":{"+good":true}}"
"{"has_temps":false,"too_long":true,"too_long_as_of":"2016-02-12T12:28:28.238+00:00","tags":{"+good":true}}"
I can get part of the way there with this statement in my where clause trips.metadata->'tags'->>'+good' = 'true' but that returns all instances where tags are good and true including all entries above. I want to return entries with the specific statement "{"tags":{"+good":true}}" only. So taking out the two entries that begin has_temps.
Any thoughts on how to do this?
With jsonb column the solution is obvious:
with trips(metadata) as (
values
('{"tags":{"+good":true}}'::jsonb),
('{"has_temps":false,"tags":{"+good":true}}'),
('{"tags":{"+good":true}}'),
('{"has_temps":false,"too_long":true,"too_long_as_of":"2016-02-12T12:28:28.238+00:00","tags":{"+good":true}}')
)
select *
from trips
where metadata = '{"tags":{"+good":true}}';
metadata
-------------------------
{"tags":{"+good":true}}
{"tags":{"+good":true}}
(2 rows)
If the column's type is json then you should cast it to jsonb:
...
where metadata::jsonb = '{"tags":{"+good":true}}';
If I get you right, you can check text value of the "tags" key, like here:
select true
where '{"has_temps":false,"too_long":true,"too_long_as_of":"2016-02-12T12:28:28.238+00:00","tags":{"+good":true}}'::json->>'tags'
= '{"+good":true}'

Update all rows where contains 5 keys

I have Ticket table that has some columns like this :
ID : int
Body : nvarchar
Type : int
I have many rows where the Body column has value like this :
IPAddress = sometext, ComputerName = sometext , GetID = sometext, CustomerName=sometext-sometext , PharmacyCode = 13162900
I want update all rows' Type column where the Body column has at least five of the following keys:
IPAddress, ComputerName, GetID, CustomerName, PharmacyCode
You could do it with a simple update statement like that
UPDATE Ticket
SET Type = 4
WHERE Body LIKE '%IPAddress%'
and Body LIKE '%ComputerName%'
and Body LIKE '%GetID%'
and Body LIKE '%CustomerName%'
and Body LIKE '%PharmacyCode%'
if you know the 'keys' are always in the same order you could concatenate the LIKE conditions like so
UPDATE Ticket
SET Type = 4
WHERE Body LIKE '%IPAddress%ComputerName%GetID%CustomerName%PharmacyCode%'
If you have the possibility to change the data model it would be much better to explode this key & value column into an own table and link it back to this table as it is done in a proper relational model.
If you could calculate number of key value pair by number of = present in your string you could use this query
Update tblname set col=val where len(colname) - len(replace(colname,'=','')>5
The where part actually gives number of equal signs present in your string.

Check if a value exists in a collection stored in XML data type column

I have an XML data type column called "tags".
In that, I am storing a collection, like so:
<ArrayOfString>
<string>personal</string>
<string>travel</string>
<string>gadgets</string>
<string>parenting</string>
</ArrayOfString>
I want to select all the rows, that have one of the values that I am looking for: for example, I want to select all rows in the table that have a tag "travel".
I know that this works, if I know the index of the value I am looking for:
select * from posts
where tags.value('(/ArrayOfString/string)[1]', 'nvarchar(1000)') = 'travel'
but this query works only if the tag "travel" is the 2nd item in the nodes. How do I check if a value exists, irrespective of the position it is in?
select *
from tags
where tags.exist('/ArrayOfString/string[. = "travel"]') = 1
Or like this if you want to check against a variable.
declare #Val varchar(10)
set #Val = 'travel'
select *
from tags
where tags.exist('/ArrayOfString/string[. = sql:variable("#Val")]') = 1
You can try something like this:
SELECT
*
FROM
dbo.Posts
WHERE
tags.exist('/ArrayOfString/string/text()[. = "travel"]') = 1
This will list all the rows that have "travel" in one of the strings in your XML