T-SQL: Count Numbers of semicolons before expression - sql

I got a table with strings that look like that:
'9;1;test;A;11002'
How would I count how many semicolons are there before the 'A'?
Cheers!

Using string functions
select len(left(str,charindex(str,'A')) - len(replace(left(str,charindex(str,'A'), ';', '')) n
from tbl

Hint1: The whole issue has some smell... You should not store your data as CSV string. But sometimes we have to work with what we have...
Hint2: The following needs SQL-Server v2016. With an older version we'd need to do something similar based on XML.
Try this:
--A declared table to mockup your issue
DECLARE #tbl TABLE(ID INT IDENTITY, YourCSVstring VARCHAR(100));
INSERT INTO #tbl(YourCSVstring)
VALUES('9;1;test;A;11002');
--the query
SELECT t.ID
,A.*
FROM #tbl t
CROSS APPLY OPENJSON(CONCAT(N'["',REPLACE(t.YourCSVstring,';','","'),N'"]')) A;
The idea in short:
We use some replacements to translate your CSV-string to a JSON array.
Now we can use use OPENJSON() to read it.
The value is the array item, the key its zero-based index.
Proceed with this however you need it.
Just to give you some fun: You can easily read the CSV type-safe into columns by doubling the [[ and using WITH to specify your columns:
SELECT t.ID
,A.*
FROM #tbl t
CROSS APPLY OPENJSON(CONCAT(N'[["',REPLACE(t.YourCSVstring,';','","'),N'"]]'))
WITH(FirstNumber INT '$[0]'
,SecondNumber INT '$[1]'
,SomeText NVARCHAR(100) '$[2]'
,YourLetterA NVARCHAR(100) '$[3]'
,FinalNumber INT '$[4]')A
returns:
ID FirstNumber SecondNumber SomeText YourLetterA FinalNumber
1 9 1 test A 11002

Related

Replace string without fixed length

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

Is there any way to convert varchar to int value in sql?

I have a query like this when I pass the values into in operator in sql it shows:
Conversion failed when converting the varchar value '3,4,9' to data type int.
How can I solve the issue?
declare #values varchar(100)
set #values = '3,4,9'
select #values
select * from CmnItemType where ItemTypeID in (#values)
No. You can use string_split() or a similar user-defined function:
where itemtypeid in (select try_convert(int, value) from string_split(#values))
What I usually do is use table variable, like this one:
DECLARE #values TABLE (id INT)
INSERT INTO #values (id) VALUES (3),(4),(9)
SELECT id FROM #values
From that, you could simply do a join to your tables.
If you are creating a stored procedure, you can use a TVP to pass parameters, here is the Microsoft doc on that. With a TVP, your code can simply call your SP with a list and you will be able to join it in the SP.
Hope this will help.

Optimization of a substring query with charindex to trim the left part of a string

I need to get a substring of xyzdf/1234 resulting in 1234 (i.e. trimming the left part of the slash / ) . I have used
substring('xyzdf/1234',charindex('/','xyzdf/1234')+1,len('xyzdf/1234')-charindex('/','xyzdf/1234'))
which works but it is repetitive...
then I have used this way:
stuff('xyzdf/1234',1,charindex('/','xyzdf/1234'),'') and it works too and it is more compact, but still repeats the same argument twice 'xyzdf/1234'.
I wonder what would be the faster way to trim the left part. I will need to clean data in one column for million records. Not sure if the stuff command is faster enough. (Mind you it is a bulk operation). Thanks!
You could select the string from a VALUES.
That way you can repeat the value without double hardcoding it.
Then get the right part with the number from it.
F.e. using RIGHT, CHARINDEX, REVERSE and VALUES:
select right(val, charindex('/',reverse(val))-1) as nr
from (values ('xyzdf/1234')) q(val);
Or use SUBSTRING, CHARINDEX, LEN and VALUES:
select substring(val,charindex('/',val)+1,len(val)) as nr
from (values ('xyzdf/1234')) q(val);
Or abuse PARSENAME:
select parsename(replace('xyzdf/1234','/','.'),1) as nr;
Or use variables:
declare #value varchar(30) = 'xyzdf/1234';
declare #nr int = right(#value, charindex('/',reverse(#value))-1);
select #nr as nr;
But if the intention is to update a column so that only the number remains?
Then using the SUBSTRING method is probably still the safest.
Because it would keep those without / untouched, and without crashing on an Invalid length parameter passed error.
Example:
declare #Table table (id int identity(1,1) primary key, col1 varchar(30));
insert into #Table (col1) values
('xyzdf/1234'),
('12345');
update #Table
set col1 = substring(col1,charindex('/',col1)+1,len(col1))
where col1 like '%/[0-9]%';
select * from #Table;

How to manipulate comma-separated list in SQL Server

I have a list of values such as
1,2,3,4...
that will be passed into my SQL query.
I need to have these values stored in a table variable. So essentially I need something like this:
declare #t (num int)
insert into #t values (1),(2),(3),(4)...
Is it possible to do that formatting in SQL Server? (turning 1,2,3,4... into (1),(2),(3),(4)...
Note: I can not change what those values look like before they get to my SQL script; I'm stuck with that list. also it may not always be 4 values; it could 1 or more.
Edit to show what values look like: under normal circumstances, this is how it would work:
select t.pk
from a_table t
where t.pk in (#place_holder#)
#placeholder# is just a literal place holder. when some one would run the report, #placeholder# is replaced with the literal values from the filter of that report:
select t.pk
from a_table t
where t.pk in (1,2,3,4) -- or whatever the user selects
t.pk is an int
note: doing
declare #t as table (
num int
)
insert into #t values (#Placeholder#)
does not work.
Your description is a bit ridicuolus, but you might give this a try:
Whatever you mean with this
I see what your trying to say; but if I type out '#placeholder#' in the script, I'll end up with '1','2','3','4' and not '1,2,3,4'
I assume this is a string with numbers, each number between single qoutes, separated with a comma:
DECLARE #passedIn VARCHAR(100)='''1'',''2'',''3'',''4'',''5'',''6'',''7''';
SELECT #passedIn; -->: '1','2','3','4','5','6','7'
Now the variable #passedIn holds exactly what you are talking about
I'll use a dynamic SQL-Statement to insert this in a temp-table (declared table variable would not work here...)
CREATE TABLE #tmpTable(ID INT);
DECLARE #cmd VARCHAR(MAX)=
'INSERT INTO #tmpTable(ID) VALUES (' + REPLACE(SUBSTRING(#passedIn,2,LEN(#passedIn)-2),''',''','),(') + ');';
EXEC (#cmd);
SELECT * FROM #tmpTable;
GO
DROP TABLE #tmpTable;
UPDATE 1: no dynamic SQL necessary, all ad-hoc...
You can get the list of numbers as derived table in a CTE easily.
This can be used in a following statement like WHERE SomeID IN(SELECT ID FROM MyIDs) (similar to this: dynamic IN section )
WITH MyIDs(ID) AS
(
SELECT A.B.value('.','int') AS ID
FROM
(
SELECT CAST('<x>' + REPLACE(SUBSTRING(#passedIn,2,LEN(#passedIn)-2),''',''','</x><x>') + '</x>' AS XML) AS AsXml
) as tbl
CROSS APPLY tbl.AsXml.nodes('/x') AS A(B)
)
SELECT * FROM MyIDs
UPDATE 2:
And to answer your question exactly:
With this following the CTE
insert into #t(num)
SELECT ID FROM MyIDs
... you would actually get your declared table variable filled - if you need it later...

How to get the data between mth and nth occurrence in a string

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.