I have a set of data that looks like this:
Before
FirstName LastName Field1 Field2 Field3 ... Field27
--------- -------- ------ ------ ------ -------
Mark Smith A B C D
John Baptist X T Y G
Tom Dumm R B B U
However, I'd like the data to look like this:
After
FirstName LastName Field Value
--------- -------- ----- -----
Mark Smith 1 A
Mark Smith 2 B
Mark Smith 3 C
Mark Smith 4 D
John Baptist 1 X
John Baptist 2 T
John Baptist 3 Y
John Baptist 4 G
Tom Dumm 1 R
Tom Dumm 2 B
Tom Dumm 3 B
Tom Dumm 4 U
I have looked at the PIVOT function. It may work. I am not too sure. I couldn't make sense of how to use it. But, I am not sure that the pivot could place a '4' in the 'Field' column. From my understanding, the PIVOT function would simply transpose the values of Field1...Field27 into the 'Value' column.
I have also considered iterating over the table with a Cursor and then looping over the field columns, and then INSERTing into another table the 'Field's and 'Value's. However, I know this will impact performance since it's a serial-based operation.
Any help would be greatly appreciated! As you can tell, I'm quite new to T-SQL (or SQL in general) and SQL Server.
You can perform with an UNPIVOT. There are two ways to do this:
1) In a Static Unpivot you would hard-code your Field columns in your query.
select firstname
, lastname
, replace(field, 'field', '') as field
, value
from test
unpivot
(
value
for field in (field1, field2, field3, field27)
) u
See a SQL Fiddle for a working demo.
2) Or you could use a Dynamic Unpivot which will get the list of items to PIVOT when you run the SQL. The Dynamic is great if you have a large amount of fields that you will be unpivoting.
create table mytest
(
firstname varchar(5),
lastname varchar(10),
field1 varchar(1),
field2 varchar(1),
field3 varchar(1),
field27 varchar(1)
)
insert into mytest values('Mark', 'Smith', 'A', 'B', 'C', 'D')
insert into mytest values('John', 'Baptist', 'X', 'T', 'Y', 'G')
insert into mytest values('Tom', 'Dumm', 'R', 'B', 'B', 'U')
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
select #cols = stuff((select ','+quotename(C.name)
from sys.columns as C
where C.object_id = object_id('mytest') and
C.name like 'Field%'
for xml path('')), 1, 1, '')
set #query = 'SELECT firstname, lastname, replace(field, ''field'', '''') as field, value
from mytest
unpivot
(
value
for field in (' + #cols + ')
) p '
execute(#query)
drop table mytest
Both will produce the same results.
If you want to do it query than quick and dirty way will be to create Union
Select FirstName,LastName,1,Field1
from table
UNION ALL
Select FirstName,LastName,2,Field2
from table
.
.
And similar for all field cols
Rather than using pivot, use unpivot like this:
select firstname, lastname, substring(field,6,2) as field, value
from <yourtablename>
unpivot(value for field in (field1,field2,field3,field4,field5,field6,field7,field8,field9,field10,field11,field12,field13,field14,field15,field16,field17,field18,field19,field20,field21,field22,field23,field24,field25,field26,field27,field1,field2,field3,field4,field5,field6,field7,field8,field9,field10,field11,field12,field13,field14,field15,field16,field17,field18,field19,field20,field21,field22,field23,field24,field25,field26,field27)) as unpvt;
Related
I have a large data set that looks like the below:
NAME Value
Dan 1
Dan 92
Dan A4
Steve 1
Steve B10
John 4
I'm trying to convert it into a table like:
Name Value1 Value2 Value3
Dan 1 92 B10
Steve 1 B10 Null
John 4 Null Null
So there is an unknown amount of rows and I'd like to create a new column for every value when it exists. Anyone have an idea of how to do this in SQL?
The example you provided would work perfectly using PIVOT, but you need to supply a category to the values to do the pivot.
e.g.
NAME Category Value
Dan Value1 1
Dan Value2 92
Dan Value3 A4
Steve Value1 1
Steve Value3 B10
John Value1 4
Then your results would be like this
Name Value1 Value2 Value3
Dan 1 92 A4
Steve 1 NULL B10
John 4 NULL NULL
Here's Microsoft's documentation:
https://learn.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot?view=sql-server-ver15
To do this dynamically, please read this post. It handles the same situation well.
SQL Server dynamic PIVOT query?
To pivot data you need something to pivot it by.
In this case it can be generated using ROW_NUMBER.
For example:
--
-- sample data
--
create table yourlargetable (
id int identity(1,1) primary key,
name nvarchar(30),
value nvarchar(30)
);
insert into yourlargetable (name, value) values
('jane', 'val1'), ('jane', 'val2'), ('jane', 'val3'),
('john', 'val4'), ('john', 'val5');
--
-- declare a few variables
--
declare #DynSql nvarchar(max);
declare #Cols nvarchar(max);
declare #ColTotal int;
--
-- how many columns are needed
--
select top 1 #ColTotal = count(*)
from yourlargetable
group by name
order by count(*) desc;
--
-- generate a string with column names
--
with RCTE_NUMS as
(
select 1 as n
union all
select n+1
from RCTE_NUMS
where n < #ColTotal
)
select #Cols = concat(#Cols+', ', quotename(concat('Value', n)))
from RCTE_NUMS
order by n;
--
-- create the dynamic sql string
--
set #DynSql = 'select *'+ char(10) +
'from ('+
'select name, value '+ char(10) +
', concat(''Value'', row_number() over (partition by name order by value)) col '+ char(10) +
'from yourlargetable) s'+ char(10) +
'pivot (max(value) '+ char(10) +
'for col in ('+ #Cols +')) p'+ char(10) +
'order by name';
-- select #DynSql;
--
-- run the dynamic sql
--
exec sp_executesql #DynSql;
Returns:
name Value1 Value2 Value3
jane val1 val2 val3
john val4 val5 NULL
I have multiple rows for members and want to merge them based on the values of two columns by giving priority to the value 'Yes'.
Name | Status1 | Status2
Jon | Yes | No
Jon | No | Yes
I want the query to return
Name | Status1 | Status2
Jon | Yes | Yes
So, if the column has Yes even once, it has to assign Yes for the person and No otherwise.
Below is for BigQuery Standard SQL
#standardSQL
SELECT Name, MAX(Status1) AS Status1, MAX(Status2) AS Status2
FROM `project.dataset.table`
GROUP BY Name
You can test, play with it using sample data
#standardSQL
WITH `project.dataset.table` AS (
SELECT 'Jon' Name, 'Yes' Status1, 'No' Status2 UNION ALL
SELECT 'Jon', 'No', 'Yes'
)
SELECT Name, MAX(Status1) AS Status1, MAX(Status2) AS Status2
FROM `project.dataset.table`
GROUP BY Name
with result
Row Name Status1 Status2
1 Jon Yes Yes
In addition to Mikhail's answer, I am adding another solution with MsSQL. Syntax may be different but the logic would be similar:
create table test
(id int , name1 varchar(10), name2 varchar(10))
insert into test values (1,'yes','no')
insert into test values (2,'no','no')
insert into test values (3,'yes','yes')
declare #searchKey varchar(10) = 'yes'
declare #cols varchar(255) = (SELECT STUFF((
SELECT ', ' + c.name
FROM sys.columns c
JOIN sys.types AS t ON c.user_type_id=t.user_type_id
WHERE t.name != 'int' AND t.name != 'bit' AND t.name !='date' AND t.name !='datetime'
AND object_id =(SELECT object_id FROM sys.tables WHERE name='test')
FOR XML PATH('')),1,2,''))
declare #sql nvarchar(max) = 'SELECT * from test where '''+#searchKey+''' in ('+#cols+')'
exec sp_executesql #sql
Edit: Please note that this solution checks all the columns of a table if a specific value is included by any column. Assume the OP needs to check 100 columns, until status100, then I believe a dynamic solution like that would be more handy.
I'm utilizing a table (called REFERENCE_TABLE) and referencing values stored in a column (STATEMENT) in order to populate the CATEGORY column in a report. There are a variety of different columns in the reporting data (FIELD1, ZONENAME, DISTRICT) and any of these can be used to determine the CATEGORY. Also, schools can be in more than one DISTRICT.
How can I work with this table and basically pull in CATEGORY (like I would using a LEFT JOIN)? The actual table utilized is much larger and is constantly being updated, so pulling the data out manually and including it in the WHERE statement would not be ideal.
You need to use Dynamic SQL for this. The idea of dynamic SQL is you write your query to a string, and then call sp_executesql with your string variable as a parameter, and it runs the query.
Assuming your FIELD1, ZONENAME, and DISTRICT fields are all in the same table (called DATA_TABLE, for instance), you can get what you need with a query like this:
DECLARE #sql NVARCHAR(MAX)
SELECT #sql = STUFF((
SELECT 'UNION SELECT d.*, ''' + r.CATEGORY + ''' AS CATEGORY, '''
+ REPLACE(r.STATEMENTFIELD, '''', '''''') + ''' AS MATCHING_CRITERIA '
+ 'FROM DATA_TABLE d WHERE ' + r.STATEMENTFIELD
FROM REFERENCE_TABLE r
FOR XML PATH('')
), 1, 6, '')
EXEC sp_executesql #sql
The SQL statement basically executes "SELECT * FROM DATA_TABLE WHERE " + each criterion from the REFERENCE_TABLE, and unions all the results together. The STUFF and FOR XML PATH clauses just turn the individual "SELECT *" queries into one big one by sticking a "UNION" between each query.
This gives you an output like this:
FIELD1 ZONENAME DISTRICT CATEGORY MATCHING_CRITERIA
0001 A NY SCHOOL1 FIELD1 IN ('0001','0002','0003')
0001 A NY SCHOOL1 ZONENAME IN ('A')
0001 A NY SCHOOL4 DISTRICT IN ('NY')
0002 A IL SCHOOL1 FIELD1 IN ('0001','0002','0003')
0002 A IL SCHOOL1 ZONENAME IN ('A')
0003 B NY SCHOOL1 FIELD1 IN ('0001','0002','0003')
0003 B NY SCHOOL3 ZONENAME IN ('B')
0003 B NY SCHOOL4 DISTRICT IN ('NY')
etc.
(Here are my sample data DDL & DML statements for testing. When I ask SQL questions, I like to include these. It makes it easier for responders to get started.)
CREATE TABLE DATA_TABLE (FIELD1 VARCHAR(4), ZONENAME CHAR(1), DISTRICT CHAR(2))
INSERT INTO DATA_TABLE VALUES ('0001', 'A', 'NY')
INSERT INTO DATA_TABLE VALUES ('0002', 'A', 'IL')
INSERT INTO DATA_TABLE VALUES ('0003', 'B', 'NY')
INSERT INTO DATA_TABLE VALUES ('0004', 'B', 'IL')
INSERT INTO DATA_TABLE VALUES ('0005', 'C', 'NY')
INSERT INTO DATA_TABLE VALUES ('0006', 'C', 'IL')
INSERT INTO DATA_TABLE VALUES ('0007', 'D', 'NY')
INSERT INTO DATA_TABLE VALUES ('0008', 'D', 'IL')
INSERT INTO DATA_TABLE VALUES ('0009', 'E', 'NY')
CREATE TABLE REFERENCE_TABLE (STATEMENTFIELD NVARCHAR(100), CATEGORY NVARCHAR(10))
INSERT INTO REFERENCE_TABLE VALUES
('FIELD1 IN (''0001'',''0002'',''0003'')', 'SCHOOL1'),
('ZONENAME IN (''A'')', 'SCHOOL1'),
('FIELD1 IN (''0004'',''0005'',''0006'')', 'SCHOOL2'),
('ZONENAME IN (''B'')', 'SCHOOL3'),
('ZONENAME IN (''C'')', 'SCHOOL4'),
('FIELD1 IN (''0007'',''0008'',''0009'')', 'SCHOOL4'),
('DISTRICT IN (''NY'')', 'SCHOOL4')
This gives you a DATA_TABLE like this, which has rows that match multiple criteria in some cases:
FIELD1 ZONENAME DISTRICT
0001 A NY
0002 A IL
0003 B NY
0004 B IL
0005 C NY
0006 C IL
0007 D NY
0008 D IL
0009 E NY
The first row matches mutiple criteria. Should that show a single row or a row for each match? Distinct rows? Not sure. I went with the output with the most information in it. You could, for instance, remove the MATCHING_CRITERIA column and select distinct rows.
Or, the following solution, for example, returns distinct records from DATA_TABLE with comma-delimited matching schools in a column:
DECLARE #sql NVARCHAR(MAX)
SELECT #sql = 'WITH cte AS (' + STUFF((
SELECT 'UNION SELECT d.*, ''' + r.CATEGORY + ''' AS CATEGORY '
+ 'FROM DATA_TABLE d WHERE ' + r.STATEMENTFIELD
FROM REFERENCE_TABLE r
FOR XML PATH('')
), 1, 6, '') + ')
SELECT *,
STUFF((SELECT '','' + CATEGORY FROM cte c
WHERE FIELD1 = d.FIELD1 AND ZONENAME = d.ZONENAME AND DISTRICT = d.DISTRICT
FOR XML PATH('''')), 1, 1, '''') AS COMMA_DELIMITED_CATEGORIES
FROM DATA_TABLE d'
EXEC sp_executesql #sql
Returns:
FIELD1 ZONENAME DISTRICT COMMA_DELIMITED_CATEGORIES
0001 A NY SCHOOL1,SCHOOL4
0002 A IL SCHOOL1
0003 B NY SCHOOL1,SCHOOL3,SCHOOL4
0004 B IL SCHOOL2,SCHOOL3
0005 C NY SCHOOL2,SCHOOL4
0006 C IL SCHOOL2,SCHOOL4
0007 D NY SCHOOL4
0008 D IL SCHOOL4
0009 E NY SCHOOL4
If I'm not mistaken, there is a rule table and a data table
Here is the SQL script that creates those tables and populates with sample data
create table REFERENCE_TABLE (
STATEMENTFIELD nvarchar(1000),
category varchar(20)
)
insert into REFERENCE_TABLE values ('Field1 in (''0001'',''0002'',''0003'')','Shool1'),('ZoneName in (''A'')','Shool1')
insert into REFERENCE_TABLE values ('Field1 in (''0004'',''0005'',''0006'')','Shool2'),('ZoneName in (''B'')','Shool3')
insert into REFERENCE_TABLE values ('ZoneName in (''C'')','Shool4')
create table REFERENCE_DATA(
Field1 varchar(10),
ZoneName varchar(10),
District varchar(10),
Category varchar(20),
)
insert into REFERENCE_DATA (Field1) select '0002'
insert into REFERENCE_DATA (Field1) select '0001'
insert into REFERENCE_DATA (District) select 'NY'
insert into REFERENCE_DATA (Field1) select '0004'
insert into REFERENCE_DATA (Field1) select '0003'
insert into REFERENCE_DATA (ZoneName) select 'A'
Now I build a dynamic SQL update statement as follows
declare #sql nvarchar(max) = '
update REFERENCE_DATA
set category =
case
'
select #sql = #sql + 'when ' + statementfield + ' then ''' + category + '''
'
from REFERENCE_TABLE
set #sql = #sql + '
end'
print #sql
The print command shows the SQL statement that will update all data according to the defined rules in one statement
If it is OK for you, you can execute it by using sp_executesql procedure.
Replace PRINT command line with following
execute sp_executesql #sql
Given this data:
Name Property Value
---------- ---------- ----------
Bob Hair Red
Bob Eyes Blue
Fred Hair Brown
Fred Height Tall
what SQL would be required to produce these results?
Property Bob Fred
---------- ---------- ----------
Hair Red Brown
Eyes Blue
Height Tall
I'm using SQL Server 2008, but a generic solution would be nice.
You did not specify what RDBMS you are using but this is a pivot. You can use an aggregate function and a CASE expression in all databases:
select property,
max(case when name='Bob' then value else '' end) Bob,
max(case when name='Fred' then value else '' end) Fred
from yourtable
group by property
See SQL Fiddle with Demo
If you are using a database that has a PIVOT function (SQL Server 2005+/Oracle 11g+), then your code will be similar to this:
select *
from
(
select property, name, value
from yourtable
) src
pivot
(
max(value)
for name in (Bob, Fred)
) piv
See SQL Fiddle with Demo
The above queries work great, if you know the name values ahead of time, but if you don't then you will want to use dynamic sql:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' + QUOTENAME(name)
from yourtable
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT property,' + #cols + ' from
(
select property, name, value
from yourtable
) x
pivot
(
max(value)
for name in (' + #cols + ')
) p '
execute(#query)
See SQL Fiddle with Demo
All three will produce the same result:
| PROPERTY | BOB | FRED |
---------------------------
| Eyes | Blue | |
| Hair | Red | Brown |
| Height | | Tall |
Here's the old-fashioned way, of course assuming that each (Name, Property) is unique:
SELECT Properties.Property, Bob.Value, Fred.Value
FROM
(
SELECT DISTINCT Property
FROM myTable
) Properties
LEFT OUTER JOIN
(
SELECT Property, Value
FROM myTable
WHERE Name = 'Bob'
) Bob ON Properties.Property = Bob.Property
LEFT OUTER JOIN
(
SELECT Property, Value
FROM myTable
WHERE Name = 'Fred'
) Fred ON Properties.Property = Fred.Property
Of course you can only do this if you know the columns ahead of time. You could make and execute dynamic SQL if you did not, but this is not without its issues.
Depending on your RDBMS you may be able to use a pivot query instead, which will simplify the syntax (or make it possible if you have an unknown number/names of people)
Ok SO, here's your time to shine!
No really, I'm getting my butt kicked by an MS-SQL query that I can't seem to get to work.
What I am trying to do is search on a patient name; but also return patients who have a similar first or last name to the querying patient's last name. So "John Smith" can return anyone named "John Smith" or anyone who has a first or last name like "smith". If the a patient has multiple disease states, then combine those disease states into a single column. I have the following tables (though of course there are many more columns, but these are the most imortant):
Patient Table
PatientID FirstName LastName UserIDFK
10000 John Smith 1
10001 Miss Smith 2
10002 Smith Bomb 3
10003 Bobby Smith 4
-- etc
DiseaseStateForUser
UserIDFK DiseaseStateRefId
1 1
1 2
2 2
3 1
3 2
4 1
GlobalLookUp
RefId Ref_Code
1 HIV
2 HEPC
The results I'm looking for are this:
PatientID FirstName LastName DiseaseStates
10000 John Smith HIV|HEPC
10001 Miss Smith HEPC
10002 Smith Bomb HIV|HEPC
10003 Bobby Smith HIV
I've taken the examples from these questions (and countless others):
Is there a way to create a SQL Server function to “join” multiple
rows from a subquery into a single delimited
field?
Simulating group_concat MySQL function in MS SQL Server
2005?
As well as from this blog post Emulating MySQL’s GROUP_CONCAT() Function in SQL Server 2005 I came up with the following SQL procedure
DECLARE
#PatientID INT=null,
#FirstName Varchar(15)= null,
#LastName Varchar(15)= 'Smith',
#Name Varchar(15) = 'John Smith',
Select
Patient.First_Name,
Patient.Last_Name,
patient.PatientID,
(select CAST(GlobalLookUp.Ref_Code + '|' as VARCHAR(MAX))
from
TBL_PATIENT patient
,TBL_GBLLOOKUP GlobalLookUp
,TBL_DiseaseStateForUser DiseaseStateForUser
-- Try and make a collection of all the PatientIDs
-- that match the search criteria
-- so that only these are used to build
-- the DiseaseStatesColumn
,(Select
Patient.PatientID
FROM TBL_PATIENT patient
,TBL_SITEMASTER SiteMaster
,TBL_USERMASTER UserMaster
,TBL_USERSINSITES UserInSites
,TBL_GBLLOOKUP GlobalLookUp
,TBL_DiseaseStateForUser DiseaseStateForUser
WHERE (((patient.[Last_Name] like #LastName + '%') OR (patient.[Last_Name] Like #Name + '%' ))
OR ((patient.[First_Name] Like #Name + '%' ))
OR (patient.[First_Name] + ' ' + patient.[Last_Name] Like #Name + '%' ))
AND UserMaster.User_Id = UserInSites.User_Id_FK
AND UserInSites.Site_Id_FK = SiteMaster.Site_Id
AND UserInSites.Is_Active = 'True'
AND patient.[User_Id_FK] = UserMaster.[User_Id]
AND (DiseaseStateForUser.User_Id_FK = patient.User_Id_FK
AND DiseaseStateForUser.DiseaseState_RefId_FK = GlobalLookUp.Ref_Id)
and DiseaseStateForUser.Is_Active='True'
AND patient.[Is_Active] = 'TRUE'
group by Patient.PatientID) as PATIENTIDs
where patient.PatientID = PATIENTIDs.PatientID
AND (DiseaseStateForUser.User_Id_FK = patient.User_Id_FK
AND DiseaseStateForUser.DiseaseState_RefId_FK = GlobalLookUp.Ref_Id)
For XML PATH('')) as MultiDiseaseState
FROM TBL_PATIENT patient, TBL_SITEMASTER SiteMaster ,TBL_USERMASTER UserMaster,TBL_USERSINSITES UserInSites, TBL_GBLLOOKUP GlobalLookUp, TBL_DiseaseStateForUser DiseaseStateForUser
WHERE (((patient.[Last_Name] like #LastName + '%') OR (patient.[Last_Name] Like #Name + '%' ))
or ((patient.[First_Name] Like #Name + '%' ))
OR (patient.[First_Name] + ' ' + patient.[Last_Name] Like #Name + '%' ))
AND patient.PatientID = patient.PatientID
AND UserMaster.User_Id = UserInSites.User_Id_FK
AND UserInSites.Site_Id_FK = SiteMaster.Site_Id
AND UserInSites.Is_Active = 'True'
AND patient.[User_Id_FK] = UserMaster.[User_Id]
AND DiseaseStateForUser.User_Id_FK = patient.User_Id_FK
AND DiseaseStateForUser.DiseaseState_RefId_FK = GlobalLookUp.Ref_Id
and DiseaseStateForUser.Is_Active='True'
AND patient.[Is_Active] = 'TRUE'
group by PatientID, patient.First_Name, patient.Last_Name, GlobalLookUp.Ref_Code
order by PatientID
Unfortunately, this query nets me the following:
PatientID FirstName LastName MultiDiseaseState
10000 John Smith HIV|HEPC|HEPC|HIV|HEPC|HIV
10001 Miss Smith HIV|HEPC|HEPC|HIV|HEPC|HIV
10002 Smith Bomb HIV|HEPC|HEPC|HIV|HEPC|HIV
10003 Bobby Smith HIV|HEPC|HEPC|HIV|HEPC|HIV
In other words, the select CAST(GlobalLookUp.Ref_Code + '|' as VARCHAR(MAX)) call is building up the MultiDiseaseState column with all of the disease states for ALL of the selected patients.
I know there is something fundamentally wrong with the most inner SELECT statement, but I'm having a hard time figuring out what it is and how to write the query so that it builds only the disease states for a given patient.
Kind of a long post, but are there any suggestions people can make given the code snippets I've provided?
You should be able to use the Stuff function (I think it's only on SQL 2005 and higher) to make this work, I took your example data and wrote a demonstration off of that
SET NOCOUNT ON
CREATE TABLE #Patient
(
PatientID INT,
FirstName varchar(25),
LastName varchar(25),
UserIDFK INT
)
INSERT INTO #PATIENT SELECT 10000,'John','Smith',1
INSERT INTO #PATIENT SELECT 10001,'Miss','Smith',2
INSERT INTO #PATIENT SELECT 10002,'Smith','Bomb',3
INSERT INTO #PATIENT SELECT 10003,'Bobby','Smith',4
CREATE TABLE #DiseaseStateForUser
(
UserIDFK int,
DiseaseStateRefId int
)
INSERT INTO #DiseaseStateForUser SELECT 1,1
INSERT INTO #DiseaseStateForUser SELECT 1,2
INSERT INTO #DiseaseStateForUser SELECT 2,2
INSERT INTO #DiseaseStateForUser SELECT 3,1
INSERT INTO #DiseaseStateForUser SELECT 3,2
INSERT INTO #DiseaseStateForUser SELECT 4,1
CREATE TABLE #GlobalLookUp
(
RefId int,
Ref_Code varchar(10)
)
INSERT INTO #GlobalLookUp SELECT 1,'HIV'
INSERT INTO #GlobalLookUp SELECT 2,'HEPC'
SELECT
PatientID,
UserIDFK,
FirstName,
LastName,
STUFF(
(SELECT '|' + l.Ref_Code
FROM #DiseaseStateForUser u with (Nolock)
JOIN dbo.#GlobalLookUp l with (nolock)
ON u.DiseaseStateRefId = l.RefId
WHERE u.UserIDFK = p.UserIDFK FOR XML PATH('')
)
, 1, 1, '')
FROM #PATIENT p with (Nolock)
GROUP BY PatientID, FirstName, LastName, UserIDFK