3

I have data in sql (Just to note: SQL STudio is the IDE) like:

data
a_10_b_c
a_1_b_c

I want to get the data between first two symbols _:

Output
10
1
Giorgi Nakeuri
  • 35,155
  • 8
  • 47
  • 75
Vasista B
  • 329
  • 1
  • 2
  • 10
  • 1
    what's the query you have right now ? – Anthony N. Nov 11 '15 at 19:00
  • select substring(LocationID,charindex('_', LocationID)+1,LEN(LocationID) - CHARINDEX('_', LocationID) - CHARINDEX('_', REVERSE(LocationID))) FROM [SecCharDB].[dbo].[locationmapping] - something like this but this doesnot work – Vasista B Nov 11 '15 at 19:01
  • 1
    Which RDBMS is this for? Please add a tag to specify whether you're using `mysql`, `postgresql`, `sql-server`, `oracle` or `db2` - or something else entirely. – marc_s Nov 11 '15 at 19:02
  • 1
    It is for SQL server and IDE is SSMS – Vasista B Nov 11 '15 at 19:04
  • try substring(LocationID,charindex('-', LocationID),charindex('-', LocationID,charindex('-', LocationID)+1)-charindex('-', LocationID)) – Anthony N. Nov 11 '15 at 19:14
  • How can this have 4 upvotes... – James Z Nov 12 '15 at 17:32
  • Prefered that as I am triggering query in Python through XML via c#, I dint want much sql manipulation going on so... – Vasista B Nov 16 '15 at 02:01
  • Hi Vasista, I think James was wondering about the upvotes to the question itself :-) If you read the tool tip there's something about "research effort" and "clear", your question is OK, but quite "thin". Not really worth the many upvotes (4+ / 1-)... – Shnugo Nov 16 '15 at 08:18

2 Answers2

4

This would be my approach:

SELECT CAST('<x>' + REPLACE(data,'_','</x><x>') + '</x>' AS XML).value('/x[2]','int')
FROM YourTable

First you transform this to an XML and then you pick the second node..

EDIT: Some more examples where this approach is usefull:

CROSS APPLY: You can use this approach to get several tokens at once

DECLARE @tbl TABLE(separated VARCHAR(100));
INSERT INTO @tbl VALUES('1_23:50_Look_this_is_a_test'),('2_12:00_that''s_one_more_test'),('3_13:30_great!_It_works!');

SELECT Converted.value('/x[1]','int') AS number
      ,Converted.value('/x[2]','time') AS time
      ,Converted.value('/x[3]','varchar(max)') AS text
FROM @tbl
CROSS APPLY(SELECT CAST('<x>' + REPLACE(separated,'_','</x><x>') + '</x>' AS XML) AS Converted) AS MySeparated
--type-safe and easy:
/*
number  time    text
1       23:50   Look
2       12:00   that's
3       13:30   great!
*/
GO

CTE: use as parameter

DECLARE @Parameter VARCHAR(100)='1_12:30_SomeValue';
WITH MyParameters AS
(
    SELECT CAST('<x>' + REPLACE(@Parameter,'_','</x><x>') + '</x>' AS XML).value('/x[1]','int') AS IntParam
          ,CAST('<x>' + REPLACE(@Parameter,'_','</x><x>') + '</x>' AS XML).value('/x[2]','time') AS TimeParam
          ,CAST('<x>' + REPLACE(@Parameter,'_','</x><x>') + '</x>' AS XML).value('/x[3]','varchar(max)') AS TextParam
)
SELECT IntParam,TimeParam,TextParam
FROM MyParameters
/*
IntParam    TimeParam   TextParam
1           12:30:00    SomeValue
*/
GO

Split String: Transform to list

DECLARE @MyIDs VARCHAR(100)='3,5,7';
SELECT A.B.value('.','int') TheIntValue
FROM(SELECT CAST('<x>' + REPLACE(@MyIDs,',','</x><x>') + '</x>' AS XML) AS MyListAsXML) AS x
CROSS APPLY MyListAsXML.nodes('/x') AS A(B)

/*
TheIntValue
3
5
7
*/
GO

Dynamic IN Statement

DECLARE @tbl TABLE(ID INT,Content VARCHAR(max));
INSERT INTO @tbl VALUES(1,'Value 1'),(2,'Value 2'),(3,'Value 3'),(4,'Value 4'),(5,'Value 5'),(6,'Value 6'),(7,'Value 7');

DECLARE @MyIDs VARCHAR(100)='3,5,7';
/*
This won't work (due to the fact, that @MyIDs is not a list of INTs but a text
SELECT * FROM @tbl WHERE ID IN(@MyIDs)
*/
WITH AsList AS
(
    SELECT A.B.value('.','int') TheIntValue
    FROM(SELECT CAST('<x>' + REPLACE(@MyIDs,',','</x><x>') + '</x>' AS XML) AS MyListAsXML) AS x
    CROSS APPLY MyListAsXML.nodes('/x') AS A(B)

)
SELECT * FROM @tbl WHERE ID IN(SELECT TheIntValue FROM AsList)

/*
ID  Content
3   Value 3
5   Value 5
7   Value 7
*/
Shnugo
  • 66,100
  • 9
  • 53
  • 114
  • @VasistaKrishnaBaderdinni, I prefer this approach because of two reasons: First: You can pick all needed nodes immediately. Second: You get the values type safe... – Shnugo Nov 11 '15 at 19:24
  • also it solves my purpose as I dint wanted to join and then find or split, I wanted it on go as I am collecting the info into Python for data analysis – Vasista B Nov 16 '15 at 02:00
  • @VasistaKrishnaBaderdinni, I edited my answer. The approach is very usefull in many scenarios. Feel free to share this... – Shnugo Nov 16 '15 at 08:59
2

You can do this with nested string functions. Often, this is simpler using outer apply:

select t3.output
from t outer apply
     (select stuff(t.col, 1, charindex('_', t.col), '') as col2
     ) t2 outer apply
     (select left(t2.col2, charindex('_', t2.col2)) as output
     ) t3;
Gordon Linoff
  • 1,242,037
  • 58
  • 646
  • 786