'Unpivoting' a SQL table - sql

I'm looking to 'unpivot' a table, though I'm not sure what the best way of going about it, is. Additionally, the values are separated by a ';'. I've listed a sample of what I'm looking at:
​
Column_A
Column_B
Column_C
Column_D
000
A;B;C;D
01;02;03;04
X;Y;D;E
001
A;B
05;06
S;T
002
C
07
S
​
From that, I'm looking for a way to unpivot it, but also to keep the relations it's currently in. As in, the first value in Column_B, C, and D are tied together:
​
|Column_A|Column_B|Column_C|Column_D|
|:-|:-|:-|:-|
|000|A|01|X|
|000|B|02|Y|
|000|C|03|D|
|000|D|04|E|
|001|A|05|S|
And so on.
My initial thought is to use a CTE, which I've set up as:
WITH TEST AS(
SELECT DISTINCT Column_A, Column_B, Column_C, VALUE AS Column_D
from [TABLE]
CROSS APPLY STRING_SPLIT(Column_D, ';'))
SELECT \* FROM TEST
;
Though that doesn't seem to produce the correct results, especially after stacking the CTEs and string splits.
As an update, there were really helpful solutions below. They all ran as expected, however I had one last addition. Is it possible/reasonable to ignore a row/column if it's blank? For example, skipping over Column_C where Column_A is '001'.
|Column_A|Column_B|Column_C|Column_D|
|:-|:-|:-|:-|
|000|A;B;C;D|01;02;03;04|X;Y;D;E|
|001|A;B||S;T|
|002|C|07|S|

Here is a JSON based method. SQL Server 2016 onwards.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ColA varchar(3), ColB varchar(8000), ColC varchar(8000), ColD varchar(8000));
INSERT INTO #tbl VALUES
('000','A;B;C;D','01;02;03;04','X;Y;D;E'),
('001','A;B','05;06','S;T'),
('002','C','07','S');
-- DDL and sample data population, end
WITH rs AS
(
SELECT *
, ar1 = '["' + REPLACE(ColB, ';', '","') + '"]'
, ar2 = '["' + REPLACE(ColC, ';', '","') + '"]'
, ar3 = '["' + REPLACE(ColD, ';', '","') + '"]'
FROM #tbl
)
SELECT ColA, ColB.[value] AS [ColB], ColC.[value] AS ColC, ColD.[value] AS ColD
FROM rs
CROSS APPLY OPENJSON (ar1, N'$') AS ColB
CROSS APPLY OPENJSON (ar2, N'$') AS ColC
CROSS APPLY OPENJSON (ar3, N'$') AS ColD
WHERE ColB.[key] = ColC.[key]
AND ColB.[key] = ColD.[key];
Output
+------+------+------+------+
| ColA | ColB | ColC | ColD |
+------+------+------+------+
| 000 | A | 01 | X |
| 000 | B | 02 | Y |
| 000 | C | 03 | D |
| 000 | D | 04 | E |
| 001 | A | 05 | S |
| 001 | B | 06 | T |
| 002 | C | 07 | S |
+------+------+------+------+

You can use a recursive CTE to walk through the strings. Assuming they are all the same length (i.e. same number of semicolons):
with cte as (
select a, convert(varchar(max), null) as b, convert(varchar(max), null) as c, convert(varchar(max), null) as d,
convert(varchar(max), b + ';') as rest_b, convert(varchar(max), c + ';') as rest_c, convert(varchar(max), d + ';') as rest_d,
0 as lev
from t
union all
select a,
left(rest_b, charindex(';', rest_b) - 1),
left(rest_c, charindex(';', rest_c) - 1),
left(rest_d, charindex(';', rest_d) - 1),
stuff(rest_b, 1, charindex(';', rest_b), ''),
stuff(rest_c, 1, charindex(';', rest_c), ''),
stuff(rest_d, 1, charindex(';', rest_d), ''),
lev + 1
from cte
where rest_b <> ''
)
select a, b, c, d
from cte
where lev > 0
order by a, lev;
Here is a db<>fiddle.
EDIT:
You can ensure filter to use rows that have the same number of semicolons by using:
where (length(b) - length(replace(b, ';', ''))) = (length(c) - length(replace(c, ';', ''))) and
(length(b) - length(replace(b, ';', ''))) = (length(d) - length(replace(d, ';', '')))
You could also extend c and d with a bunch of semicolons so no error occurs and the resulting values are empty strings. Extra semicolons in those columns don't matter, so you could use:
select a, convert(varchar(max), null) as b, convert(varchar(max), null) as c, convert(varchar(max), null) as d,
convert(varchar(max), b + ';') as rest_b, convert(varchar(max), c + replicate(';', length(b))) as rest_c, convert(varchar(max), d + replicate(';', length(b))) as rest_d,
0 as lev

The real problem here is your design. Hopefully the reason you're doing this is to fix your design.
Unfortunately you can't use SQL Server's inbuilt STRING_SPLIT for this, as it doesn't provide an ordinal position. As such I use DelimitedSplit8K_LEAD to separate the values into rows, and then "join" then back up. This does assume that all the columns have the same number of delimited values.
CREATE TABLE dbo.YourTable (ColA varchar(3),
ColB varchar(8000),
ColC varchar(8000),
ColD varchar(8000));
INSERT INTO dbo.YourTable
VALUES('000','A;B;C;D','01;02;03;04','X;Y;D;E'),
('001','A;B','05;06','S;T'),
('002','C','07','S');
GO
SELECT YT.ColA,
DSLB.Item AS ColB,
DSLC.Item AS ColC,
DSLD.Item AS ColD
FROM dbo.YourTable YT
CROSS APPLY dbo.DelimitedSplit8K_LEAD(YT.ColB,';') DSLB
CROSS APPLY dbo.DelimitedSplit8K_LEAD(YT.ColC,';') DSLC
CROSS APPLY dbo.DelimitedSplit8K_LEAD(YT.ColD,';') DSLD
WHERE DSLB.ItemNumber = DSLC.ItemNumber
AND DSLC.ItemNumber = DSLD.ItemNumber;
GO
DROP TABLE dbo.YourTable;

Related

Split comma separated values of a multiple column in row in SQL query

I have data Like this
Code address phno
123 test1,test2,test3 123,456,789
And I want output
Code address phno
123 test1 123
123 test2 456
123 test3 789
My code is this
declare #address VARCHAR(500) = 'test1,test2,test3', #phoneno VARCHAR(500) = '123,456,789'
select *
from STRING_SPLIT(#address, ','), STRING_SPLIT(#phoneno, ',')
but it returns multiple values
You can use recursive CTEs for this:
with cte as (
select code, cast(NULL as varchar(max)) as address, cast(NULL as varchar(max)) as phno,
cast(address + ',' as varchar(max)) as rest_address, cast(phno + ',' as varchar(max)) as rest_phno, 0 as lev
from t
union all
select code, left(rest_address, charindex(',', rest_address) - 1), left(rest_phno, charindex(',', rest_phno) - 1),
stuff(rest_address, 1, charindex(',', rest_address), ''), stuff(rest_phno, 1, charindex(',', rest_phno), ''), lev + 1
from cte
where rest_address <> ''
)
select code, address, phno
from cte
where lev > 0;
Here is a db<>fiddle.
If you can have more than 100 elements in the lists, you'll need option (maxrecursion 0).
Please try the following solution.
SQL
DECLARE #tbl TABLE (Code CHAR(3), address VARCHAR(100), phno VARCHAR(100));
INSERT INTO #tbl (Code, address, phno) VALUES
('123', 'test1,test2,test3', '123,456,789');
;WITH cte1 AS
(
SELECT Code, value AS address
, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS seq
FROM #tbl
CROSS APPLY STRING_SPLIT(address, ',')
), cte2 AS
(
SELECT Code, value AS phno
, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS seq
FROM #tbl
CROSS APPLY STRING_SPLIT(phno, ',')
)
SELECT cte1.code, cte1.address, cte2.phno
FROM cte1 INNER JOIN cte2
ON cte2.seq = cte1.seq;
SQL #2, JSON based
;WITH rs AS
(
SELECT *
, ar1 = '["' + Replace(address, ',', '","') + '"]'
, ar2 = '["' + Replace(phno, ',', '","') + '"]'
FROM #tbl
)
SELECT Code, address.[value] AS [address], phno.[value] AS phno
FROM rs
CROSS APPLY OPENJSON (ar1, N'$') AS address
CROSS APPLY OPENJSON (ar2, N'$') AS phno
WHERE address.[key] = phno.[key];
Output
+------+---------+------+
| code | address | phno |
+------+---------+------+
| 123 | test1 | 123 |
| 123 | test2 | 456 |
| 123 | test3 | 789 |
+------+---------+------+

Concatenate or merge many columns values with a separator between and ignoring nulls - SQL Server 2016 or older

I want to simulate the CONCAT_WS SQL Server 2017+ function with SQL Server 2016 version or older in order to concatenate many columns which values are strings like that:
Input:
| COLUMN1 | COLUMN2 | COLUMN3 | COLUMN4 |
'A' 'B' NULL 'D'
NULL 'E' 'F' 'G'
NULL NULL NULL NULL
Output:
| MERGE |
'A|B|D'
'E|F|G'
NULL
Notice that the output result is a new column that concatenate all values separated by '|'. The default value should be NULL if there are no values in the columns.
I tried with CONCAT and a CASE statement with many WHEN conditions but is really dirty and I am not allowed to use this solution. Thanks in advance.
One convenient way is:
select stuff( coalesce(',' + column1, '') +
coalesce(',' + column2, '') +
coalesce(',' + column3, '') +
coalesce(',' + column4, ''), 1, 1, ''
)
Here is another method by using XML and XQuery.
The number of columns is not hard-coded, it could be dynamic.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (id INT IDENTITY PRIMARY KEY, col1 CHAR(1), col2 CHAR(1), col3 CHAR(1), col4 CHAR(1));
INSERT INTO #tbl (col1, col2, col3, col4) VALUES
( 'A', 'B', NULL, 'D'),
(NULL, 'E' , 'F' , 'G'),
(NULL, NULL, NULL , NULL);
-- DDL and sample data population, end
DECLARE #separator CHAR(1) = '|';
SELECT id, REPLACE((
SELECT *
FROM #tbl AS c
WHERE c.id = p.id
FOR XML PATH('r'), TYPE, ROOT('root')
).query('data(/root/r/*[local-name() ne "id"])').value('.', 'VARCHAR(100)') , SPACE(1), #separator) AS concatColumns
FROM #tbl AS p;
Output
+----+---------------+
| id | concatColumns |
+----+---------------+
| 1 | A|B|D |
| 2 | E|F|G |
| 3 | |
+----+---------------+

Extracting data between two delimiters in SQL Server?

I have a column that contains data in the format of aaa|bbb|ccc and need to extract aaa, bbb & ccc from the data separately.
I tried
SELECT
SUBSTRING(Column1, 0, CHARINDEX('|', Column1)) AS [First],
SUBSTRING(Column1, CHARINDEX('|', Column1) + 1, LEN(Column1)) AS [Second]
FROM
Table1
OUTPUT:
aaa [FIRST],bbb|ccc [Second]
but I need aaa [FIRST],bbb [Second],ccc [Third]
If you have a string that are exactly same format (three times) delimited by | then you can PARSENAME() :
select col1, parsename(cols, 3) fisrt, parsename(cols, 2) second, parsename(cols, 1) third
from table1 t1 cross apply
( values (replace(col1, '|', '.'))
) t2 (cols);
You can try below way where function charindex,SUBSTRING and REVERSE used
with t as (select 'aaa|bbb|ccc' as val)
select t.*,
LEFT(val, charindex('|', val) - 1),
SUBSTRING(val, charindex('|', val)+1, len(val) - CHARINDEX('|', reverse(val)) - charindex('|', val)),
REVERSE(LEFT(reverse(val), charindex('|', reverse(val)) - 1))
from t;
val
aaa|bbb|ccc aaa bbb ccc
You can split the value in the column using a common table expression (CTE) since split function is not available in your version of SQL Server
WITH CTE(userString,startIndex,EndIndex)
AS
(
SELECT Column1,1,CHARINDEX('|',Column1)
FROM Table1
UNION ALL
SELECT Column1, EndIndex+1,CHARINDEX('|',Column1,EndIndex+1)
FROM CTE
WHERE EndIndex !=0
)
SELECT SUBSTRING(userString,
startIndex,
CASE WHEN EndIndex > 0
THEN EndIndex - startIndex ELSE LEN(Column1) END)
as splitVALUES
FROM CTE
I do this all the time and it works well for me:
DECLARE #delimString VARCHAR(255) = 'aaa|bbb|ccc';
DECLARE #xml XML = '<val>' + REPLACE( #delimString, '|', '</val><val>' ) + '</val>'
SELECT
x.f.value( '.', 'VARCHAR(50)' ) AS val
FROM #xml.nodes( '//val' ) x( f );
Returns
+-----+
| val |
+-----+
| aaa |
| bbb |
| ccc |
+-----+
If you're looking for a columnar return and know you will always only have three values to parse, you might be able to get away with something like the below example. You can run it in SSMS.
DECLARE #table TABLE ( [value] VARCHAR(255) );
INSERT INTO #table ( [value] ) VALUES
( 'aaa|bbb|ccc' )
, ( '0A-PRDS|JQLM-1|1967' )
, ( 'J1658|G-1|2003' );
SELECT
[value]
, SUBSTRING( [value], 0, CHARINDEX( '|', [value] ) ) AS Column1
, SUBSTRING(
[value]
, ( CHARINDEX( '|', [value]) + 1 ) -- starting position of column 2.
, CHARINDEX( '|', [value], ( CHARINDEX( '|', [value] ) + 1 ) ) - ( CHARINDEX( '|', [value]) + 1 ) -- length of column two is the number of characters between the two delimiters.
) AS Column2
, SUBSTRING(
[value]
, CHARINDEX( '|', [value], ( CHARINDEX( '|', [value] ) + 1 ) ) + 1
, LEN( [value] )
) AS Column3
FROM #table;
Returns
+---------------------+---------+---------+---------+
| value | Column1 | Column2 | Column3 |
+---------------------+---------+---------+---------+
| aaa|bbb|ccc | aaa | bbb | ccc |
| 0A-PRDS|JQLM-1|1967 | 0A-PRDS | JQLM-1 | 1967 |
| J1658|G-1|2003 | J1658 | G-1 | 2003 |
+---------------------+---------+---------+---------+

Sql Query Join on Comma Separated Value

I have a table that has a composite key and a comma separated value. I need the single row split into one row for each comma separated element. I have seen similar questions and similar answers but have not been able to translate them into a solution for myself.
I'm running SQL Server 2008 R2.
| Key Part 1 | Key Part 2 | Key Part 3 | Values |
|------------------------------------------------------|
| A | A | A | PDE,PPP,POR |
| A | A | B | PDE,XYZ |
| A | B | A | PDE,RRR |
|------------------------------------------------------|
and I need this as output
| Key Part 1 | Key Part 2 | Key Part 3 | Values | Sequence |
|-------------------------------------------------------------------|
| A | A | A | PDE | 0 |
| A | A | A | PPP | 1 |
| A | A | A | POR | 2 |
| A | A | B | PDE | 0 |
| A | A | B | XYZ | 1 |
| A | B | A | PDE | 0 |
| A | B | A | RRR | 1 |
|-------------------------------------------------------------------|
Thanks
Geoff
Here is a simple inline approach if you don't have or want a Split/Parse UDF
Example
Select A.[Key Part 1]
,A.[Key Part 2]
,A.[Key Part 3]
,B.*
From YourTable A
Cross Apply (
Select [Values] = LTrim(RTrim(X2.i.value('(./text())[1]', 'varchar(max)')))
,[Sequence] = Row_Number() over (Order By (Select null))-1
From (Select x = Cast('<x>' + replace(A.[Values],',','</x><x>')+'</x>' as xml)) X1
Cross Apply x.nodes('x') X2(i)
) B
Returns
EDIT - If Open to a Table-Valued Function
The Query would Look Like This
Select A.[Key Part 1]
,A.[Key Part 2]
,A.[Key Part 3]
,[Values] = B.RetVal
,[Sequence] = B.RetSeq-1
From #YourTable A
Cross Apply [dbo].[udf-Str-Parse-8K](A.[Values],',') B
The UDF if Interested
CREATE FUNCTION [dbo].[udf-Str-Parse-8K] (#String varchar(max),#Delimiter varchar(25))
Returns Table
As
Return (
with cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (IsNull(DataLength(#String),0)) Row_Number() over (Order By (Select NULL)) From (Select N=1 From cte1 a,cte1 b,cte1 c,cte1 d) A ),
cte3(N) As (Select 1 Union All Select t.N+DataLength(#Delimiter) From cte2 t Where Substring(#String,t.N,DataLength(#Delimiter)) = #Delimiter),
cte4(N,L) As (Select S.N,IsNull(NullIf(CharIndex(#Delimiter,#String,s.N),0)-S.N,8000) From cte3 S)
Select RetSeq = Row_Number() over (Order By A.N)
,RetVal = LTrim(RTrim(Substring(#String, A.N, A.L)))
From cte4 A
);
--Orginal Source http://www.sqlservercentral.com/articles/Tally+Table/72993/
--Select * from [dbo].[udf-Str-Parse-8K]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse-8K]('John||Cappelletti||was||here','||')
If all CSV values are exactly 3 characters (as you have in your test data) you can use a a tally table in an incredibly efficient manner by creating the exact number of rows needed up front (as opposed to creating a row for every character to find the delimiter character)... because you already know the delimiter location.
In this case, I'll use a tally function but you can use a fixed tally table as well.
Code for the tfn_Tally function...
SET QUOTED_IDENTIFIER ON
SET ANSI_NULLS ON
GO
CREATE FUNCTION dbo.tfn_Tally
/* ============================================================================
07/20/2017 JL, Created. Capable of creating a sequense of rows
ranging from -10,000,000,000,000,000 to 10,000,000,000,000,000
============================================================================ */
(
#NumOfRows BIGINT,
#StartWith BIGINT
)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
WITH
cte_n1 (n) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (n)), -- 10 rows
cte_n2 (n) AS (SELECT 1 FROM cte_n1 a CROSS JOIN cte_n1 b), -- 100 rows
cte_n3 (n) AS (SELECT 1 FROM cte_n2 a CROSS JOIN cte_n2 b), -- 10,000 rows
cte_n4 (n) AS (SELECT 1 FROM cte_n3 a CROSS JOIN cte_n3 b), -- 100,000,000 rows
cte_Tally (n) AS (
SELECT TOP (#NumOfRows)
(ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1) + #StartWith
FROM
cte_n4 a CROSS JOIN cte_n4 b -- 10,000,000,000,000,000 rows
)
SELECT
t.n
FROM
cte_Tally t;
GO
How to use it in the solution...
-- create some test data...
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
CREATE TABLE #TestData (
KeyPart1 CHAR(1),
KeyPart2 CHAR(1),
KeyPart3 CHAR(1),
[Values] varchar(50)
);
INSERT #TestData (KeyPart1, KeyPart2, KeyPart3, [Values]) VALUES
('A', 'A', 'A', 'PDE,PPP,POR'),
('A', 'A', 'B', 'PDE,XYZ'),
('A', 'B', 'A', 'PDE,RRR,XXX,YYY,ZZZ,AAA,BBB,CCC');
--==========================================================
-- solution query...
SELECT
td.KeyPart1,
td.KeyPart2,
td.KeyPart3,
x.SplitValue,
[Sequence] = t.n
FROM
#TestData td
CROSS APPLY dbo.tfn_Tally(LEN(td.[Values]) - LEN(REPLACE(td.[Values], ',', '')) + 1, 0) t
CROSS APPLY ( VALUES (SUBSTRING(td.[Values], t.n * 4 + 1, 3)) ) x (SplitValue);
And the results...
KeyPart1 KeyPart2 KeyPart3 SplitValue Sequence
-------- -------- -------- ---------- --------------------
A A A PDE 0
A A A PPP 1
A A A POR 2
A A B PDE 0
A A B XYZ 1
A B A PDE 0
A B A RRR 1
A B A XXX 2
A B A YYY 3
A B A ZZZ 4
A B A AAA 5
A B A BBB 6
A B A CCC 7
If the assumption that all of the csv elements are the number of characters is incorrect, you'd be better off using a traditional tally based splitter. In which case my recommendation is DelimitedSplit8K written by Jeff Moden.
In that case, the solution query would look like this...
SELECT
td.KeyPart1,
td.KeyPart2,
td.KeyPart3,
SplitValue = dsk.Item,
[Sequence] = dsk.ItemNumber - 1
FROM
#TestData td
CROSS APPLY dbo.DelimitedSplit8K(td.[Values], ',') dsk;
Ann the result...
KeyPart1 KeyPart2 KeyPart3 SplitValue Sequence
-------- -------- -------- ---------- --------------------
A A A PDE 0
A A A PPP 1
A A A POR 2
A A B PDE 0
A A B XYZ 1
A B A PDE 0
A B A RRR 1
A B A XXX 2
A B A YYY 3
A B A ZZZ 4
A B A AAA 5
A B A BBB 6
A B A CCC 7
HTH, Jason
-- Create Table
Create table YourTable
(
p1 varchar(50),
p2 varchar(50),
p3 varchar(50),
pval varchar(50)
)
go
-- Insert Data
insert into YourTable values ('A','A','A','PDE,PPP,POR'),
('A','A','B','PDE,XYZ'),('A','B','A','PDE,RRR')
go
-- View Sample Data
SELECT p1, p2, p3 , pval FROM YourTable
go
-- Required Result
SELECT p1,p2,p3, LTRIM(RTRIM(Split.a.value('.', 'VARCHAR(100)'))) as Value1 , ROW_NUMBER() OVER(PARTITION BY id ORDER BY id ASC)-1 AS SequenceNo
FROM
(SELECT ROW_NUMBER() over (order by (SELECT NULL)) AS ID, p1,p2,p3, pval, CAST ('<M>' + REPLACE(pval, ',', '</M><M>') + '</M>' AS XML) AS Data from YourTable
) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)
go
-- Remove Temp created table
drop table YourTable
go

SQL Column Concatenation

I want to concatenate the column names in my query result if the column is true. Below is the format of my data, and the format of the intended output as well as a snippet of the SQL I attempted.
DATA
ID | ColA | ColB | ColC | ColD
1 | T | F | T | F
2 | F | T | F | T
3 | T | F | T | F
4 | F | T | F | T
5 | T | F | T | F
INTENDED OUTPUT
ID | TYPE
1 | ColA,ColC
2 | ColB,ColD
3 | ColA,ColC
4 | ColB,ColD
5 | ColA,ColC
ATTEMPTED SQL
SELECT UniqueIDColumn,
CASE WHEN ColA = 1 THEN 'A' END + ', ' +
CASE WHEN ColB = 1 THEN 'B' END + ', ' +
CASE WHEN ColC = 1 THEN 'C' END + ', ' +
CASE WHEN ColD = 1 THEN 'D' END
FROM TableName
Easily achievable using Unpivot operator:
;WITH unpivoted AS
(
SELECT Id, Val, Col
FROM (SELECT ID, ColA, ColB, ColC, ColD FROM #Data) AS Up
UNPIVOT (Val FOR Col IN (ColA, ColB, ColC, ColD)) AS Upv
)
SELECT Id, STUFF((SELECT ', ' + Col FROM unpivoted WHERE Id = up.Id AND Val = 'T' FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') AS [Type]
FROM unpivoted up
GROUP BY Id
You have to write a function that receives two parameter, and, if the first parameter is true, returns the second parameter plus a delimiter (e.g., space), otherwise just the delimiter:
DROP FUNCTION IF EXISTS returnNameIfTrue;
DELIMITER $$
CREATE FUNCTION returnNameIfTrue(columnValue int, columnName varchar(10))
RETURNS varchar(10)
BEGIN
declare s varchar(10);
IF columnValue = 1 THEN
set s = concat(columnName , ' ');
else
set s = ' ';
END IF;
RETURN s;
END;
$$
DELIMITER ;
Then, the solution will be:
select id, rtrim(concat(returnNameIfTrue(colA, 'colA'),
returnNameIfTrue(colB, 'colB'),
returnNameIfTrue(colC, 'colC'),
returnNameIfTrue(colD, 'colD'))) as type
from tableName
Or, if you also want to get rid of additional space:
select id, replace(replace(replace(rtrim(concat(returnNameIfTrue(colA, 'colA'),
returnNameIfTrue(colB, 'colB'),
returnNameIfTrue(colC, 'colC'),
returnNameIfTrue(colD, 'colD'))), ' ', ' '), ' ', ' '), ' ', ' ') as type
from tableName