Substring and split in sql server - sql-server

I have these data in my sql server as you can see here :
1/2
1/4
2/23
12/13
1/10
...
I need to change these to 001,001,002,012,001,..
I use this .but it doesn't work
LEFT(SheetNumber,LEN(SheetNumber)-CHARINDEX('/',SheetNumber))
My query
SELECT [Id]
,LEFT(SheetNumber,LEN(SheetNumber)-CHARINDEX('/',SheetNumber))
,[SubmitDateTime]
FROM [SPMS2].[dbo].[Lines] where SheetNumber like '%/%'

You dont need to use LEN. Just use
LEFT(SheetNumber,CHARINDEX('/',SheetNumber) - 1)
To make it into 3 digits with 0 in the front, you could use something like this
Right('000' + LEFT(SheetNumber,CHARINDEX('/',SheetNumber) - 1), 3)

Try this
RIGHT('000' + LEFT(SheetNumber, CHARINDEX('/', SheetNumber) - 1), 3)

Try this
DECLARE #tbl TABLE(SheetNumber VARCHAR(100));
INSERT INTO #tbl VALUES
('1/2')
,('1/4')
,('2/23')
,('12/13')
,('1/10');
SELECT STUFF(
(
SELECT ',' + REPLACE(STR(LEFT(SheetNumber,CHARINDEX('/',SheetNumber) - 1),3),' ','0')
FROM #tbl
FOR XML PATH('')
),1,1,'');
The result
001,001,002,012,001

Related

Combine string_split column results in table SQL

I'm trying to create a stored procedure for updating a table in a batch. I want to take parameters in as a nvarchar and call string_split on them.
#ParamList1 NVARCHAR(max) = '1,2,3,4,5'
#ParamList2 NVARCHAR(max) = 'a,b,c,d,e'
I want to get a temporary table like
Param1 Param2
1 a
2 b
3 c
...
How would I do this?
Unfortunately, string_split() does not guarantee ordering or provide a position argument (Microsoft are you listening?).
So, the safest method is a recursive CTE (or perhaps another approach using XML):
with cte as (
select convert(nvarchar(max), NULL) as x1, convert(nvarchar(max), NULL) as x2, #paramlist1 as rest1, #paramlist2 as rest2, 1 as lev
union all
select convert(nvarchar(max), left(rest1, charindex(',', rest1 + ',') - 1)),
convert(nvarchar(max), left(rest2, charindex(',', rest2 + ',') - 1)),
stuff(rest1, 1, charindex(',', rest1 + ','), ''),
stuff(rest2, 1, charindex(',', rest2 + ','), ''),
lev + 1
from cte
where rest1 <> '' and rest2 <> ''
)
select *
from cte
where x1 is not null;
Here is a db<>fiddle.
You've got an answer already, which is working fine, but this should be faster and easier:
You did not specify your SQL-Server's version, but - talking about STRING_SPLIT() - I assume it's at least v2016. If this is correct, you can use OPENJSON. Your list of numbers needs nothing more than brackets to be a JSON-array ([1,2,3]), while an array of words/letters can be transformed with some easy string operations (["a","b","c"]).
Following the docs, OPENJSON returns the elements position in [key], while the element itself is returned in [value]. You can simply JOIN these sets:
DECLARE #ParamList1 NVARCHAR(max) = '1,2,3,4,5';
DECLARE #ParamList2 NVARCHAR(max) = 'a,b,c,d,e';
SELECT p1.[key] AS FragmentNr
,p1.[value] AS P1
,p2.[value] AS P2
FROM OPENJSON(CONCAT('[',#ParamList1 + ']')) p1
INNER JOIN OPENJSON(CONCAT('["',REPLACE(#ParamList2,',','","'),'"]')) p2 ON p1.[key]=p2.[key] ;
In this answer you will find some details (UPDATE section 1 and 2).

Find MaxNonZeroPosition of a scale in a decimal

I have a scenario, wherein I need a sql query to find out the MaxNonZeroPosition in a scale of every decimal column of a database using SQL Server.
For ex: 43321.70090100, Max nonZero Position is 6 here.
In order to reduce complexity I've put part in a subquery. This should give you what you need:
DECLARE #T DECIMAL(20, 12) = 43321.70090100
SELECT LEN(CONVERT(VARCHAR(20), SS)) - PATINDEX('%[^0]%', REVERSE(SS))
FROM (
SELECT #T Ori
, SUBSTRING(CONVERT(VARCHAR(20), #T), CHARINDEX('.', #T), LEN(#T)) SS
) Sub
Another approach
declare #test decimal(20, 12) = 221123.812000430035000
select len(replace(#test, 0, ' ')) - charindex('.', #test)

STRING_SPLIT from column value and reverse it in SQL Server

I am trying to reverse string values in a column in SQL Server.
What I am trying to achieve is, when the value of the column is child/parent, I want to change it to parent/child when child contains '112'
For example I want to change columns with value Reports/112-Major to 112-Major/Reports
To start with, I tried to use STRING_SPLIT and append them like String_Split(ColumnName,'/')[1] + String_Split(ColumnName,'/')[0] if String_Split(ColumnName,'/')[1] like '%112%'
Most of the examples that I see online have something like
SELECT Value FROM STRING_SPLIT('Lorem/ipsum/dolor/sit/amet.', '/');
But I want to split and then merge based on condition and then update the column
Something like,
update tblTableName
set siteUrl = String_Split(ColumnName,'/')[1] + String_Split(ColumnName,'/')[0]
where `String_Split(ColumnName,'/')[1] like '%112%'
Is there a way to do this in SQL Server?
You can use this expression:
stuff(col, 1, charindex('/', col), '') + '/' + left(col, charindex('/', col) - 1)
Another option just for fun.
Gordon's solution would be my first choice and is certainly more performant (+1), but the following illustrates a simple technique which can be used to split a string into columns and not rows.
Sample
Declare #YourTable table (ID int,YourCol varchar(100))
Insert Into #YourTable values
(1,'Reports/112-Major'),
(2,'Reports/Something Else')
Update #YourTable Set YourCol = Pos2+'/'+Pos1
From #YourTable A
Cross Apply (
Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
From (Select Cast('<x>' + replace((Select replace(A.YourCol,'/','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) as xDim) as B1
) B
Where Pos2 Like '%112%'
Updated Results
ID YourCol
1 112-Major/Reports
2 Reports/Something Else

Concatenate the result of an ordered String_Split in a variable

In a SqlServer database I use, the database name is something like StackExchange.Audio.Meta, or StackExchange.Audio or StackOverflow . By sheer luck this is also the url for a website. I only need split it on the dots and reverse it: meta.audio.stackexchange. Adding http:// and .com and I'm done. Obviously Stackoverflow doesn't need any reversing.
Using the SqlServer 2016 string_split function I can easy split and reorder its result:
select value
from string_split(db_name(),'.')
order by row_number() over( order by (select 1)) desc
This gives me
| Value |
-----------------
| Meta |
| Audio |
| StackExchange |
As I need to have the url in a variable I hoped to concatenate it using this answer so my attempt looks like this:
declare #revname nvarchar(150)
select #revname = coalesce(#revname +'.','') + value
from string_split(db_name(),'.')
order by row_number() over( order by (select 1)) desc
However this only returns me the last value, StackExchange. I already noticed the warnings on that answer that this trick only works for certain execution plans as explained here.
The problem seems to be caused by the order by clause. Without that I get all values, but then in the wrong order. I tried to a add ltrimand rtrim function as suggested in the Microsoft article as well as a subquery but so far without luck.
Is there a way I can nudge the Sql Server 2016 Query Engine to concatenate the ordered result from that string_split in a variable?
I do know I can use for XML or even a plain cursor to get the result I need but I don't want to give up this elegant solution yet.
As I'm running this on the Stack Exchange Data Explorer I can't use functions, as we lack the permission to create those. I can do Stored procedures but I hoped I could evade those.
I prepared a SEDE Query to experiment with. The database names to expect are either without dots, aka StackOverflow, with 1 dot: StackOverflow.Meta or 2 dots, `StackExchange.Audio.Meta, the full list of databases is here
I think you are over-complicating things. You could use PARSENAME:
SELECT 'http://' + PARSENAME(db_name(),1) +
ISNULL('.' + PARSENAME(db_name(),2),'') + ISNULL('.'+PARSENAME(db_name(),3),'')
+ '.com'
This is exactly why I have the Presentation Sequence (PS) in my split function. People often scoff at using a UDF for such items, but it is generally a one-time hit to parse something for later consumption.
Select * from [dbo].[udf-Str-Parse]('meta.audio.stackexchange','.')
Returns
Key_PS Key_Value
1 meta
2 audio
3 stackexchange
The UDF
CREATE FUNCTION [dbo].[udf-Str-Parse] (#String varchar(max),#delimeter varchar(10))
--Usage: Select * from [dbo].[udf-Str-Parse]('meta.audio.stackexchange','.')
-- Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')
-- Select * from [dbo].[udf-Str-Parse]('id26,id46|id658,id967','|')
Returns #ReturnTable Table (Key_PS int IDENTITY(1,1) NOT NULL , Key_Value varchar(max))
As
Begin
Declare #intPos int,#SubStr varchar(max)
Set #IntPos = CharIndex(#delimeter, #String)
Set #String = Replace(#String,#delimeter+#delimeter,#delimeter)
While #IntPos > 0
Begin
Set #SubStr = Substring(#String, 0, #IntPos)
Insert into #ReturnTable (Key_Value) values (#SubStr)
Set #String = Replace(#String, #SubStr + #delimeter, '')
Set #IntPos = CharIndex(#delimeter, #String)
End
Insert into #ReturnTable (Key_Value) values (#String)
Return
End
Probably less elegant solution but it takes only a few lines and works with any number of dots.
;with cte as (--build xml
select 1 num, cast('<str><s>'+replace(db_name(),'.','</s><s>')+'</s></str>' as xml) str
)
,x as (--make table from xml
select row_number() over(order by num) rn, --add numbers to sort later
t.v.value('.[1]','varchar(50)') s
from cte cross apply cte.str.nodes('str/s') t(v)
)
--combine into string
select STUFF((SELECT '.' + s AS [text()]
FROM x
order by rn desc --in reverse order
FOR XML PATH('')
), 1, 1, '' ) name
Is there a way I can nudge the Sql Server 2016 Query Engine to concatenate the ordered result from that string_split in a variable?
You can just use CONCAT:
DECLARE #URL NVARCHAR(MAX)
SELECT #URL = CONCAT(value, '.', #URL) FROM STRING_SPLIT(DB_NAME(), '.')
SET #URL = CONCAT('http://', LOWER(#URL), 'com');
The reversal is accomplished by the order of parameters to CONCAT. Here's an example.
It changes StackExchange.Garage.Meta to http://meta.garage.stackexchange.com.
This can be used to split and reverse strings in general, but note that it does leave a trailing delimiter. I'm sure you could add some logic or a COALESCE in there to make that not happen.
Also note that vNext will be adding STRING_AGG.
To answer the 'X' of this XY problem, and to address the HTTPS switch (especially for Meta sites) and some other site name changes, I've written the following SEDE query which outputs all site names in the format used on the network site list.
SELECT name,
LOWER('https://' +
IIF(PATINDEX('%.Mathoverflow%', name) > 0,
IIF(PATINDEX('%.Meta', name) > 0, 'meta.mathoverflow.net', 'mathoverflow.net'),
IIF(PATINDEX('%.Ubuntu%', name) > 0,
IIF(PATINDEX('%.Meta', name) > 0, 'meta.askubuntu.com', 'askubuntu.com'),
IIF(PATINDEX('StackExchange.%', name) > 0,
CASE SUBSTRING(name, 15, 200)
WHEN 'Audio' THEN 'video'
WHEN 'Audio.Meta' THEN 'video.meta'
WHEN 'Beer' THEN 'alcohol'
WHEN 'Beer.Meta' THEN 'alcohol.meta'
WHEN 'CogSci' THEN 'psychology'
WHEN 'CogSci.Meta' THEN 'psychology.meta'
WHEN 'Garage' THEN 'mechanics'
WHEN 'Garage.Meta' THEN 'mechanics.meta'
WHEN 'Health' THEN 'medicalsciences'
WHEN 'Health.Meta' THEN 'medicalsciences.meta'
WHEN 'Moderators' THEN 'communitybuilding'
WHEN 'Moderators.Meta' THEN 'communitybuilding.meta'
WHEN 'Photography' THEN 'photo'
WHEN 'Photography.Meta' THEN 'photo.meta'
WHEN 'Programmers' THEN 'softwareengineering'
WHEN 'Programmers.Meta' THEN 'softwareengineering.meta'
WHEN 'Vegetarian' THEN 'vegetarianism'
WHEN 'Vegetarian.Meta' THEN 'vegetarianism.meta'
WHEN 'Writers' THEN 'writing'
WHEN 'Writers.Meta' THEN 'writing.meta'
ELSE SUBSTRING(name, 15, 200)
END + '.stackexchange.com',
IIF(PATINDEX('StackOverflow.%', name) > 0,
CASE SUBSTRING(name, 15, 200)
WHEN 'Br' THEN 'pt'
WHEN 'Br.Meta' THEN 'pt.meta'
ELSE SUBSTRING(name, 15, 200)
END + '.stackoverflow.com',
IIF(PATINDEX('%.Meta', name) > 0,
'meta.' + SUBSTRING(name, 0, PATINDEX('%.Meta', name)) + '.com',
name + '.com'
)
)
)
)
) + '/'
)
FROM sys.databases WHERE database_id > 5

SQL Server : select all after specific character

How I can select
"ALT1" if value is "W61N03D20V0-WHIH-ALT1"
"ALT2" if for "W61N03D20V0-WHIH-ALT2"
"SW" for "W61N03D20V0-WHIH-SW"
"Default" for "W61N26D1YA1-VICU" (without prefix)
"Defailt" for "W61N27D21V2-AZTD"
In other words I'm looking for a way extract last part after second suffix, but if I have't second suffix - then default
Thanks for advice
Try it like this:
First you "split" the string on its minus signs with the XML trick.
Then you read the third node from you XML - voila!
CREATE TABLE #tbl(content VARCHAR(100));
INSERT INTO #tbl VALUES('W61N03D20V0-WHIH-ALT1')
,('W61N03D20V0-WHIH-SW')
,('W61N26D1YA1-VICU');
WITH SplittedAsXml AS
(
SELECT CAST('<x>' + REPLACE(content,'-','</x><x>') + '</x>' AS XML) AS Content
FROM #tbl
)
SELECT ISNULL(Content.value('/x[3]','varchar(max)'),'default') AS TheThirdPart
FROM SplittedAsXml;
DROP TABLE #tbl;
The result
ALT1
SW
default
Going this ways would also give you the chance to get the other parts in one go just querying /x[1] and /x[2] too
I did it using the built-in substring() function:
declare #str VARCHAR(40) = 'W61N03D20V0-WHIH-ALT1' -- also works for the other examples
declare #sep VARCHAR(1) = '-'
declare #middleToEnd VARCHAR(40) = substring(#str, charindex(#sep, #str) + 1, len(#str))
declare #pos INT = charindex(#sep, #middleToEnd)
declare #lastPart VARCHAR(40) =
CASE WHEN #pos = 0
THEN 'Default'
ELSE substring(#middleToEnd, #pos + 1, len(#middleToEnd))
END
select #lastPart
For best performance, you can solve it with this one-liner(calculation is one line)
SELECT
COALESCE(STUFF(col,1,NULLIF(CHARINDEX('-',col, CHARINDEX('-',col)+1), 0),''),'Default')
FROM (values
('W61N03D20V0-WHIH-ALT1'),('W61N03D20V0-WHIH-ALT2'),
('W61N03D20V0-WHIH-SW'),('W61N26D1YA1-VICU'),
('W61N27D21V2-AZTD')) x(col)
Result:
ALT1
ALT2
SW
Default
Default
If I understand what you are asking for, the following does what you need:
-- fake table
WITH SomeTable AS (
SELECT 'W61N03D20V0-WHIH-ALT1' AS Field1
UNION ALL
SELECT 'W61N03D20V0-WHIH-SW'
UNION ALL
SELECT 'W61N26D1YA1-VICU'
)
-- select
SELECT
CASE CHARINDEX('-WHIH-', Field1)
WHEN 0 THEN 'Default'
ELSE SUBSTRING(Field1, CHARINDEX('-WHIH-', Field1) + 6, LEN(Field1) - (CHARINDEX('-WHIH-', Field1) + 5))
END
FROM SomeTable
Use can use a CASE expression to check whether the string starts with W61N03D20V0-WHIH.
If it starts with it use a combination of RIGHT, REVERSE and CHARINDEX functions to get last part from the string, else Default.
Query
select case when [your_column_name] like 'W61N03D20V0-WHIH%'
then right([your_column_name], charindex('-', reverse([your_column_name]), 1) - 1)
else 'Default' end as new_column_name
from your_table_name;
SQl Fiddle demo

Resources