How to delimit string(s) and convert to column headers? - sql

How can you take a string(s) and convert to a column (dynamically as the source string(s) will change):
Example:
(select *
from table
inner join
(select column1, [dynamic field] from table) as dynamic_data
on table.column1 = dynamic_data.column1)
Column1
------
a,b,c
c,d,e
a,f,e
to this
column1 a b c d e f
-------------------
a,b,c |x|x|x| | | |
c,d,e | | |x|x|x| |
a,f,e |x| | | |x|x|

Use like and case:
select column1,
(case when ',' + column1 + ',' like '%,a,%' then 'x' end) as a,
(case when ',' + column1 + ',' like '%,b,%' then 'x' end) as b,
(case when ',' + column1 + ',' like '%,c,%' then 'x' end) as c,
(case when ',' + column1 + ',' like '%,d,%' then 'x' end) as d,
(case when ',' + column1 + ',' like '%,e,%' then 'x' end) as e,
(case when ',' + column1 + ',' like '%,f,%' then 'x' end) as f
from t;
I am not sure why "dynamic" is necessary. The issue isn't the source strings, but the destination columns. If you need those to match the source strings, then you do need to use dynamic SQL . . . and that seems rather complicated.
EDIT:
The heart of the dynamic SQL is putting together the case expressions. You can do this using string_split() and string_agg() (or equivalent functions in older versions of SQL Server):
select string_agg(replace(' (case when '','' + column1 + '','' like ''%,[value],%'' then ''x'' end) as [value]', '[value]', value), '
'
) within group (order by value) as cols
from (select distinct value
from t cross apply
string_split(t.column1, ',')
) t
Here is a db<>fiddle.
I'll let you construct the rest of the query.

Related

Create new column by each value separated by ";" SQL Server SUBSTRING

I have this 'Tests' column with n Rows
1 Test0;Test1;Test2
2 Test3;Test5;Test8
...
...
I need to separate each value by semicolon and create 1 column for each index.
This should be the result:
Column New1:
Test0
Test3
Column New2:
Test1
Test5
Column New3:
Test2
Test8
I think this is probably close enough to what you want:
select t.*, s.*
from t cross apply
(select max(case when seqnum = 1 then s.value end) as test_1,
max(case when seqnum = 2 then s.value end) as test_2,
max(case when seqnum = 3 then s.value end) as test_3
from (select s.value,
row_number() over (order by charindex(';' + s.value + ';', ';' + t.tests + ';')) as seqnum
from string_split(t.tests, ';') s
) s
) s;
Here is a db<>fiddle.
Note: This approach will not work if there are duplicates in the column.
Solved here How to split one column into two columns in SQL Server
(Note: this approach is made to split one column into two columns)
declare #t table (id int, name varchar(50))
select
case when CHARINDEX(';', Column)>0
then SUBSTRING(Column, 1, CHARINDEX(';', Column)-1)
else Column end Column1,
CASE WHEN CHARINDEX(';', Column)>0
THEN SUBSTRING(Column, CHARINDEX(';',Column)+1,len(Column))
ELSE NULL END as Column2
from #t
Try this
select SUBSTRING(Tests,1,CHARINDEX(';', Tests)-1) as New1,
SUBSTRING(Tests,CHARINDEX(';', Tests)+1,CHARINDEX(';', Tests,CHARINDEX(';', Tests)-1)-1) as New2,
SUBSTRING(Tests,CHARINDEX(';', Tests,CHARINDEX(';', Tests)+1)+1,len(Tests)) as New3

Convert all Rows to Columns in SQL Server

Trying to Convert Rows to Multiple columns in SQL Server as show below:
Current result:
PortCode CarCode
------------------------
AAB A5
ADR BH
SAN QQ
Expected result:
PortCode CarCode PortCode CarCode PortCode CarCode
-----------------------------------------------------------
AAB A5 ADR BH SAN QQ
Tried withPIVOT but didn't helped.
Can anyone please explain me, how to achieve it?
If I understand correctly,
select max(case when seqnum = 1 then portcode end) as portcode_1,
max(case when seqnum = 1 then carcode end) as carcode_1,
max(case when seqnum = 2 then portcode end) as portcode_2,
max(case when seqnum = 2 then carcode end) as carcode_2,
max(case when seqnum = 3 then portcode end) as portcode_3,
max(case when seqnum = 3 then carcode end) as carcode_3
from (select t.*, row_number() over (order by (select null)) as seqnum
from t
) t;
Notes:
This is not dynamic. It produces 6 columns (but you can use the same idea for a dynamic query).
The ordering of the results is indeterminate. SQL tables represent unordered sets. If you want the columns in a particular order, then replace (select null) with the appropriate column.
If you want to make it dynamic, you can use the following sql query.
Query
declare #sql as varchar(max);
select #sql = 'select ' + stuff((
select distinct ',max(case [PortCode] when ' + char(39) + [PortCode] + char(39) +
' then [PortCode] end) as [PortCode]'
+ ',max(case [CarCode] when ' + char(39) + [CarCode] + char(39) +
' then [CarCode] end) as [CarCode]'
from [your_table_name]
for xml path('')
), 1, 1, '');
select #sql += ' from [your_table_name];';
exec(#sql);

Replace a value in a comma separated string in SQL Server database

I'm using a SQL Server 2014 database and I have a column that contains comma-separated values such as:
1,2,3
4,5
3,6,2
4,2,8
2
What I need to do is to replace the number 2 with the number 3 (string values) in each record and not duplicate the 3 if possible. I'm not sure that this can be done unless I use a function and I'm not sure how to do it in a function.
I think I need to split a string into a table and then loop the values and put it back together with the new value. Is there an easier way? Any help is appreciated.
Expect output would therefore be:
1,3
4,5
3,6
4,3,8
3
While it is possible, I do not encourage this:
DECLARE #old AS VARCHAR(3) = '2';
DECLARE #new AS VARCHAR(3) = '3';
WITH opdata(csv) AS (
SELECT '1,22,3' UNION ALL
SELECT '1,2,3' UNION ALL
SELECT '4,5' UNION ALL
SELECT '3,6,2' UNION ALL
SELECT '4,2,8' UNION ALL
SELECT '2'
), cte1 AS (
SELECT
csv,
CASE
WHEN ',' + csv + ',' LIKE '%,' + #old + ',%' THEN
CASE
WHEN ',' + csv + ',' LIKE '%,' + #new + ',%' THEN REPLACE(',' + csv + ',', ',' + #old + ',', ',') -- new already present so just delete old
ELSE REPLACE(',' + csv + ',', ',' + #old + ',', ',' + #new + ',') -- replace old with new
END
ELSE ',' + csv + ','
END AS tmp
FROM opdata
)
SELECT
csv,
STUFF(STUFF(tmp, 1, 1, ''), LEN(tmp) - 1, 1, '') AS res
FROM cte1
Result:
csv | res
-------+-------
1,22,3 | 1,22,3
1,2,3 | 1,3
4,5 | 4,5
3,6,2 | 3,6
4,2,8 | 4,3,8
2 | 3
Note that the plethora of ',...,' is required to avoid replacing values such as 22. If you are using SQL Server 2017 you can ditch the extra CTE + STUFF and use TRIM(',' FROM ...).
This isn't going to perform particularly well, however:
WITH CTE AS (
SELECT *
FROM (VALUES ('1,2,3'),
('4,5'),
('3,6,2'),
('4,2,8'),
('2')) V(DS))
SELECT CASE WHEN DS LIKE '%3%' THEN REPLACE(REPLACE(DS,'2,',''),',2','')
WHEN DS LIKE '%2%' THEN REPLACE(DS,'2','3')
ELSE DS
END
FROM CTE;
May be you are looking something like this.
SELECT REPLACE(CASE WHEN CHARINDEX('2', '1,2,3') > 0 THEN REPLACE('1,2,3', '2','') ELSE REPLACE('1,2,3', '2','3') END, ',,',',')
I have taken a hard coded value for demonstration. You can replace'1,2,3' with column name in the table.
To update:
DECLARE #was nvarchar(2) = 2,
#willbe nvarchar(2) = 3,
#d nvarchar(1) = ','
UPDATE strings
SET string = REVERSE(
STUFF(
REVERSE(
STUFF(
CASE WHEN CHARINDEX(#d+#willbe+#d,#d+string+#d) > 0
THEN REPLACE(#d+string+#d,#d+#was+#d,#d)
ELSE REPLACE(#d+string+#d,#d+#was+#d,#d+#willbe+#d)
END,1,1,'')
),1,1,''))
Output:
1,3
4,5
3,6
4,3,8
3

SQL count string matches

Please take a look at this simple SQL server database :
I want the result to have 3 column, and the column "CountString" is the total number of string that matches ('this','is', 'count', 'example').
I have managed to detect those words using this query, but it can`t detect multiple words :
SELECT
productid,
NAME,
((CASE
WHEN Concat(' ', NAME, ' ') LIKE '% this %' THEN 1
ELSE 0
END) + (CASE
WHEN Concat(' ', NAME, ' ') LIKE '% is %' THEN 1
ELSE 0
END) + (CASE
WHEN Concat(' ', NAME, ' ') LIKE '% count %' THEN 1
ELSE 0
END) + (CASE
WHEN
Concat(' ', NAME, ' ') LIKE '% example %' THEN 1
ELSE 0
END)) AS CountString
FROM product;
However, if the name for productID 1 is "this is count this example". I want it to be counted as 5. Could you solve this ?
Create Table product(productid int, NAME varchar(100))
Insert Into product Values(1,'this is this example')
Insert Into product Values(2,'this is this this count this example')
SELECT productid,count(*) as CountString
FROM
(
SELECT A.[productid],
Split.a.value('.', 'VARCHAR(100)') AS String
FROM (SELECT [productid],
CAST ('<M>' + REPLACE([NAME], ' ', '</M><M>') + '</M>' AS XML) AS String
FROM product) AS A
CROSS APPLY String.nodes ('/M') AS Split(a)
) As Word
WHERE String in ('this','is','count','example')
Group by productid
Try this
DECLARE #TableString TABLE(ID INT IDENTITY,String nvarchar(max))
INSERT INTO #TableString(String)
SELECT 'this is count this example' UNION ALL
SELECT 'Bearing Ball' UNION ALL
SELECT 'BB Ball Bearing ' UNION ALL
SELECT 'this is example'
-- Here the delimeter is space
SELECT id AS productid, COUNT(stringValue) AS StringValueCount FROM
(
SELECT id ,
Split.a.value('.', 'VARCHAR(1000)') AS stringValue
FROM (
SELECT id,CAST('<S>' + REPLACE(String, ' ', '</S><S>') + '</S>' AS XML) AS String
FROM #TableString
) AS A
CROSS APPLY String.nodes('/S') AS Split(a)
)Dt
WHERE dt.stringValue in ('this','is','count','example')
GROUP BY id
Result
productid StringValueCount
-----------------------------
1 5
4 3

Get values of 2 columns as single value separated with '|'

DECLARE #mockup TABLE(Column1 VARCHAR(1),Column2 VARCHAR(1));
INSERT INTO #mockup VALUES('1','2'),('-','2'),('1','2'),('-','-'),('2','-'),('1','2');
SELECT ISNULL(NULLIF(Column1 + '|','-|'),'')
+ISNULL(NULLIF(Column2,'-'),'')
FROM #mockup
Above query result is as below,
1|2
2
1|2
2|
1|2
I want the result as above only except row4, where 2| should be only as 2 .
'|' should not be there at before or end of the values.
Simply join both fields and use REPLACE to remove |- and -|. Condition in WHERE clause avoids records where both fields are -:
select replace(replace(Column1+'|'+Column2,'-|',''),'|-','') as Result
from mockup
where coalesce(nullif(Column1,'-'),nullif(Column2,'-')) is not null
Output:
Result
1|2
2
1|2
2
1|2
See result here.
Use Replace function
SELECT replace(replace(replace(Column1 + '|' + Column2,'-|',''),'|-',''),'-','')
FROM #mockup
or try using CASE statement
SELECT CASE
WHEN column1 LIKE '[0-9]' AND column2 LIKE '[0-9]' THEN column1 + '|' + column1
WHEN column1 LIKE '[0-9]' AND column2 NOT LIKE '[0-9]' THEN column1
ELSE column2
END
FROM #mockup
if you want to check the - instead of numbers then
SELECT CASE
WHEN column1 NOT LIKE '-' AND column2 NOT LIKE '-' THEN column1 + '|' + column1
WHEN column1 NOT LIKE '-' AND column2 LIKE '-' THEN column1
ELSE column2
END
FROM #mockup
I would do this as:
select stuff( ((case when column1 not like '-' then '|' + column1 else '' end) +
(case when column2 not like '-' then '|' + column2 else '' end)
), 1, 1, '');
This is the simplest way that I've found to implement concat_ws() in SQL Server. concat_ws() is a function available in other databases, where you would just do:
select concat_ws('|',
(case when column1 not like '-' then column1 end),
(case when column2 not like '-' then column2 end)
)