How to get expression from string - sql

Here is the string :'(a+b)+(x/y)*1000'
from that string i want to get '(x/y)' meaning i want the part that contains the division to check later if denominator <> 0 to avoid division by zero.
The string formula can vary but divisions are always between parenthesis.
How can i achieve that in sql ?

Bits that it appears you already have (based on a comment you made)...
Pos of the '/' = CHARINDEX('/', yourString)
Pos of the ')' = CHARINDEX(')', yourString, CHARINDEX('/', yourString) + 1)
The position of the ( is a little different, as you need to search backwards. So you need to reverse the string. And so you also need to change the starting position.
CHARINDEX('(', REVERSE(yourString), LEN(yourString) - CHARINDEX('/', yourString) + 2)
Which give the position from the right hand side. LEN(yourString) - position + 1 give the position from the left hand side.
Add that all together and you get a very long formula...
SUBSTRING(
yourString,
LEN(yourString)
- CHARINDEX('(', REVERSE(yourString), LEN(yourString) - CHARINDEX('/', yourString) + 2)
+ 1,
CHARINDEX(')', yourString, CHARINDEX('/', yourString) + 1)
- LEN(yourString)
+ CHARINDEX('(', REVERSE(yourString), LEN(yourString) - CHARINDEX('/', yourString) + 2)
- 1
)

Remove everything up to the second ( using stuff and get the characters to the next ) using left.
declare #S varchar(20)
set #S = '(1+2)+(3/4)*1000'
select left(S2.S, charindex(')', S2.S)-1)
from (select stuff(#S, 1, charindex('(', #S), '')) as S1(S)
cross apply (select stuff(S1.S, 1, charindex('(', S1.S), '')) as S2(S)

Related

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

Get substring between second and fourth slash

I have a string that looks like this:
Y:\Data\apples\oranges\Scott\notes
I need a column that looks like this:
apples\oranges
This is what I have so far and it does not work:
SELECT SUBSTRING(
[Group],
CHARINDEX('\', [Group]) + 1,
LEN([Group]) - CHARINDEX('\', [Group]) - CHARINDEX('\', REVERSE([Group]))
) from datamap.finaltest
The strings will not always have a finite amount of slashes. For example you could have:
Y:\Data\Apples\bananas
Y:\Apples\Pears\oranges\peanuts
The data will always have:
drive letter + '\' + '1st level folder' + '\' + 'Second level folder'
It may have more than two levels though.
I have searched the forum but can't find anything specific.
Thanks
A blatant approach by converting your input into XML and taking the values by node and re-concatenating the nodes you want in output
;WITH MyTempData
AS
(
SELECT Convert(xml,'<n>'+Replace('Y:\Data\Apples','\','</n><n>')+'</n>') XMLString
)
SELECT COALESCE(XMLString.value('(/n[3])', 'varchar(20)'),'') + '\' +
COALESCE(XMLString.value('(/n[4])', 'varchar(20)'),'') MyFinalOutput
FROM MyTempData
Probably not the best way, but this will get you there.
DECLARE #string varchar(255) = 'Y:\data\apples\oranges\Scott\notes'
SELECT LEFT(RIGHT(#string,LEN(#string)-CHARINDEX('\', #string, CHARINDEX('\', #string,1) + 1)),CHARINDEX('\', RIGHT(#string,LEN(#string)-CHARINDEX('\', #string, CHARINDEX('\', #string,1) + 1)), CHARINDEX('\',RIGHT(#string,LEN(#string)-CHARINDEX('\', #string, CHARINDEX('\', #string,1) + 1)),1)+1)-1)
Here is a way using recursive CHARINDEX
declare #var varchar(4000) = 'Y:\Data\apples\oranges\Scott\notes'
declare #firstSlash int = (select CHARINDEX('\',#var,CHARINDEX('\',#var) + 1))
declare #fourthSlash int = (select CHARINDEX('\',#var,CHARINDEX('\',#var,CHARINDEX('\',#var,CHARINDEX('\',#var) + 1)+1)+1))
select SUBSTRING(#var,#firstSlash + 1,#fourthSlash - #firstSlash - 1)
Or, for your data table...
select SUBSTRING([Group],CHARINDEX('\',[Group],CHARINDEX('\',[Group]) + 1) + 1,CHARINDEX('\',[Group],CHARINDEX('\',[Group],CHARINDEX('\',[Group],CHARINDEX('\',[Group]) + 1)+1)+1) - CHARINDEX('\',[Group],CHARINDEX('\',[Group]) + 1) - 1)
If this is something you need to do often, or is prone to changing, it may be beneficial to implement a function which will make your code more readable/maintainable:
SELECT SUBSTRING(#t, dbo.CHARINDEX2('\', #t, 2) + 1, dbo.CHARINDEX2('\', #t, 3));
Using this 'find nth occurence' function:
http://www.sqlservercentral.com/scripts/Miscellaneous/30497/

SQL need advice with substring and charindex

I have the following string 'Custom Code changed from (JA) to (JC)'.
I want to extract JA into a column and JC into another.
I have managed to get the first bracket using
SUBSTRING(Call_Note_Note, CHARINDEX('(', Call_Note_Note + '(') + 1,
CHARINDEX(')', Call_Note_Note + '()') - CHARINDEX('(', Call_Note_Note + '(') - 1) as 'first'
I cant figure out how to use a simular query to extract the second part?
any help would be great!
Extra
There can be two formats on with one bracket and another with two brackets see below.
Custom Code (POO) Added
Custom Code changed from (POO) to (JA)
Aron
Assuming it will always have only two codes.
Here's how you can do it using the REVERSE() function. This will return the first and last:
DECLARE #String VARCHAR(100) = 'Custom Code changed from (JA) to (JC)'
SELECT SUBSTRING(#String, CHARINDEX('(', #String + '(') + 1,CHARINDEX(')', #String + '()') - CHARINDEX('(', #String + '(') - 1) as 'first',
REVERSE(SUBSTRING(REVERSE(#String), CHARINDEX(')', REVERSE(#String) + '(') + 1, CHARINDEX('(', REVERSE(#String) + ')(') - CHARINDEX(')', REVERSE(#String) + ')') - 1)) as 'Second'
Result:
Another way would be(More Readable)
DECLARE #str VARCHAR(max)='Custom Code changed from (JA) to (JC)';
WITH cte
AS (SELECT #str AS string)
SELECT Substring(string, fst + 1, scd - fst - 1) as first_val,
Substring(string, trd + 1, fou - trd - 1) as second_val
FROM cte
CROSS apply (VALUES (Charindex('(', string))) cs (fst)
CROSS apply (VALUES (Charindex(')', string, fst + 1))) cs1 (scd)
CROSS apply (VALUES (Charindex('(', string, scd + 1))) cs2 (trd)
CROSS apply (VALUES (Charindex(')', string, trd + 1))) cs3 (fou)
Result:
╔═══════════╦════════════╗
║ first_val ║ second_val ║
╠═══════════╬════════════╣
║ JA ║ JC ║
╚═══════════╩════════════╝

Ternary operator in SQL? "invalid length parameter passed to the LEFT or SUBSTRING function"

Sorry for this misleading subject, i didn't know how to word better.
Because i'm mainly a software-developer, the ternary operator comes to my mind with my following problem.
I need to find the most robust way to link two tables via nullable foreign-key(modModel and tabSparePart). The only similarity between both is the model's name and the sparepart's description(the tabSparePart is an external table from customer that is imported automatically, so it's not my responsibility and i cannot change the data).
Consider the following sparepart-names:
W200I_E/Swap
EXCHANGEUNIT P1i / SILVERBLACK/ CYRILLIC
The modelnames that i want to find are P1i and W200I_E.
So there is only one strong rule that i can ensure in the where-clause:
there must be a separator / and the relevant part is the first one.
Here is the sample data:
Create table #temp(Partname varchar(100))
INSERT INTO #temp
SELECT 'EXCHANGEUNIT P1i / SILVERBLACK/ CYRILLIC' UNION ALL SELECT 'W200I_E/Swap unit/Black'
I would have been finished with following query:
SELECT RTRIM(LEFT(Partname, CHARINDEX('/', Partname) - 1)) AS UNIT
FROM #temp
WHERE CHARINDEX('/', Partname) > 0
... what returns:
EXCHANGEUNIT P1i
W200I_E
But i need P1i. So i need a way to handle also the case that the first part is separated by whitespaces. In that case i need to select the last word, but only if it is separated at all.
I'm getting a "invalid length parameter passed to the LEFT or SUBSTRING function"-error with following query:
SELECT REVERSE( LEFT( REVERSE(RTRIM(LEFT(Partname, CHARINDEX('/', Partname) - 1)))
, CHARINDEX(' ', REVERSE(RTRIM(LEFT(Partname, CHARINDEX('/', Partname) - 1))))-1 ))
AS Unit
FROM #temp
WHERE CHARINDEX('/', Partname) > 0
This would work without the second record that has no whitespace. If i would also ensure that the first part contains a whitespace, i would discard valid records.
To cut a long story short, I need to find a way to combine both ways according to the existence of separators.
PS: This has arisen from: Get the last word of a part of a varchar (LEFT/RIGHT)
If anybody is interested, this is the complete (working) stored-procedure. I'm sure i've never used such a strange JOIN:
CREATE PROC [dbo].[UpdateModelSparePart](#updateCount int output)
with execute as Owner
AS
BEGIN
BEGIN TRANSACTION
UPDATE modModel SET fiSparePart=ModelPart.idSparePart
FROM modModel INNER JOIN
(
SELECT m.idModel
,m.ModelName
,sp.idSparePart
,sp.Price
,Row_Number()Over(Partition By idModel ORDER BY Price DESC)as ModelPrice
FROM modModel AS m INNER JOIN tabSparePart AS sp
ON m.ModelName = CASE
WHEN CHARINDEX(' ', REVERSE(RTRIM(LEFT(sp.SparePartDescription, CHARINDEX('/', sp.SparePartDescription) - 1)))) > 0 THEN
REVERSE( LEFT( REVERSE(RTRIM(LEFT(sp.SparePartDescription, CHARINDEX('/', sp.SparePartDescription) - 1)))
,CHARINDEX(' ', REVERSE(RTRIM(LEFT(sp.SparePartDescription, CHARINDEX('/', sp.SparePartDescription) - 1))))-1 ))
ELSE
RTRIM(LEFT(sp.SparePartDescription, CHARINDEX('/', sp.SparePartDescription) - 1))
END
WHERE (CHARINDEX('/', sp.SparePartDescription) > 0)
GROUP BY idModel,ModelName,idSparePart,Price
)As ModelPart
ON ModelPart.idModel=modModel.idModel
Where ModelPrice=1
SET #updateCount = ##ROWCOUNT;
COMMIT TRANSACTION
END
A more concise version.
SELECT REVERSE(SUBSTRING(Rev, 0, CHARINDEX(' ', Rev))) AS Unit
FROM #temp
CROSS APPLY (
SELECT REVERSE(RTRIM(LEFT(Partname, CHARINDEX('/', Partname) - 1))) + ' '
) T(Rev)
WHERE CHARINDEX('/', Partname) > 0
I was able to solve the problem:
SELECT 'Unit' =
CASE
WHEN CHARINDEX(' ', REVERSE(RTRIM(LEFT(Partname, CHARINDEX('/', Partname) - 1)))) > 0 THEN
REVERSE( LEFT( REVERSE(RTRIM(LEFT(Partname, CHARINDEX('/', Partname) - 1)))
,CHARINDEX(' ', REVERSE(RTRIM(LEFT(Partname, CHARINDEX('/', Partname) - 1))))-1 ))
ELSE
RTRIM(LEFT(Partname, CHARINDEX('/', Partname) - 1))
END
FROM #temp
WHERE CHARINDEX('/', Partname) > 0
Ugly but working fine.

SQL - Selecting portion of a string

If I have a simple table where the data is such that the rows contains strings like:
/abc/123/gyh/tgf/345/6yh/5er
In SQL, how can I select out the data between the 5th and 6th slash? Every row I have is simply data inside front-slashes, and I will only want to select all of the characters between slash 5 and 6.
CLR functions are more efficient in handling strings than T-SQL. Here is some info to get you started on writing a CLR user defined function.
http://msdn.microsoft.com/en-us/library/ms189876.aspx
http://www.mssqltips.com/tip.asp?tip=1344
I think you should create the function that has 3 parameters:
the value you are searching
the delimiter (in your case: /)
The instance you are looking for (in your case: 5)
Then you split on the delimiter (into an array). Then return the 5th item in the array (index 4)
Here is a t-sql solution, but I really believe that a CLR solution would be better.
DECLARE #RRR varchar(500)
SELECT #RRR = '/abc/123/gyh/tgf/345/6yh/5er'
DECLARE
#index INT,
#INSTANCES INT
SELECT
#index = 1,
#INSTANCES = 5
WHILE (#INSTANCES > 1) BEGIN
SELECT #index = CHARINDEX('/', #RRR, #index + 1)
SET #INSTANCES = #INSTANCES - 1
END
SELECT SUBSTRING(#RRR, #index + 1, CHARINDEX('/', #RRR, #index + 1) - #index - 1)
SELECT SUBSTRING(myfield,
/* 5-th slash */
CHARINDEX('/', myfield,
CHARINDEX('/', myfield,
CHARINDEX('/', myfield,
CHARINDEX('/', myfield,
CHARINDEX('/', myfield) + 1) + 1) + 1) + 1)
+ 1,
/* 6-th slash */
CHARINDEX('/', myfield,
CHARINDEX('/', myfield,
CHARINDEX('/', myfield,
CHARINDEX('/', myfield,
CHARINDEX('/', myfield,
CHARINDEX('/', myfield) + 1) + 1) + 1) + 1) + 1)
-
/* 5-th slash again */
CHARINDEX('/', myfield,
CHARINDEX('/', myfield,
CHARINDEX('/', myfield,
CHARINDEX('/', myfield,
CHARINDEX('/', myfield) + 1) + 1) + 1) + 1)
- 1)
FROM myTable
WHERE ...
This will work, but it's far from elegant. If possible, select the complete field and filter out the required value on the client side (using a more powerful programming language than T-SQL). As you can see, T-SQL was not designed to do this kind of stuff.
(Edit: I know the following does not apply to your situation but I'll keep it as a word of advise for others who read this:)
In fact, relational databases are not designed to work with string-separated lists of values at all, so an even better solution would be to split that field into separate fields in your table (or into a subtable, if the number of entries varies).
Maybe... SELECT FROM `table` WHERE `field` LIKE '%/345/%'