How to use substring in SQL Server - sql

Suppose I have this query.
SELECT
proj.refno [Reference No.],
proj.projname [NNNN],
TotalCost= '$' + CONVERT(NVARCHAR(100),cast(ROUND((cast(ship.volfinish as int) * data.price)/1000,2) as decimal(5,2)))
FROM
projects proj
INNER JOIN
projdata data ON proj.controlno = data.controlno
INNER JOIN
shipment ship ON data.ctrlno = ship.dctrlno
WHERE
proj.refno IN ('item1', 'item2','item3')
ORDER BY
proj.refno
with this output:
Reference No. NNNN TotalCost
GR-NFS52 abc123 StudentsTitle123 (NNNN: xxxxxxxxxxxxx) $215.45
GR-PFS53 def456 StudentsTitle456 (NNNN: xxxxxxxxxxxxx) $259.55
GR-SSFS43 ghi789 StudentsTitle789 (NNNN: xxxxxxxxxxxxx) $242.35
How can I make the NNNN column used the substring function with this output. Cause I'm not into t-sql.
NNNN
xxxxxxxxxxxxx
xxxxxxxxxxxxx
xxxxxxxxxxxxx

Assuming you have pattern like NNNN: xxxxxxxxxxx) in your strings you can extract this number using some simple manipulation over the string value using charindex and substring:
declare #str nvarchar(max)
select #str = 'Students (NNNN: 9781410314291)'
select substring(#str,
charindex('ISBN:', #str) + 6,
charindex(')', #str, charindex('NNNN:', #str)) - charindex('NNNN:', #str) - 6)
Here we first find position of NNNN: substring, then position of first occurence of closing bracket ) after this substing and taking part of string between these positions - it is exactly number you need.
In your particular case you can use outer apply in select query in order to make it more readable by avoiding multiple copy-pasting the same charindex('NNNN:', proj.projname) expression:
select
proj.refno [Reference No.],
substring(proj.projname,
CALC.pos_from,
charindex(')', proj.projname, CALC.pos_from) - CALC.pos_from - 6) as [NNNN],
....
FROM projects proj
.....
outer apply (select charindex('NNNN:', proj.projname) as pos_from) as CALC

Try this:
DECLARE #str nvarchar(max) = 'Novels for Students, vol. 52 (ISBN: 9781410314291)'
SELECT
REPLACE(STUFF(#str, 1, PATINDEX('% '+REPLICATE('[0-9]', 13) + '%', #str), ''), ')', '')
Result:
9781410314291

Related

How to pull out information from a long string of data

I have this data point:
455-U-202007302233,455-L-202007302233,422-U-202008011052,422-L-202008011052,857-U-202008041142,857-L-202008061215
Column: ,[t810str]
How would I be able to modify column [t810str] in order to pull out the last comma set before 857?
Desired Result = 422-L-202008011052
First you need to implement some kind of splitter that respects ordinal position (STRING_SPLIT does not). I'm therefore going to make use of DelimitedSplit8k_LEAD. Then you can split the value, and use LAG to get the prior value. Finally you can filter on where the item has a value LIKE '857%' but the previous does not:
WITH CTE AS(
SELECT DS.Item,
LAG(DS.Item) OVER (PARTITION BY YourColumn ORDER BY DS.itemNumber) AS PrevItem
FROM (VALUES('455-U-202007302233,455-L-202007302233,422-U-202008011052,422-L-202008011052,857-U-202008041142,857-L-202008061215'))V(YourColumn)
CROSS APPLY dbo.DelimitedSplit8K_LEAD(V.YourColumn,',') DS)
SELECT C.PrevItem
FROM CTE C
WHERE C.Item LIKE '857%'
AND C.PrevItem NOT LIKE '857%';
Based on your data and the assumption that items are 18 characters (your data do not indicate otherwise):
DECLARE #t AS NVARCHAR(255) = '455-U-202007302233,455-L-202007302233,422-U-202008011052,422-L-202008011052,857-U-202008041142,857-L-202008061215';
SELECT RIGHT(LEFT(#t,CHARINDEX(',857',#t)-1),18)
Using cross apply (which you can also rewrite using a CTE or a subquery for readability). This removes everything after first occurrence of 857 and then grabs the last set that's left. So even if you have multiple 857 and varying length of delimited strings, this should work
select *, right(remind , charindex (',' ,reverse(remind))-1)
from t t1
cross apply (select stuff(col, charindex(',857',col), len(col),'') as remind) t2
DEMO
Another solution use a recursive CTE
DECLARE #Var VARCHAR(200) = '455-U-202007302233,455-L-202007302233,422-U-202008011052,422-L-202008011052,857-U-202008041142,857-L-202008061215';
WITH CTE AS
(
SELECT 0 N, LEFT(#Var, CHARINDEX(',', #Var)-1) Part,
RIGHT(#Var, LEN(#Var) - CHARINDEX(',', #Var)) Remind
UNION ALL
SELECT N + 1,
LEFT(Remind, CHARINDEX(',', Remind) - 1),
RIGHT(Remind, LEN(Remind) - CHARINDEX(',', Remind))
FROM CTE
WHERE CHARINDEX(',', Remind) <> 0
)
SELECT TOP 1 Part
FROM CTE
WHERE LEFT(Remind, 3) = '857'
ORDER BY N;
Demo
Implemented with string functions (and assuming your data items can have variable length :-) it might look a bit confusing (therefore I'd prefer #Larnu's answer):
DECLARE #string VARCHAR(2000) = '455-U-202007302233,455-L-202007302233,422-U-202008011052,422-L-202008011052,857-U-202008041142,857-L-202008061215'
SELECT SUBSTRING(#string, CHARINDEX(',857',#string) - CHARINDEX(',', REVERSE( LEFT(#string, PATINDEX('%,857%',#string) - 1)) ) + 1, CHARINDEX(',', REVERSE( LEFT(#string, PATINDEX('%,857%',#string) - 1)))-1 )
Parts of the latter separated:
DECLARE #string VARCHAR(2000) = '455-U-202007302233,455-L-202007302233,422-U-202008011052,422-L-202008011052,857-U-202008041142,857-L-202008061215'SELECT CHARINDEX(',857',#string)
SELECT LEFT(#string, PATINDEX('%,857%',#string) - 1)
SELECT REVERSE( LEFT(#string, PATINDEX('%,857%',#string) - 1) )
SELECT CHARINDEX(',', REVERSE( LEFT(#string, PATINDEX('%,857%',#string) - 1)) )

SQL Server : substring from specific position of '-' hyphen

I need to perform a substring operation in SQL Server and get a string from start of hyphen character (-).
My input string is :
'44345434595-E535-12349-5273-202003-16785'
and I want to extract the string from 4th instance of hyphen to 5th instance of hyphen and my desired result is : 202003
You can use string_split function in sqlserver
declare #str varchar(max)= '44345434595-E535-12349-5273-202003-16785'
select * from (select value, row_number() over (order by charindex('-' + value + '-', '-' + #str + '-')) rn
from string_split(#str, '-')) t1
where t1.rn in (5)
Since there's no guarantee of ordering using string_split function, we need to sort by position based on your -
row_number() over (order by charindex('-' + value + '-', '-' + #str + '-'))
If your input string has fixed format you can set position and lenght directly
SELECT SUBSTRING(yourColumn, 29, 5) FROM YourTable
If the Format is Fixed You can use Substring for that:
DECLARE #STR VARCHAR(100)
SELECT #STR='44345434595-E535-12349-5273-202003-16785'
SELECT SUBSTRING(#STR, 29, 6)
Output: 202003
Below is the code that might suit your requirement and will work if the input has exactly 5 hypen.
DECLARE #data varchar(50)= '44345434595-E535-12349-5273-202003-16785'
select
reverse(
SUBSTRING
(
SUBSTRING
(
reverse(#data),
charindex('-',reverse(#data),1)+1,
len(#data)
),
1,
charindex('-',SUBSTRING(reverse(#data),
charindex('-',reverse(#data),1)+1,
len(#data)))-1)
);

Find the count of words in string

SQL: How to find the count of words in following example?
declare #s varchar(55) = 'How to find the count of words in this string ?'
Subquestions:
How to count spaces?
How to count double/triple/... spaces as one? answer by Gordon Linoff here
How to avoid counting of special characters? Example: 'Please , don't count this comma'
Is it possible without string_split function (because it's available only since SQL SERVER 2016)?
Summary with the best solutions HERE
Thanks to Gordon Linoff's answer here
SELECT len(replace(replace(replace(replace(#s,' ','<>'),'><',''),'<>',' '),' ',','))
OutPut
-------
How,to,find,the,count,of,words,in,this,string?
SELECT replace(replace(replace(replace(replace(#s,' ','<>'),'><',''),'<>',' '),' ',','),',','')
OutPut
------
Howtofindthecountofwordsinthisstring?
Now you can find the difference between the length of both the output and add 1 for the last word like below.
declare #s varchar(55) = 'How to find the count of words in this string?'
SELECT len(replace(replace(replace(replace(#s,' ','<>'),'><',''),'<>',' '),' ',','))
-len(replace(replace(replace(replace(replace(#s,' ','<>'),'><',''),'<>',' '),' ',','),',',''))
+ 1 AS WORD_COUNT
WORD_COUNT
----------
10
http://sqlfiddle.com/#!18/06c1d/5
One method uses a recursive CTE:
declare #s varchar(55) = 'How to find the count of words in this string ?';
with cte as (
select convert(varchar(max), '') as word,
convert(varchar(max), ltrim(#s)) as rest
union all
select left(rest, patindex('%[ ]%', rest + ' ') - 1),
ltrim(stuff(rest, 1, patindex('%[ ]%', rest + ' '), ''))
from cte
where rest <> ''
)
select count(*)
from cte
where word not in ('', '?', ',')
--OPTION (MAXRECURSION 1000); -- use if number of words >99
;
Here is a db<>fiddle.
First thing is you need to remove the double/tripple.. or more count into one.
declare #str varchar(500) = 'dvdv sdd dfxdfd dfd'
select Replace(Replace(Replace( #str,' ',']['), '[]', ''), '][', ' ')
this will remove all the unnecessary space in between the word and you'll get your final word.
After that you may use string_split (for SQL SERVER 2016 and above). To count the number of word in your text from which minus 1 is your total count of spaces.
select count(value) - 1 from string_split( #str, ' ')
Final query looks like
declare #str varchar(500) = 'dvdv sdd dfxdfd dfd'
select count(value) - 1 from string_split( Replace(Replace(Replace( #str,' ',']['), '[]', ''), '][', ' '), ' ')
For only word count and if your MSSQL Version support STRING_SPLIT, you can use this simple script below-
DECLARE #s VARCHAR(55) = 'How to find the count of words in this string ?'
SELECT
COUNT(
IIF(
LTRIM(value)='',
NULL,
1
)
)
FROM STRING_SPLIT(#s, ' ')
WHERE value LIKE '%[0-9,A-z]%'
Using string_split (available only since SQL SERVER 2016):
declare #string varchar(55) = 'How to find the count of words in this string ?';
select count(*) WordCount from string_split(#string,' ') where value like '%[0-9A-Za-z]%'
The same idea is used in following answers:
https://stackoverflow.com/a/57783421/6165594
https://stackoverflow.com/a/57783743/6165594
Without using string_split:
declare #string varchar(55) = 'How to find the count of words in this string ?';
;with space as
( -- returns space positions in a string
select cast( 0 as int) idx union all
select cast(charindex(' ', #string, idx+1) as int) from space
where charindex(' ', #string, idx+1)>0
)
select count(*) WordCount from space
where substring(#string,idx+1,charindex(' ',#string+' ',idx+1)-idx-1) like '%[0-9A-Za-z]%'
OPTION (MAXRECURSION 0);
The same idea is used in following answers:
https://stackoverflow.com/a/57787850/6165594
As Inline Function:
ALTER FUNCTION dbo.WordCount
(
#string NVARCHAR(MAX)
, #WordPattern NVARCHAR(MAX) = '%[0-9A-Za-z]%'
)
/*
Call Example:
1) Word count for single string:
select * from WordCount(N'How to find the count of words in this string ? ', default)
2) Word count for set of strings:
select *
from (
select 'How to find the count of words in this string ? ' as string union all
select 'How many words in 2nd example?'
) x
cross apply WordCount(x.string, default)
Limitations:
If string contains >100 spaces function fails with error:
Msg 530, Level 16, State 1, Line 45
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
NB! OPTION (MAXRECURSION 0); -- don't work within inline function
*/
RETURNS TABLE AS RETURN
(
with space as
( -- returns space positions in a string
select cast( 0 as int) idx union all
select cast(charindex(' ', #string, idx+1) as int) from space
where charindex(' ', #string, idx+1)>0
)
select count(*) WordCount from space
where substring(#string,idx+1,charindex(' ',#string+' ',idx+1)-idx-1) like #WordPattern
-- OPTION (MAXRECURSION 0); -- don't work within inline function
);
go

How to get the middle word using Substring via Charindex of Second Position?

Basically what I am trying to do is that I want to get the middle word, using the second occurrence of the same character (on this case, dash "-").
This is the sample input:
declare #word nvarchar(max)
set #word = 'Technical Materials - Conversion - Team Dashboard'
There are three parts on this sentence, and they are divided by '-' dash line.
The first part is 'Technical Materials' which I am able to get using:
SELECT LTRIM(RTRIM(SUBSTRING(#word, 0, CHARINDEX('-', #word, 0))))
The last set was 'Team Dashboard' which I am able to get using:
SELECT CASE WHEN LEN(#word) - LEN(REPLACE(#word, '-', '')) = 1
THEN NULL
ELSE
RIGHT(#word,CHARINDEX('-', REVERSE(#word))-1)
END
The problem was, I am having a hard time getting the middle words which is 'Conversion' in this example.
If the format is fixed, you can use PARSENAME to achieve your expectation:
DECLARE #Word AS NVARCHAR(MAX) = 'Technical Materials - Conversion - Team Dashboard'
SELECT PARSENAME(REPLACE(#Word, '-', '.'), 2)
if you want to trim the extra spaces, then:
SELECT LTRIM(RTRIM(PARSENAME(REPLACE(#Word, '-', '.'), 2)))
Try this query:
SELECT
SUBSTRING(#word,
CHARINDEX('-', #word) + 2,
CHARINDEX('-', #word, CHARINDEX('-', #word) + 1) -
CHARINDEX('-', #word) - 3)
FROM yourTable
The general strategy here is to use SUBSTRING(), which requires the starting and ending positions of the middle string in question. We can use CHARINDEX to find both the first and second dash in the string. From this, we can compute the positions of the middle substring we want.
Demo here:
Rextester
This will find the text between the first 2 occurrences of '-'
DECLARE #word nvarchar(max)
SET #word = 'Technical Materials - Conversion - Team Dashboard'
SELECT SUBSTRING(x, 0, charindex('-', x))
FROM (values(stuff(#word, 1, charindex('-', #word), ''))) x(x)
This will find the middle element. In case of an even number of elements it will pick the first of the 2 middle elements
DECLARE #word nvarchar(max)
SET #word = 'Technical Materials - Conversion - Team Dashboard'
;WITH CTE(txt, rn, cnt) as
(
SELECT
t.c.value('.', 'VARCHAR(2000)'),
row_number() over (order by (select 1)), count(*) over()
FROM (
SELECT x = CAST('<t>' +
REPLACE(#word, ' - ', '</t><t>') + '</t>' AS XML)
) a
CROSS APPLY x.nodes('/t') t(c)
)
SELECT txt
FROM CTE
WHERE (cnt+1) / 2 = rn

Formatting a number as a monetary value including separators

I need some help with a sql transformation. This part of query that I have been provided with:
'$' + replace(cast((CAST(p.Price1 AS decimal(10,2)) * cast(isnull(p.Multiplier,1) as decimal(10,2))) as varchar), '.0000', '')
Basically, it ends up being a varchar that looks like this: $26980
I need to insert a comma at the thousand and million mark (if applicable). So in this instance, $26,980
What's the easiest way to do that without having to rewrite the whole thing?
Do it on the client side. Having said that, this example should show you the way.
with p(price1, multiplier) as (select 1234.5, 10)
select '$' + replace(cast((CAST(p.Price1 AS decimal(10,2)) * cast(isnull(p.Multiplier,1) as decimal(10,2))) as varchar), '.0000', ''),
'$' + parsename(convert(varchar,cast(p.price1*isnull(p.Multiplier,1) as money),1),2)
from p
The key is in the last expression
'$' + parsename(convert(varchar,cast(p.price1*isnull(p.Multiplier,1) as money),1),2)
Note: if p.price1 is of a higher precision than decimal(10,2), then you may have to cast it in the expression as well to produce a faithful translation since the original CAST(p.Priced1 as decimal(10,2)) will be performing rounding.
If you really must do it in TSQL you can use CONVERT(), but this sort of thing really doesn't belong in the database:
declare #m money = 12345678
-- with decimal places
select '$' + convert(varchar, #m, 1)
-- without decimal places
select '$' + replace(convert(varchar, #m, 1), '.00', '')
You could turn this into a function, it only goes 50 characters back.
DECLARE #input VARCHAR(50)
SELECT #input = '123123123.00'
SELECT #input = CASE WHEN CHARINDEX('.', #input) > offset +1
THEN STUFF(#input, CHARINDEX('.', #input) - offset, 0, ',')
ELSE #input END
FROM (SELECT 3 offset UNION SELECT 7 UNION SELECT 12 UNION SELECT 18 UNION SELECT 25 UNION SELECT 33 UNION SELECT 42) b
PRINT #input
The offset grows by +1 for each position, because it's assuming you've already inserted the commas for the previous positions.