Searching Through Table from comma delimited column using SQL - sql-server

I have data in the below format, I would like to find the users that match any and all of the words within the comma delimited skills column:
Name | id | skills |
-------------------------------------------------------
Bbarker | 5987 | Needles, Pins, Surgery, Word, Excel |
CJerald | 5988 | Bartender, Shots |
RSarah | 5600 | Pins, Ground, Hot, Coffee |
So if I am searching for "Needles, Pins", it should return Bbarker and RSarahs rows.
How would I achieve something like this using SQL ?
I dont even know where to begin or what to search for, any help in the right direction would be great!
Thanks!

Poor design aside, sometimes we are stuck and have to deal with that poor design.
I agree that if you have the option of redesigning I would pursue that route, in the meantime there are ways you can deal with delimited data.
If you are SQL Server version 2016+ there is a built in function call STRING_SLIT() that can be used.
If you are prior to SQL Server 2016 you basically have to convert to XML as a workaround
Here's a working example of both you can explore:
DECLARE #TestData TABLE
(
[Name] NVARCHAR(100)
, [Id] INT
, [skills] NVARCHAR(100)
);
--Test data
INSERT INTO #TestData (
[Name]
, [Id]
, [skills]
)
VALUES ( 'Bbarker', 5987, 'Needles, Pins, Surgery, Word, Excel' )
, ( 'CJerald', 5988, 'Bartender, Shots' )
, ( 'RSarah', 5600, 'Pins, Ground, Hot, Coffee' );
--search words
DECLARE #Search NVARCHAR(100) = 'Needles, Pins';
--sql server 2016+ using STING_SPLIT
SELECT DISTINCT [a].*
FROM #TestData [a]
CROSS APPLY STRING_SPLIT([a].[skills], ',') [sk] --split your column
CROSS APPLY STRING_SPLIT(#Search, ',') [srch] --split your search
WHERE LTRIM(RTRIM([sk].[value])) = LTRIM(RTRIM([srch].[value])); --filter where they equal
--Prior to sql server 2016, convert XML
SELECT DISTINCT [td].*
FROM #TestData [td]
--below we are converting to xml and then spliting those out for your column
CROSS APPLY (
SELECT [Split].[a].[value]('.', 'NVARCHAR(MAX)') [value]
FROM (
SELECT CAST('<X>' + REPLACE([td].[skills], ',', '</X><X>') + '</X>' AS XML) AS [String]
) AS [A]
CROSS APPLY [String].[nodes]('/X') AS [Split]([a])
) AS [sk]
--same here for the search
CROSS APPLY (
SELECT [Split].[a].[value]('.', 'NVARCHAR(MAX)') [value]
FROM (
SELECT CAST('<X>' + REPLACE(#Search, ',', '</X><X>') + '</X>' AS XML) AS [String]
) AS [A]
CROSS APPLY [String].[nodes]('/X') AS [Split]([a])
) AS [srch]
WHERE LTRIM(RTRIM([sk].[value])) = LTRIM(RTRIM([srch].[value])); --then as before where those are equal
Both will get you the output of:
Name Id skills
---------- ------- ------------------------------------
Bbarker 5987 Needles, Pins, Surgery, Word, Excel
RSarah 5600 Pins, Ground, Hot, Coffee

How about this?
SELECT DISTINCT Name, id
FROM table
WHERE skills LIKE '%Needles%'
OR skills LIKE '%Pins%'

Related

How to split a string from a table SQL Server 2012?

I'd like to split comma-delimited strings in SQL Server 2012. I'm interested in an XML solution, not a function or while loop (performance and permissions reasons). I read this post: STRING_SPLIT in SQL Server 2012 which was helpful, however, my context is not splitting a variable but rather a column in a table. Below is an example of the kind of dataset I'm working with:
CREATE TABLE #EXAMPLE
(
ID INT,
LIST VARCHAR(1000)
)
INSERT INTO #EXAMPLE
VALUES (1, '12345,54321'), (2, '48965'), (3, '98765,45678,15935'), (4, '75315')
SELECT * FROM #EXAMPLE
DROP TABLE #EXAMPLE
Given that dataset, how could I go about splitting the LIST field on the comma so that I get this data set?
CREATE TABLE #EXAMPLE
(
ID INT,
LIST VARCHAR(1000)
)
INSERT INTO #EXAMPLE
VALUES (1, '12345'), (1, '54321'), (2, '48965'), (3, '98765'), (3, '45678'), (3, '15935'), (4, '75315')
SELECT * FROM #EXAMPLE
DROP TABLE #EXAMPLE
I feel like I'm blanking on implementing this with a table column as opposed to a variable, but I'm sure it's pretty similar. I'd be greatly appreciative of any input. Thanks!
If you want an XML solution the following should hopefully suffice.
Note - this is easily wrapped in a reusable table-valued function however you state you don't want a function so just using in-line.
select e.id, s.List
from #example e
cross apply (
select List = y.i.value('(./text())[1]', 'varchar(max)')
from (
select x = convert(xml, '<i>' + replace(e.list, ',', '</i><i>') + '</i>').query('.')
) as a cross apply x.nodes('i') as y(i)
)s
See working Fiddle
Taking into account your link, this can be done by slightly changing the query by adding Cross Apply.
Select e.ID, t.a
From #Example As e Cross Apply (
SELECT Split.a.value('.', 'NVARCHAR(MAX)') DATA
FROM
(
SELECT CAST('<X>'+REPLACE(e.List, ',', '</X><X>')+'</X>' AS XML) AS String
) AS A
CROSS APPLY String.nodes('/X') AS Split(a)) As t(a)
As #Charlieface already mentioned there is a risk to bump into XML entities: ampersand and the like.
That's why I always use a CDATA section for safety.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT, LIST VARCHAR(1000));
INSERT INTO #tbl VALUES
(1, '12345,<54321'),
(2, '48965'),
(3, '98765,45678,15935'),
(4, '75315');
-- DDL and sample data population, end
SELECT e.id, s.List
FROM #tbl e
CROSS APPLY (
SELECT List = y.i.value('(./text())[1]', 'VARCHAR(MAX)')
FROM (
SELECT x = TRY_CAST('<i><![CDATA[' + REPLACE(e.list, ',', ']]></i><i><![CDATA[') + ']]></i>' AS XML)
) AS a CROSS APPLY x.nodes('i') as y(i)
) AS s;
Output
+----+--------+
| id | List |
+----+--------+
| 1 | 12345 |
| 1 | <54321 |
| 2 | 48965 |
| 3 | 98765 |
| 3 | 45678 |
| 3 | 15935 |
| 4 | 75315 |
+----+--------+

SQL Server XML Processing: Join different Nodes based on ID

I am trying to query XML with SQL. Suppose I have the following XML.
<xml>
<dataSetData>
<text>ABC</text>
</dataSetData>
<generalData>
<id>123</id>
<text>text data</text>
</generalData>
<generalData>
<id>456</id>
<text>text data 2</text>
</generalData>
<specialData>
<id>123</id>
<text>special data text</text>
</specialData>
<specialData>
<id>456</id>
<text>special data text 2</text>
</specialData>
</xml>
I want to write a SELECT query that returns 2 rows as follows:
DataSetData | GeneralDataID | GeneralDataText | SpecialDataTest
ABC | 123 | text data | special data text
ABC | 456 | text data 2 | special data text 2
My current approach is as follows:
SELECT
dataset.nodes.value('(dataSetData/text)[1]', 'nvarchar(500)'),
general.nodes.value('(generalData/text)[1]', 'nvarchar(500)'),
special.nodes.value('(specialData/text)[1]', 'nvarchar(500)'),
FROM #MyXML.nodes('xml') AS dataset(nodes)
OUTER APPLY #MyXML.nodes('xml/generalData') AS general(nodes)
OUTER APPLY #MyXML.nodes('xml/specialData') AS special(nodes)
WHERE
general.nodes.value('(generalData/text/id)[1]', 'nvarchar(500)') = special.nodes.value('(specialData/text/id)[1]', 'nvarchar(500)')
What I do not like here is that I have to use OUTER APPLY twice and that I have to use the WHERE clause to JOIN the correct elements.
My question therefore is: Is it possible to construct the query in a way where I do not have to use the WHERE clause in such a way, because I am pretty sure that this affects performance very negatively if files become larger.
Shouldn't it be possible to JOIN the correct nodes (that is, the corresponding generalData and specialData nodes) with some XPATH statement?
Your XPath expressions are completely off.
Please try the following. It is pretty efficient. You can test its performance with a large XML.
SQL
-- DDL and sample data population, start
DECLARE #xml XML =
N'<xml>
<dataSetData>
<text>ABC</text>
</dataSetData>
<generalData>
<id>123</id>
<text>text data</text>
</generalData>
<generalData>
<id>456</id>
<text>text data 2</text>
</generalData>
<specialData>
<id>123</id>
<text>special data text</text>
</specialData>
<specialData>
<id>456</id>
<text>special data text 2</text>
</specialData>
</xml>';
-- DDL and sample data population, end
SELECT c.value('(dataSetData/text/text())[1]', 'VARCHAR(20)') AS DataSetData
, g.value('(id/text())[1]', 'INT') AS GeneralDataID
, g.value('(text/text())[1]', 'VARCHAR(30)') AS GeneralDataText
, sp.value('(id/text())[1]', 'INT') AS SpecialDataID
, sp.value('(text/text())[1]', 'VARCHAR(30)') AS SpecialDataTest
FROM #xml.nodes('/xml') AS t(c)
OUTER APPLY c.nodes('generalData') AS general(g)
OUTER APPLY c.nodes('specialData') AS special(sp)
WHERE g.value('(id/text())[1]', 'INT') = sp.value('(id/text())[1]', 'INT');
Output
+-------------+---------------+-----------------+---------------+---------------------+
| DataSetData | GeneralDataID | GeneralDataText | SpecialDataID | SpecialDataTest |
+-------------+---------------+-----------------+---------------+---------------------+
| ABC | 123 | text data | 123 | special data text |
| ABC | 456 | text data 2 | 456 | special data text 2 |
+-------------+---------------+-----------------+---------------+---------------------+
I want to suggest one more solution:
DECLARE #xml XML=
N'<xml>
<dataSetData>
<text>ABC</text>
</dataSetData>
<generalData>
<id>123</id>
<text>text data</text>
</generalData>
<generalData>
<id>456</id>
<text>text data 2</text>
</generalData>
<specialData>
<id>123</id>
<text>special data text</text>
</specialData>
<specialData>
<id>456</id>
<text>special data text 2</text>
</specialData>
</xml>';
--The query
SELECT #xml.value('(/xml/dataSetData/text/text())[1]','varchar(100)')
,B.*
,#xml.value('(/xml/specialData[(id/text())[1] cast as xs:int? = sql:column("B.General_Id")]/text/text())[1]','varchar(100)') AS Special_Text
FROM #xml.nodes('/xml/generalData') A(gd)
CROSS APPLY(SELECT A.gd.value('(id/text())[1]','int') AS General_Id
,A.gd.value('(text/text())[1]','varchar(100)') AS General_Text) B;
The idea in short:
We can read the <dataSetData>, as it is not repeating, directly from the variable.
We can use .nodes() to get a derived set of all <generalData> entries.
Now the magic trick: I use APPLY to get the values from the XML as regular columns into the result set.
This trick allows now to use sql:column() in order to build a XQuery predicate to find the corresponding <specialData>.
One more approach with FLWOR
You might try this:
SELECT #xml.query
('
<xml>
{
for $i in distinct-values(/xml/generalData/id/text())
return
<combined dsd="{/xml/dataSetData/text/text()}"
id="{$i}"
gd="{/xml/generalData[id=$i]/text/text()}"
sd="{/xml/specialData[id=$i]/text/text()}"/>
}
</xml>
');
The result
<xml>
<combined dsd="ABC" id="123" gd="text data" sd="special data text" />
<combined dsd="ABC" id="456" gd="text data 2" sd="special data text 2" />
</xml>
The idea in short:
With the help of distinct-values() we get a list of all id values in your XML
we can iterate this and pick the corresponding values
We return the result as a re-structured XML
Now you can use .nodes('/xml/combined') against this new XML and retrieve all values easily.
Performance test
I just want to add a performance test:
CREATE TABLE dbo.TestXml(TheXml XML);
INSERT INTO dbo.TestXml VALUES
(
(
SELECT 'blah1' AS [dataSetData/text]
,(SELECT o.[object_id] AS [id]
,o.[name] AS [text]
FROM sys.objects o
FOR XML PATH('generalData'),TYPE)
,(SELECT o.[object_id] AS [id]
,o.create_date AS [text]
FROM sys.objects o
FOR XML PATH('specialData'),TYPE)
FOR XML PATH('xml'),TYPE
)
)
,(
(
SELECT 'blah2' AS [dataSetData/text]
,(SELECT o.[object_id] AS [id]
,o.[name] AS [text]
FROM sys.objects o
FOR XML PATH('generalData'),TYPE)
,(SELECT o.[object_id] AS [id]
,o.create_date AS [text]
FROM sys.objects o
FOR XML PATH('specialData'),TYPE)
FOR XML PATH('xml'),TYPE
)
)
,(
(
SELECT 'blah3' AS [dataSetData/text]
,(SELECT o.[object_id] AS [id]
,o.[name] AS [text]
FROM sys.objects o
FOR XML PATH('generalData'),TYPE)
,(SELECT o.[object_id] AS [id]
,o.create_date AS [text]
FROM sys.objects o
FOR XML PATH('specialData'),TYPE)
FOR XML PATH('xml'),TYPE
)
);
GO
--just a dummy call to avoid *first call bias*
SELECT x.query('.') FROM dbo.TestXml
CROSS APPLY TheXml.nodes('/xml//*') A(x)
GO
DECLARE #t DATETIME2=SYSUTCDATETIME();
--My first approach
SELECT TheXml.value('(/xml/dataSetData/text/text())[1]','varchar(100)') AS DataSetValue
,B.*
,TheXml.value('(/xml/specialData[(id/text())[1] cast as xs:int? = sql:column("B.General_Id")]/text/text())[1]','varchar(100)') AS Special_Text
INTO dbo.testResult1
FROM dbo.TestXml
CROSS APPLY TheXml.nodes('/xml/generalData') A(gd)
CROSS APPLY(SELECT A.gd.value('(id/text())[1]','int') AS General_Id
,A.gd.value('(text/text())[1]','varchar(100)') AS General_Text) B;
SELECT DATEDIFF(MILLISECOND,#t,SYSUTCDATETIME());
GO
DECLARE #t DATETIME2=SYSUTCDATETIME();
--My second approach
SELECT B.c.value('#dsd','varchar(100)') AS dsd
,B.c.value('#id','int') AS id
,B.c.value('#gd','varchar(100)') AS gd
,B.c.value('#sd','varchar(100)') AS sd
INTO dbo.TestResult2
FROM dbo.TestXml
CROSS APPLY (SELECT TheXml.query
('
<xml>
{
for $i in distinct-values(/xml/generalData/id/text())
return
<combined dsd="{/xml/dataSetData/text/text()}"
id="{$i}"
gd="{/xml/generalData[id=$i]/text/text()}"
sd="{/xml/specialData[id=$i]/text/text()}"/>
}
</xml>
') AS ResultXml) A
CROSS APPLY A.ResultXml.nodes('/xml/combined') B(c)
SELECT DATEDIFF(MILLISECOND,#t,SYSUTCDATETIME());
GO
DECLARE #t DATETIME2=SYSUTCDATETIME();
--Yitzhak'S approach
SELECT c.value('(dataSetData/text/text())[1]', 'VARCHAR(20)') AS DataSetData
, g.value('(id/text())[1]', 'INT') AS GeneralDataID
, g.value('(text/text())[1]', 'VARCHAR(30)') AS GeneralDataText
, sp.value('(id/text())[1]', 'INT') AS SpecialDataID
, sp.value('(text/text())[1]', 'VARCHAR(30)') AS SpecialDataTest
INTO dbo.TestResult3
FROM dbo.TestXml
CROSS APPLY TheXml.nodes('/xml') AS t(c)
OUTER APPLY c.nodes('generalData') AS general(g)
OUTER APPLY c.nodes('specialData') AS special(sp)
WHERE g.value('(id/text())[1]', 'INT') = sp.value('(id/text())[1]', 'INT');
SELECT DATEDIFF(MILLISECOND,#t,SYSUTCDATETIME());
GO
SELECT * FROM TestResult1;
SELECT * FROM TestResult2;
SELECT * FROM TestResult3;
GO
--careful with real data!
DROP TABLE testResult1
DROP TABLE testResult2
DROP TABLE testResult3
DROP TABLE dbo.TestXml;
The result is clearly pointing against XQuery. (Someone might say so sad! now :-) ).
The predicate approach is by far the slowest (4700ms). The FLWOR approach is on rank 2 (1200ms) and the winner is - tatatataaaaa - Yitzhak's approach (400ms, by factor ~10!).
Which solution is best for you, will depend on the actual data (count of elements per XML, count of XMLs and so on). But the visual elegance is - regrettfully - not the only parameter for this choice :-)
Sorry to add this as another answer, but I don't want to add to the other answer. It's big enough already :-)
A combination of Yitzhak's and mine is even faster:
--This is the additional code to be placed into the performance comparison
DECLARE #t DATETIME2=SYSUTCDATETIME();
SELECT TheXml.value('(/xml/dataSetData/text/text())[1]', 'VARCHAR(20)') AS DataSetData
,B.*
, sp.value('(id/text())[1]', 'INT') AS SpecialDataID
, sp.value('(text/text())[1]', 'VARCHAR(30)') AS SpecialDataTest
INTO dbo.TestResult4
FROM dbo.TestXml
CROSS APPLY TheXml.nodes('/xml/generalData') AS A(g)
CROSS APPLY(SELECT g.value('(id/text())[1]', 'INT') AS GeneralDataID
, g.value('(text/text())[1]', 'VARCHAR(30)') AS GeneralDataText) B
OUTER APPLY TheXml.nodes('/xml/specialData[id=sql:column("B.GeneralDataID")]') AS special(sp);
SELECT DATEDIFF(MILLISECOND,#t,SYSUTCDATETIME());
The idea in short:
We read the <dataSetData> directly (no repetition)
We use APPLY .nodes() to get all <generalData>
We use APPLY SELECT to fetch the values of <generalData> elements as real columns.
We use another APPLY .nodes() to fetch the corresponding <specialData> elements
One advantage of this solution: If there might be more than one special-data entry per general-data element, this would work too.
This is now the fastest in my test (~300ms).

Split a string in SQL by hyphen in 2012 version

I have multiple string in a column where I have get last string after column
Below are three example like same I have different number hyphen that can occur in a string but desired result is I have string before last hyphen
1. abc-def-Opto
2. abc-def-ijk-5C-hello-Opto
3. abc-def-ijk-4C-hi-Build
4. abc-def-ijk-4C-123-suppymanagement
Desired result set is
def
hello
hi
123
How to do this in SQL query to get this result set. I have MSSQL 2012 version
Require a generic sql which can get the result set
There are many ways to split/parse a string. ParseName() would fail because you may have more than 4 positions.
One option (just for fun), is to use a little XML.
We reverse the string
Convert into XML
Grab the second node
Reverse the desired value for the final presentation
Example
Declare #YourTable Table ([SomeCol] varchar(50))
Insert Into #YourTable Values
('abc-def-Opto')
,('abc-def-ijk-5C-hello-Opto')
,('abc-def-ijk-4C-hi-Build')
,('abc-def-ijk-4C-123-suppymanagement')
Select *
,Value = reverse(convert(xml,'<x>'+replace(reverse(SomeCol),'-','</x><x>')+'</x>').value('x[2]','varchar(150)'))
from #YourTable
Returns
SomeCol Value
abc-def-Opto def
abc-def-ijk-5C-hello-Opto hello
abc-def-ijk-4C-hi-Build hi
abc-def-ijk-4C-123-suppymanagement 123
Without getting into XML stuff, simply using string functions of sql server.
Declare #YourTable Table ([SomeCol] varchar(50))
Insert Into #YourTable Values
('abc-def-Opto')
,('abc-def-ijk-5C-hello-Opto')
,('abc-def-ijk-4C-hi-Build')
,('abc-def-ijk-4C-123-suppymanagement');
SELECT *
,RTRIM(LTRIM(REVERSE(
SUBSTRING(
SUBSTRING(REVERSE([SomeCol]) , CHARINDEX('-', REVERSE([SomeCol])) +1 , LEN([SomeCol]) )
, 1 , CHARINDEX('-', SUBSTRING(REVERSE([SomeCol]) , CHARINDEX('-', REVERSE([SomeCol])) +1 , LEN([SomeCol]) ) ) -1
)
)))
FROM #YourTable
i am not sure this script will exactly useful to your requirement but i am just trying to give an idea how to split the data
IF OBJECT_ID('tempdb..#Temp')IS NOT NULL
DROP TABLE #Temp
;WITH CTE(Id,data)
AS
(
SELECT 1,'abc-def-Opto' UNION ALL
SELECT 2,'abc-def-ijk-5C-hello-Opto' UNION ALL
SELECT 3,'abc-def-ijk-4C-hi-Build' UNION ALL
SELECT 4,'abc-def-ijk-4C-123-suppymanagement'
)
,Cte2
AS
(
SELECT Id, CASE WHEN Id=1 AND Setdata=1 THEN data
WHEN Id=2 AND Setdata=2 THEN data
WHEN Id=3 AND Setdata=3 THEN data
WHEN Id=4 AND Setdata=4 THEN data
ELSE NULL
END AS Data
FROM
(
SELECT Id,
Split.a.value('.','nvarchar(1000)') AS Data,
ROW_NUMBER()OVER(PARTITION BY id ORDER BY id) AS Setdata
FROM(
SELECT Id,
CAST('<S>'+REPLACE(data ,'-','</S><S>')+'</S>' AS XML) AS data
FROM CTE
) AS A
CROSS APPLY data.nodes('S') AS Split(a)
)dt
)
SELECT * INTO #Temp FROM Cte2
SELECT STUFF((SELECT DISTINCT ', '+ 'Set_'+CAST(Id AS VARCHAR(10))+':'+Data
FROM #Temp WHERE ISNULL(Data,'')<>'' FOR XML PATH ('')),1,1,'')
Result
Set_1:abc, Set_2:def, Set_3:ijk, Set_4:4C
You can do like
WITH CTE AS
(
SELECT 1 ID,'abc-def-Opto' Str
UNION
SELECT 2, 'abc-def-ijk-5C-hello-Opto'
UNION
SELECT 3, 'abc-def-ijk-4C-hi-Build'
UNION
SELECT 4, 'abc-def-ijk-4C-123-suppymanagement'
)
SELECT ID,
REVERSE(LEFT(REPLACE(P2, P1, ''), CHARINDEX('-', REPLACE(P2, P1, ''))-1)) Result
FROM (
SELECT LEFT(REVERSE(Str), CHARINDEX('-', REVERSE(Str))) P1,
REVERSE(Str) P2,
ID
FROM CTE
) T;
Returns:
+----+--------+
| ID | Result |
+----+--------+
| 1 | def |
| 2 | hello |
| 3 | hi |
| 4 | 123 |
+----+--------+
Demo

MSSQL how to select string from the right of the third delimiter (counting from the right)

So my question is as follows:-
I have multiple strings with variable amounts of delimiters, the text between the delimiters can also vary in number:-
fug\klde\hzt\jkljlkjlkjl\hgftb\jghgf\ooorr\ter\fdgd
wegf\df\jght\kfd\dfgert
What I need to do is to cut the string and leave only the following from the examples:-
ooorr\ter\fdgd
jght\kfd\dfgert
so basically the third delimiter from the right side.
I have been able to use RIGHT, CHARINDEX and REVERSE to give me the last part of the string(s) but I am struggling for the rest.
Any help would be appreciated thanks in advance.
Text processing is something you might require to do in presentation layer but naive way is to do like below:
Select Substring(col1, len(col1) - CharIndex('\', reverse(col1), Charindex('\',reverse(col1),charindex('\', reverse(col1),1)+1)+1)+2, len(col1)) from #delimiterdata
Output as below:
+-----------------+
| Output |
+-----------------+
| ooorr\ter\fdgd |
| jght\kfd\dfgert |
+-----------------+
Another option is with a little XML in concert with a a Cross Apply
Example
Declare #YourTable table (ID int,SomeCol varchar(max))
Insert Into #YourTable values
(1,'fug\klde\hzt\jkljlkjlkjl\hgftb\jghgf\ooorr\ter\fdgd')
,(2,'wegf\df\jght\kfd\dfgert')
,(3,'kfd\dfgert')
Select A.ID
,NewValue = reverse(Concat(Pos1,'\'+Pos2,'\'+Pos3))
From #YourTable A
Cross Apply (
Select Pos1 = n.value('/x[1]','varchar(max)')
,Pos2 = n.value('/x[2]','varchar(max)')
,Pos3 = n.value('/x[3]','varchar(max)')
From (Select Cast('<x>' + replace((Select replace(reverse(A.SomeCol),'\','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) n) X
) B
Returns
ID NewValue
1 ooorr\ter\fdgd
2 jght\kfd\dfgert
3 kfd\dfgert --<<Note: Doesn't have 3 but will produce the last two
Using NGrams8K you could do this:
-- sample data
declare #yourtable table (someId int identity, someString varchar(1000));
insert #yourtable
values ('fug\klde\hzt\jkljlkjlkjl\hgftb\jghgf\ooorr\ter\fdgd'),('wegf\df\jght\kfd\dfgert');
with stringPrep AS
(
select
someId,
someString,
dPos = ROW_NUMBER() OVER (partition by t.someid order by ng.position desc),
position
from #yourtable t
cross apply dbo.NGrams8k(t.someString, 1) ng
where token = '\'
)
select
someId,
someString,
newString = substring(someString, position+1, 1000)
from stringPrep
where dpos = 3;
Results
someId someString newString
------- ------------------------------------------------------------ -----------------
1 fug\klde\hzt\jkljlkjlkjl\hgftb\jghgf\ooorr\ter\fdgd ooorr\ter\fdgd
2 wegf\df\jght\kfd\dfgert jght\kfd\dfgert

Pivot Table with varchar values

this is the first-time I use this and I hope i will achieve this, I am getting mad. I am still a newbie about all this programming with SQL thing, I am trying to use pivot table to turn the source table in the desired output showed as below.
This is my source table
Here is the image, I was unable to upload it (https://i.stack.imgur.com/KDStF.jpg).
But whatever I try it returns me numbers but the varchar values I would like to, as it is shown above.
Would any of you know how to achieve it?
I am using Transact SQL in Sql Server, here is my code
SELECT [Ques1], [Ques2]
FROM
(
SELECT
D.DocumentId,
DA.QuestionId,
Q.ShortText,
DA.[Text],
Q.SectionId,
1 as [Count]
-- ShortText is literally 'Ques1' or 'Ques2'
FROM Document D
INNER JOIN DocumentAnswer DA
ON DA.DocumentId = D.DocumentId
INNER JOIN Question Q
ON Q.QuestionId = DA.QuestionId
WHERE D.DeleteDate IS NULL
) d
PIVOT
(
Max(ShortText)
FOR [Text] IN ([Ques1], [Ques2])
) p
SQL Fiddle
MS SQL Server 2008 Schema Setup:
Query 1:
DECLARE #TABLE TABLE (DocID INT, Ques VARCHAR(100), Ans VARCHAR(100))
INSERT INTO #TABLE VALUES
(1 , 'Ques1' , 'Hola'),
(1 , 'Ques2' , 'Padr'),
(2 , 'Ques1' , 'Excue'),
(2 , 'Ques2' , 'Dir')
SELECT * FROM
( -- Put your existing query here
SELECT * FROM #TABLE
) t
PIVOT (MAX(Ans)
FOR Ques
IN ([Ques1],[Ques2])
)p
Results:
| DOCID | QUES1 | QUES2 |
|-------|-------|-------|
| 1 | Hola | Padr |
| 2 | Excue | Dir |

Resources