How to efficiently concatenate fields that may be null? - sql

I have a front-end field in which the user should see the values of several fields, concatenated with commas. Which is easy enough if the fields are always populated, but that is not the case. In order to avoid extra commas and spaces I came up with:
concat(
[field_one],
case when [field_one] is not null and [field_two] is not null then ', ' end,
[field_two],
case when ([field_two] is not null or [field_one] is not null) and [field_three] is not null then ', ' end,
[field_three]
) as [list_field]
Which works well enough for three fields. But the specs have changed and more fields will be added. The case statements will quickly get out of hand since they're referencing all previous fields. Is there an efficient way to do something like this?

Here is one method:
select stuff( coalesce(', ' + field_one, '') +
coalesce(', ' + field_two, '') +
coalesce(', ' + field_three, ''), 1, 2, '')
The three expressions add a ', ' before each non-null field. The stuff() removes the first one.

The NullIf() is to trap empty/non-null values. Optional if you only have nulls and values.
Example
Declare #YourTable table (field_one varchar(50),field_two varchar(50),field_three varchar(50))
Insert Into #YourTable values
('a','b','c')
,('a',null,'c')
,('a',null,null)
,('a','b',null)
,(null,'b',null)
Select *
,list_field = stuff( concat(
', '+nullif([field_one],'')
,', '+nullif([field_two],'')
,', '+nullif([field_three],'')
)
,1,2,'')
From #YourTable
Returns
field_one field_two field_three list_field
a b c a, b, c
a NULL c a, c
a NULL NULL a
a b NULL a, b
NULL b NULL b

You can actually take advantage of the fact that NULL + anything = NULL and the CONCAT function's NULL handling ability. It makes for a cleaner syntax without all of the COALESCE / ISNULL gunk...
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
CREATE TABLE #TestData (
Col_1 VARCHAR(20) NULL,
Col_2 VARCHAR(20) NULL,
Col_3 VARCHAR(20) NULL
);
INSERT #TestData (Col_1, Col_2, Col_3) VALUES
('Bob', 'A.', 'Jones'),
('123 Some St.', NULL, 'Jacksonville'),
(NULL, 'R.', 'Smith'),
('Roy', 'Finch', NULL),
(NULL, NULL, 'Prince'),
(NULL, NULL, NULL),
('Arnold', NULL, NULL);
SELECT
ConcatString = CONCAT(td.Col_1 + ' ', td.Col_2 + ' ', td.Col_3)
FROM
#TestData td;
Output...
ConcatString
--------------------------------------------------------------
Bob A. Jones
123 Some St. Jacksonville
R. Smith
Roy Finch
Prince
Arnold

I have not tested it, but I believe that this will work on any ANSI compliant database. This probably presumes that the fields are CHAR type or that you will convert them with something like CONVERT() in SQL Server or TO_CHAR() in Oracle.
SELECT
CONCAT(
COALESCE(field_one, '')
,',',COALESCE(field_two, '')
,',',COALESCE(field_three, '')
)
FROM ...
Here is a question about STUFF(). Oracle equivalent to SQL Server STUFF function?

Related

SQL concatenate value to every row based on condition [duplicate]

Consider a database table holding names, with three rows:
Peter
Paul
Mary
Is there an easy way to turn this into a single string of Peter, Paul, Mary?
If you are on SQL Server 2017 or Azure, see Mathieu Renda answer.
I had a similar issue when I was trying to join two tables with one-to-many relationships. In SQL 2005 I found that XML PATH method can handle the concatenation of the rows very easily.
If there is a table called STUDENTS
SubjectID StudentName
---------- -------------
1 Mary
1 John
1 Sam
2 Alaina
2 Edward
Result I expected was:
SubjectID StudentName
---------- -------------
1 Mary, John, Sam
2 Alaina, Edward
I used the following T-SQL:
SELECT Main.SubjectID,
LEFT(Main.Students,Len(Main.Students)-1) As "Students"
FROM
(
SELECT DISTINCT ST2.SubjectID,
(
SELECT ST1.StudentName + ',' AS [text()]
FROM dbo.Students ST1
WHERE ST1.SubjectID = ST2.SubjectID
ORDER BY ST1.SubjectID
FOR XML PATH (''), TYPE
).value('text()[1]','nvarchar(max)') [Students]
FROM dbo.Students ST2
) [Main]
You can do the same thing in a more compact way if you can concat the commas at the beginning and use substring to skip the first one so you don't need to do a sub-query:
SELECT DISTINCT ST2.SubjectID,
SUBSTRING(
(
SELECT ','+ST1.StudentName AS [text()]
FROM dbo.Students ST1
WHERE ST1.SubjectID = ST2.SubjectID
ORDER BY ST1.SubjectID
FOR XML PATH (''), TYPE
).value('text()[1]','nvarchar(max)'), 2, 1000) [Students]
FROM dbo.Students ST2
This answer may return unexpected results For consistent results, use one of the FOR XML PATH methods detailed in other answers.
Use COALESCE:
DECLARE #Names VARCHAR(8000)
SELECT #Names = COALESCE(#Names + ', ', '') + Name
FROM People
Just some explanation (since this answer seems to get relatively regular views):
Coalesce is really just a helpful cheat that accomplishes two things:
1) No need to initialize #Names with an empty string value.
2) No need to strip off an extra separator at the end.
The solution above will give incorrect results if a row has a NULL Name value (if there is a NULL, the NULL will make #Names NULL after that row, and the next row will start over as an empty string again. Easily fixed with one of two solutions:
DECLARE #Names VARCHAR(8000)
SELECT #Names = COALESCE(#Names + ', ', '') + Name
FROM People
WHERE Name IS NOT NULL
or:
DECLARE #Names VARCHAR(8000)
SELECT #Names = COALESCE(#Names + ', ', '') +
ISNULL(Name, 'N/A')
FROM People
Depending on what behavior you want (the first option just filters NULLs out, the second option keeps them in the list with a marker message [replace 'N/A' with whatever is appropriate for you]).
SQL Server 2017+ and SQL Azure: STRING_AGG
Starting with the next version of SQL Server, we can finally concatenate across rows without having to resort to any variable or XML witchery.
STRING_AGG (Transact-SQL)
Without grouping
SELECT STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department;
With grouping:
SELECT GroupName, STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department
GROUP BY GroupName;
With grouping and sub-sorting
SELECT GroupName, STRING_AGG(Name, ', ') WITHIN GROUP (ORDER BY Name ASC) AS Departments
FROM HumanResources.Department
GROUP BY GroupName;
One method not yet shown via the XML data() command in SQL Server is:
Assume a table called NameList with one column called FName,
SELECT FName + ', ' AS 'data()'
FROM NameList
FOR XML PATH('')
returns:
"Peter, Paul, Mary, "
Only the extra comma must be dealt with.
As adopted from #NReilingh's comment, you can use the following method to remove the trailing comma. Assuming the same table and column names:
STUFF(REPLACE((SELECT '#!' + LTRIM(RTRIM(FName)) AS 'data()' FROM NameList
FOR XML PATH('')),' #!',', '), 1, 2, '') as Brands
In SQL Server 2005
SELECT Stuff(
(SELECT N', ' + Name FROM Names FOR XML PATH(''),TYPE)
.value('text()[1]','nvarchar(max)'),1,2,N'')
In SQL Server 2016
you can use the FOR JSON syntax
i.e.
SELECT per.ID,
Emails = JSON_VALUE(
REPLACE(
(SELECT _ = em.Email FROM Email em WHERE em.Person = per.ID FOR JSON PATH)
,'"},{"_":"',', '),'$[0]._'
)
FROM Person per
And the result will become
Id Emails
1 abc#gmail.com
2 NULL
3 def#gmail.com, xyz#gmail.com
This will work even your data contains invalid XML characters
the '"},{"_":"' is safe because if you data contain '"},{"_":"', it will be escaped to "},{\"_\":\"
You can replace ', ' with any string separator
And in SQL Server 2017, Azure SQL Database
You can use the new STRING_AGG function
In MySQL, there is a function, GROUP_CONCAT(), which allows you to concatenate the values from multiple rows. Example:
SELECT 1 AS a, GROUP_CONCAT(name ORDER BY name ASC SEPARATOR ', ') AS people
FROM users
WHERE id IN (1,2,3)
GROUP BY a
Use COALESCE - Learn more from here
For an example:
102
103
104
Then write the below code in SQL Server,
Declare #Numbers AS Nvarchar(MAX) -- It must not be MAX if you have few numbers
SELECT #Numbers = COALESCE(#Numbers + ',', '') + Number
FROM TableName where Number IS NOT NULL
SELECT #Numbers
The output would be:
102,103,104
PostgreSQL arrays are awesome. Example:
Create some test data:
postgres=# \c test
You are now connected to database "test" as user "hgimenez".
test=# create table names (name text);
CREATE TABLE
test=# insert into names (name) values ('Peter'), ('Paul'), ('Mary');
INSERT 0 3
test=# select * from names;
name
-------
Peter
Paul
Mary
(3 rows)
Aggregate them in an array:
test=# select array_agg(name) from names;
array_agg
-------------------
{Peter,Paul,Mary}
(1 row)
Convert the array to a comma-delimited string:
test=# select array_to_string(array_agg(name), ', ') from names;
array_to_string
-------------------
Peter, Paul, Mary
(1 row)
DONE
Since PostgreSQL 9.0 it is even easier, quoting from deleted answer by "horse with no name":
select string_agg(name, ',')
from names;
Oracle 11g Release 2 supports the LISTAGG function. Documentation here.
COLUMN employees FORMAT A50
SELECT deptno, LISTAGG(ename, ',') WITHIN GROUP (ORDER BY ename) AS employees
FROM emp
GROUP BY deptno;
DEPTNO EMPLOYEES
---------- --------------------------------------------------
10 CLARK,KING,MILLER
20 ADAMS,FORD,JONES,SCOTT,SMITH
30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD
3 rows selected.
Warning
Be careful implementing this function if there is possibility of the resulting string going over 4000 characters. It will throw an exception. If that's the case then you need to either handle the exception or roll your own function that prevents the joined string from going over 4000 characters.
In SQL Server 2005 and later, use the query below to concatenate the rows.
DECLARE #t table
(
Id int,
Name varchar(10)
)
INSERT INTO #t
SELECT 1,'a' UNION ALL
SELECT 1,'b' UNION ALL
SELECT 2,'c' UNION ALL
SELECT 2,'d'
SELECT ID,
stuff(
(
SELECT ','+ [Name] FROM #t WHERE Id = t.Id FOR XML PATH('')
),1,1,'')
FROM (SELECT DISTINCT ID FROM #t ) t
A recursive CTE solution was suggested, but no code was provided. The code below is an example of a recursive CTE.
Note that although the results match the question, the data doesn't quite match the given description, as I assume that you really want to be doing this on groups of rows, not all rows in the table. Changing it to match all rows in the table is left as an exercise for the reader.
;WITH basetable AS (
SELECT
id,
CAST(name AS VARCHAR(MAX)) name,
ROW_NUMBER() OVER (Partition BY id ORDER BY seq) rw,
COUNT(*) OVER (Partition BY id) recs
FROM (VALUES
(1, 'Johnny', 1),
(1, 'M', 2),
(2, 'Bill', 1),
(2, 'S.', 4),
(2, 'Preston', 5),
(2, 'Esq.', 6),
(3, 'Ted', 1),
(3, 'Theodore', 2),
(3, 'Logan', 3),
(4, 'Peter', 1),
(4, 'Paul', 2),
(4, 'Mary', 3)
) g (id, name, seq)
),
rCTE AS (
SELECT recs, id, name, rw
FROM basetable
WHERE rw = 1
UNION ALL
SELECT b.recs, r.ID, r.name +', '+ b.name name, r.rw + 1
FROM basetable b
INNER JOIN rCTE r ON b.id = r.id AND b.rw = r.rw + 1
)
SELECT name
FROM rCTE
WHERE recs = rw AND ID=4
OPTION (MAXRECURSION 101)
I don't have access to a SQL Server at home, so I'm guess at the syntax here, but it's more or less:
DECLARE #names VARCHAR(500)
SELECT #names = #names + ' ' + Name
FROM Names
In SQL Server 2017 or later versions, you can use the STRING_AGG() function to generate comma-separated values. Please have a look below at one example.
SELECT
VendorId, STRING_AGG(FirstName,',') UsersName
FROM Users
WHERE VendorId != 9
GROUP BY VendorId
You need to create a variable that will hold your final result and select into it, like so.
Easiest Solution
DECLARE #char VARCHAR(MAX);
SELECT #char = COALESCE(#char + ', ' + [column], [column])
FROM [table];
PRINT #char;
In SQL Server vNext this will be built in with the STRING_AGG function. Read more about it in STRING_AGG (Transact-SQL).
A ready-to-use solution, with no extra commas:
select substring(
(select ', '+Name AS 'data()' from Names for xml path(''))
,3, 255) as "MyList"
An empty list will result in NULL value.
Usually you will insert the list into a table column or program variable: adjust the 255 max length to your need.
(Diwakar and Jens Frandsen provided good answers, but need improvement.)
This worked for me (SQL Server 2016):
SELECT CarNamesString = STUFF((
SELECT ',' + [Name]
FROM tbl_cars
FOR XML PATH('')
), 1, 1, '')
Here is the source: https://www.mytecbits.com/
And a solution for MySQL (since this page show up in Google for MySQL):
SELECT [Name],
GROUP_CONCAT(DISTINCT [Name] SEPARATOR ',')
FROM tbl_cars
From MySQL documentation.
Using XML helped me in getting rows separated with commas. For the extra comma we can use the replace function of SQL Server. Instead of adding a comma, use of the AS 'data()' will concatenate the rows with spaces, which later can be replaced with commas as the syntax written below.
REPLACE(
(select FName AS 'data()' from NameList for xml path(''))
, ' ', ', ')
SELECT STUFF((SELECT ', ' + name FROM [table] FOR XML PATH('')), 1, 2, '')
Here's a sample:
DECLARE #t TABLE (name VARCHAR(10))
INSERT INTO #t VALUES ('Peter'), ('Paul'), ('Mary')
SELECT STUFF((SELECT ', ' + name FROM #t FOR XML PATH('')), 1, 2, '')
--Peter, Paul, Mary
With the other answers, the person reading the answer must be aware of a specific domain table such as vehicle or student. The table must be created and populated with data to test a solution.
Below is an example that uses SQL Server "Information_Schema.Columns" table. By using this solution, no tables need to be created or data added. This example creates a comma separated list of column names for all tables in the database.
SELECT
Table_Name
,STUFF((
SELECT ',' + Column_Name
FROM INFORMATION_SCHEMA.Columns Columns
WHERE Tables.Table_Name = Columns.Table_Name
ORDER BY Column_Name
FOR XML PATH ('')), 1, 1, ''
)Columns
FROM INFORMATION_SCHEMA.Columns Tables
GROUP BY TABLE_NAME
On top of Chris Shaffer's answer:
If your data may get repeated, such as
Tom
Ali
John
Ali
Tom
Mike
Instead of having Tom,Ali,John,Ali,Tom,Mike
You can use DISTINCT to avoid duplicates and get Tom,Ali,John,Mike:
DECLARE #Names VARCHAR(8000)
SELECT DISTINCT #Names = COALESCE(#Names + ',', '') + Name
FROM People
WHERE Name IS NOT NULL
SELECT #Names
MySQL complete example:
We have users who can have much data and we want to have an output, where we can see all users' data in a list:
Result:
___________________________
| id | rowList |
|-------------------------|
| 0 | 6, 9 |
| 1 | 1,2,3,4,5,7,8,1 |
|_________________________|
Table Setup:
CREATE TABLE `Data` (
`id` int(11) NOT NULL,
`user_id` int(11) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1;
INSERT INTO `Data` (`id`, `user_id`) VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 1),
(5, 1),
(6, 0),
(7, 1),
(8, 1),
(9, 0),
(10, 1);
CREATE TABLE `User` (
`id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `User` (`id`) VALUES
(0),
(1);
Query:
SELECT User.id, GROUP_CONCAT(Data.id ORDER BY Data.id) AS rowList FROM User LEFT JOIN Data ON User.id = Data.user_id GROUP BY User.id
DECLARE #Names VARCHAR(8000)
SELECT #name = ''
SELECT #Names = #Names + ',' + Names FROM People
SELECT SUBSTRING(2, #Names, 7998)
This puts the stray comma at the beginning.
However, if you need other columns, or to CSV a child table you need to wrap this in a scalar user defined field (UDF).
You can use XML path as a correlated subquery in the SELECT clause too (but I'd have to wait until I go back to work because Google doesn't do work stuff at home :-)
To avoid null values you can use CONCAT()
DECLARE #names VARCHAR(500)
SELECT #names = CONCAT(#names, ' ', name)
FROM Names
select #names
I really liked elegancy of Dana's answer and just wanted to make it complete.
DECLARE #names VARCHAR(MAX)
SET #names = ''
SELECT #names = #names + ', ' + Name FROM Names
-- Deleting last two symbols (', ')
SET #sSql = LEFT(#sSql, LEN(#sSql) - 1)
If you want to deal with nulls you can do it by adding a where clause or add another COALESCE around the first one.
DECLARE #Names VARCHAR(8000)
SELECT #Names = COALESCE(COALESCE(#Names + ', ', '') + Name, #Names) FROM People
This answer will require some privilege on the server to work.
Assemblies are a good option for you. There are a lot of sites that explain how to create it. The one I think is very well explained is this one.
If you want, I have already created the assembly, and it is possible to download the DLL file here.
Once you have downloaded it, you will need to run the following script in your SQL Server:
EXEC sp_configure 'show advanced options', 1
RECONFIGURE;
EXEC sp_configure 'clr strict security', 1;
RECONFIGURE;
CREATE Assembly concat_assembly
AUTHORIZATION dbo
FROM '<PATH TO Concat.dll IN SERVER>'
WITH PERMISSION_SET = SAFE;
GO
CREATE AGGREGATE dbo.concat (
#Value NVARCHAR(MAX)
, #Delimiter NVARCHAR(4000)
) RETURNS NVARCHAR(MAX)
EXTERNAL Name concat_assembly.[Concat.Concat];
GO
sp_configure 'clr enabled', 1;
RECONFIGURE
Observe that the path to assembly may be accessible to server. Since you have successfully done all the steps, you can use the function like:
SELECT dbo.Concat(field1, ',')
FROM Table1
Since SQL Server 2017 it is possible to use the STRING_AGG function.
I usually use select like this to concatenate strings in SQL Server:
with lines as
(
select
row_number() over(order by id) id, -- id is a line id
line -- line of text.
from
source -- line source
),
result_lines as
(
select
id,
cast(line as nvarchar(max)) line
from
lines
where
id = 1
union all
select
l.id,
cast(r.line + N', ' + l.line as nvarchar(max))
from
lines l
inner join
result_lines r
on
l.id = r.id + 1
)
select top 1
line
from
result_lines
order by
id desc
In Oracle, it is wm_concat. I believe this function is available in the 10g release and higher.
For Oracle DBs, see this question: How can multiple rows be concatenated into one in Oracle without creating a stored procedure?
The best answer appears to be by #Emmanuel, using the built-in LISTAGG() function, available in Oracle 11g Release 2 and later.
SELECT question_id,
LISTAGG(element_id, ',') WITHIN GROUP (ORDER BY element_id)
FROM YOUR_TABLE;
GROUP BY question_id
as #user762952 pointed out, and according to Oracle's documentation http://www.oracle-base.com/articles/misc/string-aggregation-techniques.php, the WM_CONCAT() function is also an option. It seems stable, but Oracle explicitly recommends against using it for any application SQL, so use at your own risk.
Other than that, you will have to write your own function; the Oracle document above has a guide on how to do that.

Truncate specific number of characters in SQL cells

I have Description column in a table
Description
---------------
AA Check
B1 Check
RD/AA Check
WD_FA Examine
FF Examine
AA Pass
B2 Check
Examine
The desired output
Description
---------------
Check
Examine
Pass
Basically a case statement where it truncates from the left side of the cell. NOTE that the list goes on with different names and conditions, and some cells do not need modification (like the last row), so something similar to a case statement but not returning NULL if I did not specify the condition
Thank you
This removes the data up to the 1st space
case when x like '% %'
then substring(x, charindex(' ', x) +1, 8000)
else x
end
'AA Examine Device' -> 'Examine Device'
And this extracts the last word:
case when x like '% %'
then right(x, charindex(' ', reverse(x)) -1)
else x
end
'AA Examine Device' -> 'Device'
Following the white space that you have, and using DISTINCT, this gets the last word following a white space or returns the word if it's the only word.
declare #Description table (Description varchar(64))
insert into #Description
values
('AA Check'),
('B1 Check'),
('RD/AA Check'),
('WD_FA Examine'),
('FF Examine'),
('AA Pass'),
('B2 Check'),
('Examine')
select distinct
right([Description],len([Description]) - charindex(' ',[Description]))
from #Description
You can do it like below :
select distinct substring(Description, charindex(' ', Description) + 1, len(Description))
The question is too broad, but you can use something like that
DECLARE #Product TABLE (
[Description] nvarchar(255),
Condition int
);
INSERT #Product ([Description], Condition)
select 'AA Check', 1 union all
select 'B1 Check', 2 union all
select 'RD/AA Check', 3 union all
select 'WD_FA Examine', 3 union all
select 'FF Examine', 4 union all
select 'AA Pass', 1 union all
select 'B2 Check', 5 union all
select 'Examine', 6
select
[Description],
case
when Condition = 1 then replace([Description],'AA ','')
when Condition = 2 then replace([Description],'B1 ','')
when Condition = 3 then ltrim(rtrim(right([Description],len([Description]) - 5)))
when Condition = 5 then replace([Description],'B2 ','')
when Condition = 6 then [Description]
else
''
end as 'NewDescription'
from #Product
I hope it helps 🙂
Perhaps something like this:
;WITH CTE (Column1) AS (
SELECT * FROM (
VALUES
(' AA Check '), (' B1 Check'), (' RD/AA Check '), (' WD_FA Examine '), (' FF Examine '),
(' AA Pass '), (' B2 Check '), (' Examine ')
) AS A (Column1)
)
SELECT
CASE WHEN CHARINDEX('Check', Column1) > 0 THEN SUBSTRING(Column1, CHARINDEX('Check', Column1), 5)
WHEN CHARINDEX('Examine', Column1) > 0 THEN SUBSTRING(Column1, CHARINDEX('Examine', Column1), 7)
WHEN CHARINDEX('Pass', Column1) > 0 THEN SUBSTRING(Column1, CHARINDEX('Pass', Column1), 4)
END AS Results
FROM CTE
It would be painful if you have a lot of variables though.
CREATE TABLE [A] ( [Description] VARCHAR(100));
INSERT [A]
VALUES
---------------
( 'AA Check' )
,( 'B1 Check' )
,( 'RD/AA Check' )
,( 'WD_FA Examine' )
,( 'FF Examine' )
,( 'AA Pass' )
,( 'B2 Check' )
,( 'Examine' )
,( 'Examine Device' );
SELECT DISTINCT
CASE
WHEN [Description] COLLATE Latin1_General_BIN LIKE '[A-Z][a-z]%' THEN
[Description]
WHEN [Description] COLLATE Latin1_General_BIN LIKE '[A-Z][A-Z0-9]%' THEN
SUBSTRING([Description], CHARINDEX(' ', [Description]) + 1, 8000)
END AS [Description]
FROM
[A];
DROP TABLE [A];

How to concatenate columns without null value in sql

This question is asked many time here but i am not getting the proper output as i am expecting:
I have a table in which i need to concatenate columns but i don't want NULL value in it. I want it in sql server 2008 concate function does not work in 2008.
Example:
OrderTable
Customer_Number order1 order2 order3 order4
1 NULL X Y NULL
2 NULL A B NULL
3 V NULL H NULL
Now want i want is the data in concatenated manner for order only like this:
Customer_Number Order
1 X,Y
2 A,B
3 V,H
This is the code i used
Select Customer_number, ISNULL(NULLIF(order1,' ')+',','')+
ISNULL(NULLIF(order2,' ')+',','')+
ISNULL(NULLIF(order3,' ')+',','')+
ISNULL(NULLIF(order4,' ')+',','')
as Order from Ordertable
I got the below output
Customer_Number Order
1 NULL,X,Y,NULL
2 NULL,A,B,NULL
3 V,NULL,H,NULL
I already try Coalesce, Stuff, ISNULL, NULLIF but all have same result
Thanks in advance !!!
Another variation, for fun and profit, demonstrating the FOR XML trick to concatenate values pre-SQL Server 2012.
SELECT Customer_Number, STUFF(
(SELECT ',' + order1, ',' + order2, ',' + order3, ',' + order4 FOR XML PATH('')),
1, 1, ''
)
This is slight overkill for a constant number of columns (and not particularly efficient), but an easy to remember pattern for concatenation. Also, it shows off STUFF, a function any SQL developer should learn to love.
Example
Declare #YourTable Table ([Customer_Number] varchar(50),[order1] varchar(50),[order2] varchar(50),[order3] varchar(50),[order4] varchar(50))
Insert Into #YourTable Values
(1,NULL,'X','Y',NULL)
,(2,NULL,'A','B',NULL)
,(3,'V',NULL,'H',NULL)
Select Customer_Number
,[Order] = IsNull(stuff(
IsNull(','+order1,'')
+IsNull(','+order2,'')
+IsNull(','+order3,'')
+IsNull(','+order4,'')
,1,1,''),'')
From #YourTable
Returns
Customer_Number Order
1 X,Y
2 A,B
3 V,H
EDIT - IF the "NULL" are strings and NOT NULL Values
Declare #YourTable Table ([Customer_Number] varchar(50),[order1] varchar(50),[order2] varchar(50),[order3] varchar(50),[order4] varchar(50))
Insert Into #YourTable Values
(1,'NULL','X','Y','NULL')
,(2,'NULL','A','B','NULL')
,(3,'V','NULL','H','NULL')
Select Customer_Number
,[Order] = IsNull(stuff(
IsNull(','+nullif(order1,'NULL'),'')
+IsNull(','+nullif(order2,'NULL'),'')
+IsNull(','+nullif(order3,'NULL'),'')
+IsNull(','+nullif(order4,'NULL'),'')
,1,1,''),'')
From #YourTable
This is a bit unpleasant, it needs to be in a subquery to remove the trailing comma efficiently (though you could use a CTE):
SELECT
Customer_number,
SUBSTRING([Order], 0, LEN([Order])) AS [Order]
FROM(
SELECT
Customer_number,
COALESCE(order1+',', '') +
COALESCE(order2+',', '') +
COALESCE(order3+',', '') +
COALESCE(order4+',', '') AS [Order]
FROM
OrderTable) AS SubQuery
You were on the right track with the Coalesce, or IsNull. Instead of trying to track the length and use substring, left or stuff, I just used a replace to remove the trailing comma that would show up from the coalesce.
Select Customer_number,
Replace(
Coalesce(Order1 + ',','')+
Coalesce(Order2 + ',','')+
Coalesce(Order3 + ',','')+
Coalesce(Order4 + ',','')
+',',',,','') --Hack to remove the Last Comma
as [Order] from Ordertable

Concatenate first name, last name and middle name with comma

I want to concatenate 3 columns in SQL server as below:
MAX(LTRIM(RTRIM((ISNULL(LastName,'') +
', ' +
ISNULL(FirstName,'') +
', ' +
ISNULL(MiddleName,''))))) AS FullName
I have used value of this column in SELECT clause as:
MAX(FullName) AS FullName,
I would like to handle NULL values, in case all 3 last name, middle name and first name are BLANK or NULL. The query used above will show " , , " in case all 3 columns are NULL or BLANK. But I want to show "N/A" in such case.
Thanks in advance.
You could use a CASE expression:
SELECT MAX(CASE WHEN ISNULL(FirstName, '') = '' AND
ISNULL(MiddleName, '') = '' AND
ISNULL(LastName, '') = ''
THEN 'N/A'
ELSE LTRIM(RTRIM((ISNULL(LastName,'') + ', ' +
ISNULL(FirstName,'') + ', ' +
ISNULL(MiddleName,''))))
END) AS FullName
FROM yourTable
...
Check with COALESCE and then CASE Statement:
Declare #FirstName VARCHAR(50)='',#MiddleName VARCHAR(50),#LastName VARCHAR(50)
SELECT
CASE WHEN ISNULL(COALESCE(#FirstName,#MiddleName,#LastName),'')<>''
THEN ISNULL(#FirstName,'')+',' +ISNULL(#MiddleName,'')+','+ISNULL(#LastName,'')
ELSE 'N/A' END AS FullName
Use Concat like below this will do implicit conversion. So no need to use ISNULL.
select isnull(MAX(LTRIM(RTRIM((concat(LastName,
', ' ,
FirstName,
', ' ,
MiddleName,''))))) ,'n/a')AS FullName from table
The below method may seem quite complicated, but it does make adding or removing columns much simpler, and for all its perceived complexity it isn't actually doing that much under the hood, so doesn't add much overhead.
The first step is to unpivot each of your columns to rows with a common column name, so you would turn
FirstName MiddleName LastName
------------------------------------
A NULL C
Into
Name
------
A
NULL
C
Using CROSS APPLY along with the table value constructor VALUES
SELECT x.Name
FROM (VALUES ('A', NULL,'C')) AS t (FirstName, MiddleName, LastName)
CROSS APPLY (VALUES (1, t.FirstName), (2, t.MiddleName), (3, t.LastName)) x (SortOrder, Name)
ORDER BY x.SortOrder
Then you can remove NULLs and blanks with WHERE ISNULL(Name, '') <> '', then you only have valid data to concatenate together which you can do using SQL Server's XML Extensions. So you end up with a full query like:
WITH TestData AS
( SELECT *
FROM (VALUES ('A'), (NULL)) AS f (FirstName)
CROSS JOIN (VALUES ('B'), (NULL)) AS m (MiddleName)
CROSS JOIN (VALUES ('C'), (NULL)) AS l (LastName)
)
SELECT t.*,
NamesConcat = ISNULL(STUFF(NamesConcat.value('.', 'NVARCHAR(MAX)'), 1, 2, ''), 'N/A')
FROM TestData AS t
CROSS APPLY
( SELECT ', ' + x.Name
FROM (VALUES
(1, t.FirstName),
(2, t.MiddleName),
(3, t.LastName)
) x (SortOrder, Name)
WHERE ISNULL(x.Name, '') <> '' -- NOT BLANK OR NULL
ORDER BY x.SortOrder
FOR XML PATH(''), TYPE
) x (NamesConcat);
Result
FirstName MiddleName LastName NamesConcat
-------------------------------------------------
A B C A, B, C
A NULL C A, C
A B NULL A, B
A NULL NULL A
NULL B C B, C
NULL NULL C C
NULL B NULL B
NULL NULL NULL N/A
select Isnull(FirstName,' ') +' '+ ','+ Isnull(MiddleName,' ')+' '+ ' ,'+ Isnull(Lastname,' ') as Name from personaldata
select FirstName +' '+','+ MiddleName +' '+',' + Lastname as Name from personaldata
Note: The Second query will work fine if all value present and if anyone is null then it will return null for Name, to avoid such kind of concern please use the first query.

sql query complex

I have table where in a table called test which have 4 fields.one field named as listing, I have 1,2,3,4,5,6 multiple values separated by comma, I need to check whether in that table and in that particular field an id say 4 is there or not.. by a sql query.
You database design is wrong, that's why you have problems querying the data. You should have the values in a separate table, so that teach value is in it's own field. Then it would be easy to find the records:
select t.testId
from test t
inner join listing l on l.testId = t.testId
where l.id = 4
Now you have to use some ugly string comparison to find the records:
select testId
from test
where ','+listing+',' like '%,4,%'
You can try
SELECT *
FROM YourTable
WHERE REPLACE(Col, ' ', '') LIKE '4,%' --Starts with
OR REPLACE(Col, ' ', '') LIKE '%,4' --Ends with
OR REPLACE(Col, ' ', '') LIKE '%,4,%' --Contains
OR REPLACE(Col, ' ', '') = '4' --Equals
Just as a matter of interest, have a look at this
DECLARE #delimiter NVARCHAR(5),
#Val INT
SELECT #Val = 40
SELECT #delimiter = ','
DECLARE #YourTable TABLE(
ID INT,
Vals VARCHAR(50)
)
INSERT INTO #YourTable (ID,Vals) SELECT 1, '1,2,3,4,5,6,7,8'
DECLARE #TempTable TABLE(
ID INT,
Vals XML
)
INSERT INTO #TempTable
SELECT ID,
CAST('<d>' + REPLACE(Vals, #delimiter, '</d><d>') + '</d>' AS XML)
FROM #YourTable
SELECT *
FROM #TempTable tt
WHERE EXISTS(
SELECT T.split.value('.', 'nvarchar(max)') AS data
FROM tt.Vals.nodes('/d') T(split)
WHERE T.split.value('.', 'nvarchar(max)') = #Val
)
The common approach is to parse the list into a table variable or table-valued function, then either join against the table, or use an EXISTS sub-query.
There are lots of examples on how to do this:
http://www.bing.com/search?setmkt=en-US&q=SQL+parse+list+into+table
You could use an instring function in the where clause and in the select clause:
Oracle:
select substr(column, instr(column, '1', 1), 1)
where instr(column, '1', 1) > 0
works if you want a single value. Alternatively you can use a combination of case or decode statements to create a single column for each possible value:
select
decode(instr(column, '1', 1), 0, substr(column, instr(column, '1', 1), 1), null) c1,
decode(instr(column, '2', 1), 0, substr(column, instr(column, '2', 1), 1), null) c2,
decode(instr(column, '3', 1), 0, substr(column, instr(column, '3', 1), 1), null) c3
The beauty of this approach for such a poorly normalised set of data is you can save this as a view and then run SQL on that, so if you save the above you could use:
select c1, c2 from view where c1 is not null or c2 is not null
NB. In other dbms you might have to use different syntax, possibly the case rather decode statement
If you need to find 4 and only 4 (ie not 14 or 24 or 40 etc) you should use
SELECT * FROM foo WHERE col LIKE '%, 4,%'
or
SELECT * FROM foo WHERE col LIKE '%,4,%'
if there are no spaces between the commas and numbers
How about this?
Select * From Foo Where Col like '%4%'