SQL query update nvarchar with XML contents add missing attribute to node - sql

I have an nvarchar column with XML contents. Not sure if it needs to be cast to XML or not. All the XML content is formatted the same except a number of records need to be modified where the first node needs an attribute added to it:
<MyFirstNode SomeAttribute="value" SomeOtherAttribute="anothervalue">
update to:
<MyFirstNode SomeAttribute="value" AddThisAttribute="nicevalue" SomeOtherAttribute="anothervalue">
How can i update all the required nodes with the AddThisAttribute="nicevalue" ? All need the same attribute and value.

If this is actually going to be interpreted as XML, you don't neccessarily have to insert AddThisAttribute="nicevalue" in the middle of the list of attributes; one option is to do a simple
UPDATE myTable
SET XMLColumn = REPLACE(XMLColumn, '<MyFirstNode ', '<MyFirstNode AddThisAttribute="nicevalue" ')
This might be easier than trying to insert the value, especially if SomeAttribute and SomeOtherAttribute change in each row.
Alternately,
UPDATE myTable
SET XMLColumn = REPLACE(XMLColumn, ' SomeOtherAttribute=', ' AddThisAttribute="nicevalue" SomeOtherAttribute=')
could work, though if "SomeOtherAttribute" appears in other node types other than MyFirstNode, this might make undesired changes.

Related

SQL - How to check if XML field is damaged/ valid before selecting

I have a scenario where a table, containing an XML column, will occasionally contain XML data which displays the following error when trying to select it:
"Msg 6611, Level 16, State 1, Line 889
The XML data type is damaged."
I have pin pointed the record with the issue, but it's not something I want to correct at this time but I instead want my select statement to still select data from the table without the whole thing falling over. If there was a way to use a function to check whether the XML is damaged/ valid, if so select a blank value, then this would be my preferred option or if not perhaps just exclude the troublesome record from the results.
Hopefully this made sense and thank you in advance!
Example query which errors (If I exclude the XML column, the query works):
SELECT
T.ID,
T.XML
FROM
TableName T
Ultimatley, this is the kind of query I'm trying to run (I'd like the value field to display as blank if the XML is damaged):
SELECT
T.ID,
t.c.value ('(Value)[1]', 'varchar(50)') AS Value
FROM
TableName T
CROSS APPLY DocumentXML.nodes('//XMLNode') t(c)

Retrieving multiple xml child node values

I have a column of type varchar(max) populated with xml nodes and values; as an example, the column data starts with <tag1> <tag2>value1</tag2><tag3>value2</tag3>... </tag1>. What I need to get out of this string is "value1 value2 value3... valueN" within one cell for every row in the table using static SQL or a stored procedure. The node tree isn't always the same, sometimes the path is <tagX><tagY>valueY</tagY>...</tagX>.
All of my experience with shredding xml is only used to get one specific value, property, or tag, not all values while retaining the column and row count. Currently I query then loop through the result set on my product's end and shred everything, but that's no longer an option due to recent changes.
It's possible to change the column to be of type xml, but if possible I'd like to avoid having to do so.
Cast the column to XML (or change it in the table to XML) and shred the xml on //* to get all nodes in a table. Then you can use for xml path to concat the values back together.
select (
select ' '+X.N.value('text()[1]', 'varchar(max)')
from (select cast(T.XMLCol as xml)) as T1(XMLCol)
cross apply T1.XMLCol.nodes('//*') as X(N)
for xml path(''), type
).value('substring(text()[1], 2)', 'varchar(max)')
from T
SQL Fiddle

Replacing JSON Formatted String in SQL

I have a something like this in my table column:
{"InputDirection":0,"Mask":"AA","FormatString":null,"AutoCompleteValue":null,
"Filtered":"0123456789","AutoComplete":false,"ReadOnly":true}
What I want to do is to change A to N in "Mask":"AA" and remove "Filtered":"0123456789" if they exist. Mask could be in different forms like A9A, 'AAAA`, etc.
If it was in C# I could do it by myself by parsing it to JSON, etc but I need to do it within SQL.
I've found this article which shows how to parse JSON to Table. This gave me an idea that I can parse each field to temp table and make the changes on that and convert it back to JSON so update the actual field where I take this JSON field from. However, this looks like a cumbersome process for both me and the server.
Any better ideas?
You can use this LINK .
And then use the following code
select * into #demo from
(Select * from parseJSON('{"InputDirection":0,"Mask":"AA","FormatString":null,"AutoCompleteValue":null,
"Filtered":"0123456789","AutoComplete":false,"ReadOnly":true}
')) a
select * from #demo
--- CHANGE THE DATA HERE AS REQUIRED
DECLARE #MyHierarchy JSONHierarchy;
INSERT INTO #myHierarchy
select * from #demo;
-- USE THIS VALUE AND UPDATE YOUR JSON COLUMN
SELECT dbo.ToJSON(#MyHierarchy)
drop table #demo
I may be getting something wrong here but why can’t you simply use REPLACE to update what’s needed and LIKE to identify JSON strings that should be updated?
update table_T
set json_string = REPLACE(json_string, '"Filtered":"0123456789",', '')
where json_string like '%"Mask":"AA"%'
Not sure I understand why do you need to parse it….

How to store and extract XML information from an nvarchar(max) type column, and use it in joins?

I have a column of type 'nvarchar(max)' that should now hold XML information instead of just a string.
Say: col1 has value 'abc'
Now it has values, with additional info:
<el1>abc</el2>
<el2>someotherinfo</el2>
Storing the information to the column is fine, since it can still be pushed in as a string.
However, extracting the same information and also using/replacing the same information 'abc' from this column that is being used in various other joins from other tables, is something I'm not able to figure out.
how can I also push in this information into abcd when it comes from another table's value 'abcd' without losing other information?
I am building an XML from the application side and updating it in a column of type nvarchar(). All the columns have been replaced to hold the XML, so the safe assumption is that the col1 only holds XML similar to that mentioned above. Just push the XML as is and it works fine. However, how should I extract the information to use it in joins?
How do I extract a particular element from this nvarchar() string to use it in a join??
Previously, this column 'Col1' was just used as a string, and a check was done like this:
where tablex.colx = table1.col1
or
Update Table2 where
Once you cast the NVARCHAR data to the XML data type, you can use XML functions to get element/attribute values for joining to:
WITH xoutput AS (
SELECT CONVERT(xml, t.nvarchar_column) AS col
FROM YOUR_TABLE t)
SELECT x.*
FROM TABLE x
JOIN xoutput y ON y.col.value('(/path/to/your/element)[1]', 'int') = x.id
It won't be able to use indexes, because of the data type conversion...
Alternate version, using IN:
WITH xoutput AS (
SELECT CONVERT(xml, t.nvarchar_column) AS col
FROM YOUR_TABLE t)
SELECT x.*
FROM TABLE x
WHERE x.id IN (SELECT y.col.value('(/path/to/your/element)[1]', 'int')
FROM xoutput)

Use a LIKE statement on SQL Server XML Datatype

If you have a varchar field you can easily do SELECT * FROM TABLE WHERE ColumnA LIKE '%Test%' to see if that column contains a certain string.
How do you do that for XML Type?
I have the following which returns only rows that have a 'Text' node but I need to search within that node
select * from WebPageContent where data.exist('/PageContent/Text') = 1
Yet another option is to cast the XML as nvarchar, and then search for the given string as if the XML vas a nvarchar field.
SELECT *
FROM Table
WHERE CAST(Column as nvarchar(max)) LIKE '%TEST%'
I love this solution as it is clean, easy to remember, hard to mess up, and can be used as a part of a where clause.
This might not be the best performing solution, so think twice before deplying it to production. It is however very usefull for a quick debug session, which is where I mostly use it.
EDIT: As Cliff mentions it, you could use:
...nvarchar if there's characters that don't convert to varchar
You should be able to do this quite easily:
SELECT *
FROM WebPageContent
WHERE data.value('(/PageContent/Text)[1]', 'varchar(100)') LIKE 'XYZ%'
The .value method gives you the actual value, and you can define that to be returned as a VARCHAR(), which you can then check with a LIKE statement.
Mind you, this isn't going to be awfully fast. So if you have certain fields in your XML that you need to inspect a lot, you could:
create a stored function which gets the XML and returns the value you're looking for as a VARCHAR()
define a new computed field on your table which calls this function, and make it a PERSISTED column
With this, you'd basically "extract" a certain portion of the XML into a computed field, make it persisted, and then you can search very efficiently on it (heck: you can even INDEX that field!).
Marc
Another option is to search the XML as a string by converting it to a string and then using LIKE. However as a computed column can't be part of a WHERE clause you need to wrap it in another SELECT like this:
SELECT * FROM
(SELECT *, CONVERT(varchar(MAX), [COLUMNA]) as [XMLDataString] FROM TABLE) x
WHERE [XMLDataString] like '%Test%'
This is what I am going to use based on marc_s answer:
SELECT
SUBSTRING(DATA.value('(/PAGECONTENT/TEXT)[1]', 'VARCHAR(100)'),PATINDEX('%NORTH%',DATA.value('(/PAGECONTENT/TEXT)[1]', 'VARCHAR(100)')) - 20,999)
FROM WEBPAGECONTENT
WHERE COALESCE(PATINDEX('%NORTH%',DATA.value('(/PAGECONTENT/TEXT)[1]', 'VARCHAR(100)')),0) > 0
Return a substring on the search where the search criteria exists