I have the following string that I need to split from a field called symbols
234|23|HC
This is my current SQL statement
declare #t xml;
Set #t = (
Select symbols from tc for xml auto, elements)
Select #t;
which produces <symbols>234|23|HC</symbols>
but I need to split the string into child nodes so the result is like this:
<symbols>
<symbol>234</symbol>
<symbol>23</symbol>
<symbol>HC</symbol>
</symbols>
A replace version that takes care of the problem characters.
declare #T table(symbol varchar(50))
insert into #T values ('234|23|HC|Some problem chars <> &')
select cast('<symbols><symbol>'+
replace(cast(cast('' as xml).query('sql:column("symbol")') as varchar(max)),
'|',
'</symbol><symbol>')+
'</symbol></symbols> ' as xml)
from #T
Result:
<symbols>
<symbol>234</symbol>
<symbol>23</symbol>
<symbol>HC</symbol>
<symbol>Some problem chars <> &</symbol>
</symbols>
Related
I have some data that I'm looking at that has text formatting stored within a NTEXT field.
Happy enough with SQL Replace to remove data of a known length and format, however there are some fields with what looks like colour formatting and I'm trying to find a way to remove these.
An example of the data below, however (if possible) I would like to be able to remove whatever numbers follow the colours in the data but can't see how to introduce a wildcard into the replace statement.
Something like '\red***\green\***\blue***' as per Excel, but this doesn't work in Sql Server.
declare #str varchar(1500) = '\red3\green73\blue125;Jimmy Jazz\red31\green73\blue125;'
select #str,
replace(#str,'\red31\green73\blue125;','')
Any pointers would be gratefully received, thanks in advance.
Based on your sample data it would appear that you only need to remove the numbers in your string you can use patreplace8k or using patextract8K. Note the sample data and examples below:
-- Sample data
DECLARE #strings TABLE(stringId INT IDENTITY, string VARCHAR(100));
INSERT #strings VALUES('DeepPurple1978\yellow2\red009;pink\black3322'),
('red202\yellow5\red009;hotpink2'),('purple999\gray65\violet;blue\yellow381');
--==== Solution #1 Patreplace8k
SELECT
s.stringId,
pr.newString
FROM #strings AS s
CROSS APPLY samd.patReplace8K(s.string,'[0-9]','') AS pr;
--==== Solution #2 PatExtract8k + STRING_AGG (SQL 2017+)
SELECT
s.stringId,
NewString = STRING_AGG(pe.Item,'') WITHIN GROUP (ORDER BY pe.ItemNumber)
FROM #strings AS s
CROSS APPLY samd.patExtract8K(s.string,'[0-9]') AS pe
GROUP BY s.stringId;
--==== Solution #3 PatExtract8k + XML Concatination (Pre SQL 2017\)
SELECT
s.stringId,
NewString =
(
SELECT pe.item+''
FROM #strings AS s2
CROSS APPLY samd.patExtract8K(s2.string,'[0-9]') AS pe
WHERE s.stringId = s2.stringid
ORDER BY pe.itemNumber
FOR XML PATH('')
)
FROM #strings AS s
GROUP BY s.stringId;
Each of these solutions return:
stringId NewString
----------- -------------------------------------
1 DeepPurple\yellow\red;pink\black
2 red\yellow\red;hotpink
3 purple\gray\violet;blue\yellow
The second and third leverage concatenation, the second compatible with SQL Server 2017+ the third works with earlier versions (you did not include what version you are on.)
To only strip the numbers that follow one or more pre-defined colors you could use patternsplitCM. Note the use of a table with a group of colors your are seeking; in the real world I'd use a real table.
-- Colors
DECLARE #colors TABLE(color VARCHAR(20) PRIMARY KEY);
INSERT #colors VALUES('red'),('green'),('blue'),('yellow'),('purple'),('grey');
-- Sample data
DECLARE #strings TABLE(stringId INT IDENTITY, string VARCHAR(100));
INSERT #strings VALUES('Burger1978\yellow2\red009;pink\86thisfool'),
('red202\yellow5\red009;Freddy99'),('green999\grey65\violet;blue\yellow381');
SELECT
s.stringId, s.string, NewString =
(
SELECT
(
SELECT SUBSTRING(f.Item, IIF(f.M=0 AND EXISTS (SELECT c.Color FROM #colors AS c
WHERE c.Color = f.L),NULLIF(PATINDEX('%[^0-9]',f.item),0),1),8000)
FROM
(
SELECT ps.ItemNumber, ps.Item, ps.[Matched],
LAG(ps.Item,1,ps.Item) OVER (ORDER BY ps.ItemNumber)
FROM dbo.PatternSplitCM(s.string,'[^0-9\ ;]') AS ps
) AS f(ItemNumber,Item,M,L)
ORDER BY f.ItemNumber
FOR XML PATH(''), TYPE
).value('(text())[1]','varchar(8000)')
)
FROM #strings AS s;
Returns:
stringId string NewString
----------- --------------------------------------------- ----------------------------------------
1 Burger1978\yellow2\red009;pink\86thisfool Burger1978\yellow\red;pink\86thisfool
2 red202\yellow5\red009;Freddy99 red\yellow\red;Freddy99
3 green999\grey65\violet;blue\yellow381 green\grey\violet;blue\yellow
This might be a simple question for those who are experienced in working with JSON in SQL Server. I found this interesting way of aggregating strings using FOR XML in here.
create table #t (id int, name varchar(20))
insert into #t
values (1, 'Matt'), (1, 'Rocks'), (2, 'Stylus')
select id
,Names = stuff((select ', ' + name as [text()]
from #t xt
where xt.id = t.id
for xml path('')), 1, 2, '')
from #t t
group by id
How can I do the same using JSON instead of XML?
You cannot replace the XML approach with JSON. This string concatenation works due to some XML inner peculiarities, which are not the same in JSON.
Starting with SQL Server 2017 onwards you can use STRING_AGG(), but with earlier versions, the XML approach is the way to go.
Some background and a hint
First the hint: The code you showed is not safe for the XML special characters. Check my example below.
First I declare a simple XML
DECLARE #xml XML=
N'<a>
<b>1</b>
<b>2</b>
<b>3</b>
<c>
<d>x</d>
<d>y</d>
<d>z</d>
</c>
</a>';
--The XPath . tells the XML engine to use the current node (and all within)
--Therefore this will return any content within the XML
SELECT #xml.value('.','varchar(100)')
--You can specify the path to get 123 or xyz
SELECT #xml.query('/a/b').value('.','varchar(100)')
SELECT #xml.query('//d').value('.','varchar(100)')
Now your issue to concatenate tabular data:
DECLARE #tbl TABLE(SomeString VARCHAR(100));
INSERT INTO #tbl VALUES('This'),('will'),('concatenate'),('magically'),('Forbidden Characters & > <');
--The simple FOR XML query will tag the column with <SomeString> and each row with <row>:
SELECT SomeString FROM #tbl FOR XML PATH('row');
--But we can create the same without any tags:
--Attention: Look closely, that the result - even without tags - is XML typed and looks like a hyper link in SSMS.
SELECT SomeString AS [*] FROM #tbl FOR XML PATH('');
--Now we can use as a sub-select within a surrounding query.
--The result is returned as string, not XML typed anymore... Look at the forbidden chars!
SELECT
(SELECT SomeString FROM #tbl FOR XML PATH('row'))
,(SELECT SomeString AS [*] FROM #tbl FOR XML PATH(''))
--We can use ,TYPE to enforce the sub-select to be treated as XML typed itself
--This allows to use .query() and/or .value()
SELECT
(SELECT SomeString FROM #tbl FOR XML PATH('row'),TYPE).query('data(//SomeString)').value('.','nvarchar(max)')
,(SELECT SomeString AS [*] FROM #tbl FOR XML PATH(''),TYPE).value('.','nvarchar(max)')
XQuery's .data() can be used to concatenate named elements with blanks in between.
XQuery's .value() must be used to re-escpae forbidden characters.
I have a stored procedure in which i declared a variable and getting multiple values in it (separated with comma)
I want to check if it contains a specific number.If contains then run other queries.
Thanks in Advance :)
Using Aaron Bertrand's split string function you can do something like this.
DECLARE #CSVString NVARCHAR(MAX) = '13,4325,345,987, 432';
DECLARE #Id_To_Find INT = 4325
;WITH cteIdSplit
AS(
SELECT
CAST([Value] AS INT) 'Id'
FROM
dbo.FN_SplitString_AB (#CSVString, ',') A
WHERE
vn = 1
)
SELECT * FROM cteIdSplit S WHERE S.Id = #Id_To_Find;
I want to get the actual text instead of ??? result .
using sql - mssql 2005
I already tried collation and it didn't work
truncate table EPGXML
INSERT INTO EPGXML
( Data
)
SELECT CONVERT(XML,BulkColumn,2)
FROM OPENROWSET(BULK N'C:\test\test.xml', SINGLE_BLOB) O;
DECLARE #Data XML;
SELECT #Data = Data FROM EPGXML ;
select distinct filenode.value('category[1]', 'VARCHAR(300)')
FROM #Data.nodes('tv/programme ') files ( filenode )
where filenode.value('category[1]', 'VARCHAR(300)') is not null
Are you trying to read unicode text? Use NVARCHAR instead of VARCHAR for unicode support.
I'm using a SQL Server query to fetch the column information. But I need some information which is after 3rd and 4th occurrence in that particular column
Here is my sample data
[xxxxxxx||gh||vbh||CAPACITY_CPU||aed]
[qwe34||asdf||qwe||CONNECTIVITY||ghj]
[ertgfy||fgv||yuhjj||ACCESS||rty]
[tyhuj||rtg||qwert||ACCESS||TMW]
I'm looking for the data information after 3rd and 4th occurrence of ||
Something like
Capacity_CPU
CONNECTIVITY
ACCESS
My source column is not specific length, it will vary in the length
Use PATINDEX
create regex for the column that you need, then use SUBSTRING to extract the string that you want
You can use mixture of SUBSTRING, CHARINDEX, LEFT AND RIGHT Function. The best solution is you have to play with this function.
`
Create table #t( Name varchar(200))
Insert into #t
values
('[xxxxxxx||gh||vbh||CAPACITY_CPU||aed]'),
('[qwe34||asdf||qwe||CONNECTIVITY||ghj]'),
('[ertgfy||fgv||yuhjj||ACCESS||rty]'),
('[tyhuj||rtg||qwert||ACCESS||TMW]')
Select * from #t
Select
name,
Right(LEFT(name,len(name)-6),charindex('||',reverse(LEFT(name,len(name)-7))))
From #t
`
1) Instead of trying to do such operations with those strings you could normalize database by designing and adding a new table. In this case, you would need a simple SELECT:
SELECT Column4
FROM dbo.Table;
2) Otherwise, one solution is to convert those strings into XML and to use nodes and value XML methods:
DECLARE #Source NVARCHAR(MAX);
SET #Source =
N'[xxxxxxx||gh||vbh||CAPACITY_CPU||aed]
[qwe34||asdf||qwe||CONNECTIVITY||ghj]
[ertgfy||fgv||yuhjj||ACCESS||rty]
[tyhuj||rtg||qwert||ACCESS||TMW]';
DECLARE #EncodedSource NVARCHAR(MAX);
SET #EncodedSource = (SELECT #source FOR XML PATH(''));
DECLARE #x XML;
SET #x = REPLACE(REPLACE(REPLACE(#EncodedSource, N'[', N'<row> <col>'), N']', N'"</col> </row>'), N'||', N'</col> <col>');
SELECT r.XmlContent.value('(col[1]/text())[1]', 'NVARCHAR(100)') AS Col1,
r.XmlContent.value('(col[4]/text())[1]', 'NVARCHAR(100)') AS Col4
FROM #x.nodes('/row') r(XmlContent);
Note: you need to replace NVARCHAR(length) with the proper data type and max. length.