I have data in a database that I cannot change which is stored like this:
DocId | Staff/Relationship |
---|---|
127866 | 1395/3003,1399/1388 |
where:
1395/3003 = Cat Stevens/Therapist
and
1399/1388 = Dog Stevens/Program Staff
I only want a concatenated row of the staff names separated by commas like this:
DocId | Staff |
---|---|
127866 | Cat Stevens, Dog Stevens |
I need to split the ids from the '/' and then join them to a StaffContacts table to get the name. After that, I need to join them again into one row. I am using SQL Server version 13 so cannot use String_Split. I have this so far:
WITH CTE
AS
(SELECT
DocId
,Split.a.value('. ', 'VARCHAR(100)') 'Staff'
/* Separate into separate columns */
FROM
(SELECT DocId, CAST ('<M>' + REPLACE(Staff, ',', '</M><M>') + '</M>' AS XML) AS Data
FROM CustomerDocument cd
) AS B
CROSS APPLY Data.nodes ('/M') AS Split(a) /* Unpivot into rows so can join to tables below */
)
/* Get Staff */
(SELECT DISTINCT stafftab.DocId
,[Staff] = STUFF(/* This is added to delete the leading ', ' */
(SELECT DISTINCT ', ' + ([FirstName]+ ' ' + [LastName])
FROM cte
JOIN Documents d on cte.DocId = d.DocId
JOIN StaffContacts sc ON d.ClientId = sc.ClientId
WHERE sc.Relationship in (
SELECT CodeId
FROM Codes co
Where categorycode = 'Staff')
AND cte.DocId = stafftab.DocId
FOR XML PATH ('')) /* Concatenate multiple rows of data into comma separated values back into one column */
, 1, 2, '') /* At 1st character, delete 2 which deletes the leading ', ' */
FROM cte stafftab
)
However, I am getting this result:
DocId | Staff |
---|---|
127866 | NULL |
I tried to include this code in the above since it separates the name from the '/' but I haven't been able to get it to separate successfully to join to the StaffContacts table and then be concatenated again.
SELECT DocId,
LEFT(Staff, charindex('/', Staff)-1)
FROM CustomerDocument cd
DocId | Staff |
---|---|
127866 | 1395 |
Is there another way to do this in the version of SSMS that I have? Thank you.
This should work on SQL Server 2016 with compatibility level 130. If your compatibility level is lower, you will need to use some user-defined way to split your strings, I suggest Jeff Moden's CSV Splitter
First, create and populate sample tables (Please save us this step in your future questions):
CREATE TABLE CustomerDocument (
DocId INT PRIMARY KEY,
StaffRelationship NVARCHAR(MAX)
);
INSERT INTO CustomerDocument (DocId, StaffRelationship)
VALUES (127866, '1395/3003,1399/1388');
CREATE TABLE Staff
(
id int,
name nvarchar(100)
);
INSERT INTO Staff (id, name) VALUES (1395, 'Cat Stevens'), (1399, 'Dog Stevens');
Then, use a CTE combined with STRING_SPLIT
and a couple of CROSS APPLIES
to get your data in a normalized way, and then use a combination of STUFF
and FOR XML PATH
to aggregate the strings back together. (unfortunately, STRING_AGG
is only available from 2017 version)
WITH CTE AS (
SELECT docid, staffId
FROM CustomerDocument
CROSS APPLY (SELECT * FROM STRING_SPLIT(StaffRelationship, ',', 1)) AS pairs
CROSS APPLY (SELECT value as staffId FROM STRING_SPLIT(pairs.value, '/', 1) WHERE ordinal = 1) AS singles
)
SELECT DISTINCT docid, STUFF
(
(
SELECT ', ' + [name]
FROM CTE
INNER JOIN Staff
ON staffId = id
FOR XML PATH('')
)
, 1, 1, '') AS Staff
FROM CTE
Results:
docid | staff |
---|---|
127866 | Cat Stevens, Dog Stevens |