Reputation: 29
We have a job table that includes ID and title, something like this:
JobID | JobTitle
1 president
2 vice-president
3 director
The user table includes a jobID that is supposed to map to the job table, but whoever coded it made it a multi-select field in the UI and appended values with pipes between. So a user could be a president AND a vice-president AND a director. Here is an example of the user table
UserName | JobID
Suzy 1|2|3
Bob 3
Jane 2|1
I'm trying to run a report of all staff and their titles, but am stumped at how to iterate through the multi-value jobIDs and display the jobTitle.
The query I'm currently using is something like:
select user.username, job.JobTitle
from user
inner join job on user.JobID = job.JobID
This is all on SQL Server 2012
Any suggestions? I'd settle for displaying additional titles on subsequent rows, or in subsequent columns, whatever is easier.
Upvotes: 0
Views: 1570
Reputation: 367
In order to return a comma separated list of Job titles for each user you can adapt Julien's code, as below:
SELECT
U2.UserName,
SUBSTRING(
REPLACE(
REPLACE(
(
SELECT J.JobTitle
FROM [job] J
INNER JOIN [user] U
ON ('|'+U.JobID+'|' LIKE('%|'+CAST(J.JobID as varchar(20))+'|%'))
WHERE U.UserName = U2.UserName
FOR XML PATH('')
), '</JobTitle>', ''
), '<JobTitle>', ', '
),2,1000
) [JobTitles]
FROM [user] U2
Upvotes: 0
Reputation: 825
The best answer is obviously to simply split the column and create a separate table with 1 row per child (job) to have atomic data. But in the meantime, you can do something like :
SELECT [user].username, [job].JobTitle
FROM [job]
INNER JOIN [user]
ON ('|'+[user].JobID+'|' LIKE('%|'+CAST([job].JobID as varchar(20))+'|%'))
Oh, and before you shout after someone for inserting non-atomic data in a table, start by shunning him for using reserved keywords as table names. Never ever ever ever name your table user
.
Btw, this syntax can be used to do something like :
CREATE TABLE users AS
SELECT user.username, job.jobId
FROM [job]
INNER JOIN [user]
ON ('|'+[user].JobID+'|' LIKE('%|'+CAST([job].JobID as varchar(20))+'|%'))
which will net you a sanitized atomic table.
Upvotes: 0
Reputation: 2312
Here is a function you can use to split a delimited field and return the results to a table (which you can then use in subsequent operations):
CREATE FUNCTION dbo.ufnGENSplitDelimField (
@InputString nvarchar(max),
@Delimiter nvarchar(10)
)
RETURNS @Results TABLE (
Item nvarchar(50)
)
AS
BEGIN
-- default delimiter to comma if blank
IF ISNULL(@Delimiter,'') = ''
BEGIN
SET @Delimiter = ','
END
DECLARE @Item nvarchar(50);
DECLARE @ItemList nvarchar(max);
DECLARE @DelimIndex int;
SET @ItemList = @InputString
SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0)
/*
Loop through all items, removing the first at each iteration. For example, if we start with the string 'bob,mary,joe':
1. set @Item = 'bob'
2. insert 'bob' into result table
3. set @ItemList = 'mary,joe'
4. set @DelimIndex = 4
loop
*/
WHILE (@DelimIndex != 0)
BEGIN
SET @Item = SUBSTRING(@ItemList, 0, @DelimIndex)
INSERT INTO @Results VALUES (@Item)
-- Set @ItemList = @ItemList minus one less item
SET @ItemList = SUBSTRING(@ItemList, @DelimIndex+1, LEN(@ItemList)-@DelimIndex)
SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0)
END -- End WHILE
IF @Item IS NOT NULL -- At least one delimiter was encountered in @InputString, and we need to insert the final item in the list
BEGIN
SET @Item = @ItemList
INSERT INTO @Results VALUES (@Item)
END
ELSE -- No delimiters were encountered in @InputString, so just return @InputString
BEGIN
INSERT INTO @Results VALUES (@InputString)
END
RETURN;
END;
Upvotes: 1