Extract a substring in a column in the 5th place - SQL Server - sql-server

How can I extract a string that appears after 4 ';' in a column in a table using a select query?
For example - if U have the following value in the column:
1234;0000;567;655;0541234567;777;777
I would like to fetch the value in the 5th place; in this example it's
0541234567
Thanks!

You can use a bit of JSON
Example
Declare #YourTable table (SomeCol varchar(100))
Insert into #YourTable values ('1234;0000;567;655;0541234567;777;777')
Select A.SomeCol
,Val5 = JSON_VALUE(S,'$[4]')
from #YourTable A
Cross Apply ( values ( '["'+replace(SomeCol,';','","')+'"]' ) ) B(S)
Returns
SomeCol Val5
1234;0000;567;655;0541234567;777;777 0541234567
EDIT -- CROSS APPLY not necessary if only after ONE value
Select A.SomeCol
,Val5 = JSON_VALUE('["'+replace(SomeCol,';','","')+'"]','$[4]')
from #YourTable A

Related

SQL obtain values between second and third forward slash

In SQL Server, I am trying to obtain the values between the second and third forward slash (/) character. The length of the numbers can vary so substring(column, 8, 10) wouldn't work.
123/123/123456789/12
What I am trying to get in the current example is: 123456789
With 4 parts to your data as shown you can abuse the parsename function:
declare #string varchar(50) = '123/123/123456789/12';
select ParseName(Replace(#string,'/','.'),2);
Please try the following solution based on tokenization.
This method is generic regardless how many tokens are in place.
It will work starting from SQL Server 2012 onwards.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, tokens VARCHAR(100));
INSERT #tbl (tokens) VALUES
('123/123/123456789/12'),
('123/123/9876543210/12');
-- DDL and sample data population, end
DECLARE #separator CHAR(1) = '/';
SELECT t.*
, ThirdToken = c.value('(/root/r[position() eq 3]/text())[1]', 'VARCHAR(100)')
FROM #tbl AS t
CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' +
REPLACE(tokens, #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML)) AS t1(c);
Output
ID
tokens
ThirdToken
1
123/123/123456789/12
123456789
2
123/123/9876543210/12
9876543210
Since you are on 2016, consider the following
Example
Declare #YourTable table (ID int,SomeCol varchar(50))
Insert Into #YourTable values
(1,'123/123/123456789/12')
Select A.ID
,Pos3 = JSON_VALUE(JS,'$[2]')
From #YourTable A
Cross Apply (values ('["'+replace(SomeCol,'/','","')+'"]') ) B(JS)
Results
ID Pos3
1 123456789
If you only need the single value, there is no need for the CROSS APPLY
Select A.ID
,Pos3 = JSON_VALUE('["'+replace(SomeCol,'/','","')+'"]','$[2]')
From #YourTable A

Substring is slow with while loop in SQL Server

One of my table column stores ~650,000 characters (each value of the column contains entire table). I know its bad design however, Client will not be able to change it.
I am tasked to convert the column into multiple columns.
I chose to use dbo.DelimitedSplit8K function
Unfortunately, it can only handle 8k characters at max.
So I decided to split the column into 81 8k batches using while loop and store the same in a variable table (temp or normal table made no improvement)
DECLARE #tab1 table ( serialnumber int, etext nvarchar(1000))
declare #scriptquan int = (select MAX(len (errortext)/8000) from mytable)
DECLARE #Counter INT
DECLARE #A bigint = 1
DECLARE #B bigint = 8000
SET #Counter=1
WHILE ( #Counter <= #scriptquan + 1)
BEGIN
insert into #tab1 select ItemNumber, Item from dbo.mytable cross apply dbo.DelimitedSplit8K(substring(errortext, #A, #B), CHAR(13)+CHAR(10))
SET #A = #A + 8000
SET #B = #B + 8000
SET #Counter = #Counter + 1
END
This followed by using below code
declare #tab2 table (Item nvarchar(max),itemnumber int, Colseq varchar(10)) -- declare table variable
;with cte as (
select [etext] ,ItemNumber, Item from #tab1 -- insert table name
cross apply dbo.DelimitedSplit8K(etext,' ')) -- insert table columns name that contains text
insert into #tab2 Select Item,itemnumber, 'a'+ cast (ItemNumber as varchar) colseq
from cte -- insert values to table variable
;WITH Tbl(item, colseq) AS(
select item, colseq from #tab2
),
CteRn AS(
SELECT item, colseq,
Rn = ROW_NUMBER() OVER(PARTITION BY colseq ORDER BY colseq)
FROM Tbl
)
SELECT
a1 Time,a2 Number,a3 Type,a4 Remarks
FROM CteRn r
PIVOT(
MAX(item)
FOR colseq IN(a1,a2,a3,a4)
)p
where a3 = 'error'
gives the desired output. However, just the loop takes 15 minutes to complete and overall query completes by 27 minutes. Is there any way I can make it faster? Total row count in my table is 2. So I don't think Index can help.
Client uses Azure SQL Database so I can't choose PowerShell or Python to accomplish this either.
Please let me know if more information is needed. I tried my best to mention everything I could.

Multiple XML tag value into single column with comma separator

I have an XML where the XML have multiple similar tag and I want this value need to show in one column with comma separator and insert into table.
For example:
<test xmlns="http://www.google.com">
<code>a</code>
<code>b</code>
<code>c</code>
</test>
Since XML is too large and I am using OPENXML to perform operation and insert that value into particular table.
I am performing like
insert into table A
(
code
)
select Code from OPENXML(sometag)
with (
code varchar(100) 'tagvalue'
)
for XQUERY I am using something like this: 'for $i in x:Code return concat($i/text()[1], ";")' and I want same with OPENXML.
Output: I want code tag value into one column like a,b,c or a/b/c.
Since you're on SQL Server 2017 you could use STRING_AGG (Transact-SQL) to concatenate your code values, e.g.:
create table dbo.Test (
someTag xml
);
insert dbo.Test (someTag) values
('<test><code>a</code><code>b</code><code>c</code></test>'),
('<test><code>d</code><code>e</code><code>f</code></test>');
select [Code], [someTag]
from dbo.Test
outer apply (
select [Code] = string_agg([value], N',')
from (
select n1.c1.value('.', 'nvarchar(100)')
from someTag.nodes(N'/test/code') n1(c1)
) src (value)
) a1;
Which yields...
Code someTag
a,b,c <test><code>a</code><code>b</code><code>c</code></test>
d,e,f <test><code>d</code><code>e</code><code>f</code></test>
Just a small tweak to AlwaysLearning (+1)
Example
Declare #YourTable table (ID int,XMLData xml)
insert Into #YourTable values
(1,'<test><code>a</code><code>b</code><code>c</code></test>')
Select A.ID
,B.*
From #YourTable A
Cross Apply (
Select DelimString = string_agg(xAttr.value('.','varchar(max)'),',')
From A.XMLData.nodes('/test/*') xNode(xAttr)
) B
Returns
ID DelimString
1 a,b,c
And just for completeness, here is method #3 via pure XQuery and FLWOR expression.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, xmldata xml);
INSERT #tbl (xmldata) VALUES
('<test xmlns="http://www.google.com"><code>a</code><code>b</code><code>c</code></test>'),
('<test xmlns="http://www.google.com"><code>d</code><code>e</code><code>f</code></test>');
-- DDL and sample data population, end
DECLARE #separator CHAR(1) = ',';
-- Method #3
-- SQL Server 2005 onwards
;WITH XMLNAMESPACES (DEFAULT 'http://www.google.com')
SELECT ID
, xmldata.query('for $i in /test/code
return if ($i is (/test/code[last()])[1]) then string($i)
else concat($i, sql:variable("#separator"))')
.value('.', 'NVARCHAR(MAX)') AS [Comma_separated_list]
FROM #tbl;
Output
+----+----------------------+
| ID | Comma_separated_list |
+----+----------------------+
| 1 | a, b, c |
| 2 | d, e, f |
+----+----------------------+

How to find other than current value in SQL Server?

In SQL Server, how can I get data from a column other than specified data.
For example, I have a column with data "MySQL,Server,Database" in single row.
Now I want to find if there is any other value than MySQL in that column.
I tried by using Not Like but didn't succeed.
For example TableA :
id | Code
---+---------------------------
1 | mysql,sqlserver,database
2 | mysql
3 | sqlserver,database
4 | mysql,mysql
Here, I want to find if the column has data other than "mysql" or not, like id:1 has data other than "mysql" but id:2 have "mysql" but not other than "mysql".
Finally if I want to return the null or blank value if there no any data other than "mysql".
Code I used so far :
select code from tableA where code not like '%mysql%'
It helps if you can provide some data and some code. This works (as far as I understand the question):
CREATE TABLE #x (object_type varchar(50))
INSERT #x (object_type) VALUES ('MySQL'), ('Server'), ('Database')
SELECT * FROM #x WHERE object_type <> 'MySQL'
Base on the updated question, I think you're looking for:
CREATE TABLE #x (id int identity(1, 1), code varchar(50))
INSERT #x (code) VALUES ('mysql,sqlserver,database'), ('mysql'), ('sqlserver,database'), ('mysql,mysql')
SELECT *
FROM #x
WHERE id IN (
SELECT id
FROM #x
CROSS APPLY string_split(code, ',')
WHERE value <> 'mysql'
)
However, as #Eric Brandt asked above, it's not clear whether you want to select row id = 4.
Note STRING_SPLIT is only available for SQL Server 2016 or later. if you are using a earlier version, do a search, there are lots of similar implementation
declare #table table
(
id int identity,
Code varchar(30)
)
insert into #table select 'mysql,sqlserver,database'
insert into #table select 'mysql'
insert into #table select 'sqlserver,database'
insert into #table select 'mysql,mysql'
insert into #table select ''
insert into #table select NULL
select *
from #table t
where t.Code is null
or exists
(
select *
from string_split(t.Code, ',')
where value <> 'mysql'
)
/* RESULT
1 mysql,sqlserver,database
3 sqlserver,database
5
6 NULL
*/

How to obtain a string equals to each first letter of words in a sentence IN SQL

I have words separated with a space in a column like
apple orange banana I need the first letters as the result will be something like :
aob
First, split your text. I recommend some function:
CREATE FUNCTION Split(#text nvarchar(MAX),#separator nvarchar(MAX))
RETURNS TABLE AS RETURN
WITH Indexed AS
(
SELECT 1 N, CAST(1 AS bigint) S, CHARINDEX(#separator, #text, 1) E WHERE #text IS NOT NULL
UNION ALL
SELECT N+1, E+DATALENGTH(#separator)/2, CHARINDEX(#separator, #text, E+DATALENGTH(#separator)/2) FROM Indexed WHERE E>S
), Token AS
(
SELECT N, SUBSTRING(#text, S, CASE WHEN E=0 THEN DATALENGTH(#text)/2 ELSE E-S END) T FROM Indexed
)
SELECT * FROM Token
If you are using SQL 2016 and greater, use STRING_SPLIT instead.
Then, you can select first character of every word and join. See following example:
DECLARE #Sample TABLE (T nvarchar(100));
INSERT #Sample VALUES (N'apple orange banana'),(N'dog cat');
SELECT (SELECT SUBSTRING(T,1,1) [*] FROM Split(T,N' ') FOR XML PATH(''))
FROM #Sample
Result:
(no column name)
------
aob
dc
If you declare REGEX function in your DB (not native with SQL SERVER).
Using regexp_replace
select regexp_replace('apple orange banana','(\\w)(\\w* ?)','$1')
return
aob
I think the shortest will be this:
Here a mockup-table with two rows to simulate your issue:
DECLARE #mockup TABLE(ID INT IDENTITY,YourWords VARCHAR(100));
INSERT INTO #mockup VALUES('apple orange banana'),('one two three');
--That is the query:
SELECT m.ID
,REPLACE(Casted.query('for $w in /x return substring($w,1,1)').value('.','varchar(max)'),' ','')
FROM #mockup m
CROSS APPLY(SELECT CAST('<x>' + REPLACE(m.YourWords,' ','</x><x>') + '</x>' AS XML)) A(Casted);
The idea behind:
The string apple orange banana is tranformed to <x>apple</x><x>orange</x><x>banana</x> and is casted to XML, which allows to use XQuery.
Now we use .query() on the XML with a simple FLWOR statement. It tells the engine: run through each value of /x and return just the first letter. Calling value() on this with a . as XPath will return the values in one.
We need a final REPLACE() to get rid of blanks, which would otherwise appear as a o b instead of aob.
Just another option using a little XML. You could also use ParseName() provided you trap any periods in the string.
Example
Declare #YourTable table(ID int,LastName varchar(50),FirstName varchar(50))
Insert Into #YourTable values
(1,'Waston','Mary Jane')
Select A.ID
,NewValue = upper(
concat(
xmlData.value('/x[1]','varchar(1)')
,xmlData.value('/x[2]','varchar(1)')
,xmlData.value('/x[3]','varchar(1)')
,xmlData.value('/x[4]','varchar(1)')
,'.'
,LastName
)
)
From #YourTable A
Cross Apply ( values (convert(xml,'<x>' + replace(A.FirstName,' ','</x><x>')+'</x>' )) ) B(xmlData)
Returns
ID NewValue
1 MJ.WASTON
EDIT - Added ParseName() option
Select A.ID
,NewValue = upper(concat(Pos1,Pos2,Pos3,Pos4,'.',LastName))
From #YourTable A
Cross Apply (
Select Pos1 = left(parsename(tStr,4),1)
,Pos2 = left(parsename(tStr,3),1)
,Pos3 = left(parsename(tStr,2),1)
,Pos4 = left(parsename(tStr,1),1)
From ( values(replace(FirstName,' ','.'))) B1(tStr)
) B

Resources