Need horizontal columns in vertical - sql

I have compiled the data in a table called Employees. Table definition is
Name Age
Sam 25
Mike 28
Is it possible to write a query that can give me the output in format
SAM
25
MIKE
28
I am unable to write this query. Is it possible to do it.
If not, how can i achieve that.
I can do it using a cursor but it will largely degrade the performance of my proc.

Easily modified to suite your needs
Declare #User table (id int,First_Name varchar(50),Last_Name varchar(50),EMail varchar(50))
Insert into #User values
(1,'John','Smith','john.smith#gmail.com'),
(2,'Jane','Doe' ,'jane.doe#gmail.com')
Declare #XML xml
Set #XML = (Select * from #User for XML RAW)
Select ID = r.value('#id','int')
,Item = Attr.value('local-name(.)','varchar(100)')
,Value = Attr.value('.','varchar(max)')
From #XML.nodes('/row') as A(r)
Cross Apply A.r.nodes('./#*[local-name(.)!="id"]') as B(Attr)
Returns
ID Item Value
1 First_Name John
1 Last_Name Smith
1 EMail john.smith#gmail.com
2 First_Name Jane
2 Last_Name Doe
2 EMail jane.doe#gmail.com

As requested, but I see little value in it
Declare #Table varchar(150) = 'YourTableName'
Declare #SQL varchar(max) = '>>>'
Select #SQL = Replace(#SQL + SQL ,'>>>Union All ','')
From (Select Seq=ORDINAL_POSITION,SQL='Union All Select Value=cast(['+Column_Name+'] as varchar(500)) From ['+Table_Schema+'].['+Table_Name+']' From INFORMATION_SCHEMA.COLUMNS where Table_Name=#Table) A
Order By Seq
--Print #SQL
Exec(#SQL)
Sample Return
Value
22 Star Ave, Riverside, RI 02915
22 Planet Ave, Riverside, RI 02915
100 Peck Ave, Riverside, RI 02915
1086 Willett Ave, Riverside, RI 02915
4
5
6

Related

Create String Template from two stored procedures

I have two stored procedures that return result sets.
How can I use them to populate a string and update another column?
exec getEmailSignatureDetails 'Jane', 'Doe'
exec getFeaturedAccount 'June'
These both return columns that I would like to map to variables.
I would then like to put the variables into a string.
Then update a column in another table with that string.
Output from getEmailSignatureDetails:
addCity | addLine | addSt | addZip | fName | lName
--------------+-------------+-------+---------+-------+------
San Francisco | 777 SV Lane | CA | 94016 | Jane | Doe
Output from getFeaturedAccount:
month | img
------+----------
June | base64...
I would like to turn this into a string like
Your package has been delivered to
#fName #lName
#addLine
#addCity #addSt, #addZip
#img
And then update a column with this string matching on name.
If I understand your question your are looking to dynamically fill-in a template via macro substitution
Example
-- Create some Sample Data
Declare #getEmailSignatureDetails Table ([addCity] varchar(50),[addLine] varchar(50),[addSt] varchar(50),[addZip] varchar(50),[fName] varchar(50),[lName] varchar(50))
Insert Into #getEmailSignatureDetails Values
('San Francisco','777 SV Lane','CA',94016,'Jane','Doe')
Declare #getFeaturedAccount Table ([month] varchar(50),[img] varchar(50))
Insert Into #getFeaturedAccount Values
('June','base64..')
-- Declare the Template
Declare #Template varchar(max) ='
Your package has been delivered to
#fName #lName
#addLine
#addCity #addSt, #addZip
#img
'
-- Populate the Template
Select #Template = replace(#Template,'#'+Field,Value)
From (
Select C.*
From (values (convert(XML,(Select * From #getEmailSignatureDetails Join #getFeaturedAccount on [month]='June' For XML Raw ) ) ) ) A(XMLData)
Cross Apply (
Select Field = a.value('local-name(.)','varchar(100)')
,Value = a.value('.','varchar(max)')
From A.XMLData.nodes('/row') as C1(n)
Cross Apply C1.n.nodes('./#*') as C2(a)
Where a.value('local-name(.)','varchar(100)') not in ('Column1','Column2')
) C
) A
Updated Template
Your package has been delivered to
Jane Doe
777 SV Lane
San Francisco CA, 94016
base64..
If it helps with the visualization, the sub-query is a "dynamic" unpivot and generates the following:
Field Value
addCity San Francisco
addLine 777 SV Lane
addSt CA
addZip 94016
fName Jane
lName Doe
month June
img base64.. -- (presumably would be the image)

Return Array of Column Names if Index is True (SQL Server 2008)

I have a SQL Server table in the following form (data type is Bit):
role | column_1 | column_2 | column_3
------+------------+------------+-----------
a | True | True | False
b | True | False | True
c | False | False | True
I have tried the following:
SELECT *
FROM Roles
WHERE EXISTS (SELECT 'column_1' = 1)
(SELECT 'column_2' = 1)
(SELECT 'column_3' = 1)
However, this seems to return values 3 separate times.
How can I return the column names for a given role (ideally in a consolidated array) if the index is true?
For example:
For role a, return column_1 and column_2
For role b, return column_1 and column_3
For role c, return column_2 and column_3
Based on your comments.
Simple approach if you don't mind listing the fields
Select Role
,Roles = stuff(
replicate(',column_1',column_1)
+replicate(',column_2',column_2)
+replicate(',column_3',column_3)
,1,1,'')
From Roles
Or if you want a more dynamic approach
Example
Select A.Role
,C.*
From Roles A
Cross Apply ( values (cast((Select A.* for XML RAW) as xml))) B(XMLData)
Cross Apply (
Select Roles = Stuff((Select ','+Field
From (
Select Field = a.value('local-name(.)','varchar(100)')
,Value = a.value('.','varchar(max)')
From B.XMLData.nodes('/row') as C1(n)
Cross Apply C1.n.nodes('./#*') as C2(a)
Where a.value('local-name(.)','varchar(100)') not in ('role','ExcludeOther')
and a.value('.','varchar(max)') ='1'
) C1
For XML Path ('')),1,1,'')
) C
Both Would Return
Role Roles
a column_1,column_2
b column_1,column_3
c column_3
A stored procedure based on suggestions from John Cappelletti.
Input table name, column name, filter
Returns filter index | column name | column value
-- filterTableWithString
Alter Procedure [dbo].[filterTableWithString]
#table as VarChar(50),
#column as VarChar(50),
#filter as VarChar(50),
#string as VarChar(max)
As
Declare #ssql NVarChar(max)
Select #ssql =
'Select ' + #column +', c.*
From' + QUOTENAME(#table) + 'a
Cross Apply (Values (Cast((Select A.* for XML RAW) as xml))) B(XMLData)
Cross Apply (
Select Field = a.value(''local-name(.)'',''VarChar(max)''),
Value = a.value(''.'',''VarChar(max)'')
From B.XMLData.nodes(''/row'') as C1(n)
Cross Apply C1.n.nodes(''./#*'') as C2(a)
Where a.value(''local-name(.)'',''VarChar(max)'') Not In (''ExcludeOther'')
) C
Where ' + #column + ' = ''' + #filter + '''
And Cast(c.value as VarChar(max)) Like ( ''' + #string +''')'
Exec sp_executesql #ssql
I think you are making this a lot harder than it needs to be. And in your subqueries you are comparing a string literal to an int which will always fail. Pretty sure you just need to use a couple of predicates here.
SELECT *
FROM Roles
WHERE column_1 = 1
AND column_2 = 1
AND column_3 = 1
As promised, here is my "dynamic" EAV function. You can pass a record or entire table (avoid large tables).
To be clear, this is a helper function used to "dynamically" unpivot smaller tables or selected records within a CROSS APPLY.
Just a note, the first field in the query must be your Entity.
Example of Table
Declare #User table (ID int,Active bit,First_Name varchar(50),Last_Name varchar(50),EMail varchar(50))
Insert into #User values
(1,1,'John','Smith','john.smith#email.com'),
(2,0,'Jane','Doe' ,'jane.doe#email.com')
Select * From [dbo].[tvf-EAV]((Select * From #User for XML RAW))
Returns
Entity Attribute Value
1 Active 1
1 First_Name John
1 Last_Name Smith
1 EMail john.smith#email.com
2 Active 0
2 First_Name Jane
2 Last_Name Doe
2 EMail jane.doe#email.com
Example of ROW via Cross Cross Apply
Declare #User table (ID int,Active bit,First_Name varchar(50),Last_Name varchar(50),EMail varchar(50))
Insert into #User values
(1,1,'John','Smith','john.smith#email.com'),
(2,0,'Jane','Doe' ,'jane.doe#email.com')
Select A.EMail
,B.*
From #User A
Cross Apply [dbo].[tvf-EAV]((Select A.* for XML RAW)) B
Where Active=1
Returns
EMail Entity Attribute Value
john.smith#email.com 1 Active 1
john.smith#email.com 1 First_Name John
john.smith#email.com 1 Last_Name Smith
john.smith#email.com 1 EMail john.smith#email.com

Split comma separated value from table column into rows using mssql?

I would like to get some data into mssql view by splitting their sources. I have some columns where phone numbers are stored as comma separated values (each contains a phone contact). I neet to work with each "phone contact", so I would like to see them in rows each one. And also each row has to contain an order of the contact from the splitting.
Source:
Department | SaleMngrs | Operators | Secretary
----------------------------------------------------------
'Technics' | '123,456,77'| '+122,Line 1' | '77889,112'
'Development'| '123,3366' | null | 'Lines 7-8'
As you can see, the comma separated values are a total mess, but the spliter is , (comma).
Wanted result:
Department | TypeOfContact | Contact | ContactOrder
------------------------------------------------------
'Technics' | 'SalesManagers'| '123' | 1
'Technics' | 'SalesManagers'| '456' | 2
'Technics' | 'SalesManagers'| '77' | 3
'Technics' | 'Operators' | '+122' | 1
'Technics' | 'Operators' | 'Line 1' | 2
'Technics' | 'Secretary' | '77889' | 1
'Technics' | 'Secretary' | '112' | 2
'Development'| 'SalesManagers'| '123' | 1
'Development'| 'SalesManagers'| '3366' | 2
'Development'| 'Secretary' | 'Lines 7-8'| 1
No UDF or SP wanted. Just a SELECT please.
Please try the following (it is as pretty as the data structure) - optimized with UNPIVOT:
set nocount on
declare #source table (Department varchar(50), SaleMngrs varchar(50), Operators varchar(50), Secretary varchar(50));
insert into #source values ('Technics' , '123,456,77', '+122,Line 1' , '77889,112');
insert into #source values ('Development', '123,3366' , null , 'Lines 7-8');
;WITH cte (Department, TypeOfContact, Contact)
AS
(
SELECT Department, TypeOfContact, cast('<Contact><c>' + replace(Contact,',','</c><c>') + '</c></Contact>' as xml) AS Contact
FROM (SELECT Department, SaleMngrs AS SalesManagers, Operators, Secretary FROM #source) p
UNPIVOT (Contact FOR TypeOfContact IN (SalesManagers, Operators, Secretary)) AS unpvt
)
Select Department
, TypeOfContact
, Contact.c.value('.','varchar(20)') AS Contact
, ROW_NUMBER() OVER (PARTITION BY Department, TypeOfContact ORDER BY Department, TypeOfContact) AS ContactOrder
FROM cte CROSS APPLY Contact.nodes('/Contact/c') as Contact(c);
OUTPUT
Department TypeOfContact Contact ContactOrder
------------ ------------- -------------------- --------------------
Development SalesManagers 123 1
Development SalesManagers 3366 2
Development Secretary Lines 7-8 1
Technics Operators +122 1
Technics Operators Line 1 2
Technics SalesManagers 123 1
Technics SalesManagers 456 2
Technics SalesManagers 77 3
Technics Secretary 112 1
Technics Secretary 77889 2
EDIT: Optimized query using UNPIVOT (original below):
set nocount on
declare #source table (Department varchar(50), SaleMngrs varchar(50), Operators varchar(50), Secretary varchar(50));
insert into #source values ('Technics' , '123,456,77', '+122,Line 1' , '77889,112');
insert into #source values ('Development', '123,3366' , null , 'Lines 7-8');
;WITH cte (Department, SalesMngrs, Operators, Secretary)
AS
(
select Department
, cast('<SaleMngrs><c>' + replace(SaleMngrs,',','</c><c>') + '</c></SaleMngrs>' as xml) AS SalesMngrs
, cast('<Operators><c>' + replace(Operators,',','</c><c>') + '</c></Operators>' as xml) AS Operators
, cast('<Secretary><c>' + replace(Secretary,',','</c><c>') + '</c></Secretary>' as xml) AS Secretary
from #source
)
Select Department
, TypeOfContact
, Contact
, ROW_NUMBER() OVER (PARTITION BY Department, TypeOfContact ORDER BY Department, TypeOfContact) AS ContactOrder
FROM (
Select Department, 'SalesManagers' AS TypeOfContact, SaleMngrs.c.value('.','varchar(20)') as Contact
from cte CROSS APPLY SalesMngrs.nodes('/SaleMngrs/c') as SaleMngrs(c)
union
Select Department, 'Operators', Operators.c.value('.','varchar(20)')
from cte CROSS APPLY Operators.nodes('/Operators/c') as Operators(c)
union
Select Department, 'Secretary', Secretary.c.value('.','varchar(20)')
from cte CROSS APPLY Secretary.nodes('/Secretary/c') as Secretary(c)
) AS q;
Martin,
I know you wanted this in one SQL Statement, but if you create a Function then the SQL won't be so unpretty.
ALTER FUNCTION dbo.Split ( #InputString VARCHAR(8000), #Delimiter VARCHAR(50))
RETURNS #Items TABLE ( Item VARCHAR(8000), Rowid INT)
AS
BEGIN
IF #Delimiter = ' '
BEGIN
SET #Delimiter = ','
SET #InputString = REPLACE(#InputString, ' ', #Delimiter)
END
IF (#Delimiter IS NULL OR #Delimiter = '')
SET #Delimiter = ','
DECLARE #Item VARCHAR(8000)
DECLARE #ItemList VARCHAR(8000)
DECLARE #DelimIndex INT
declare #rowseq INT
SET #rowseq = 0
SET #ItemList = #InputString
SET #DelimIndex = CHARINDEX(#Delimiter, #ItemList, 0)
WHILE (#DelimIndex != 0)
BEGIN
SET #Item = SUBSTRING(#ItemList, 0, #DelimIndex)
SET #rowseq = #rowseq + 1
INSERT INTO #Items VALUES (#Item, #rowseq)
-- 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
BEGIN
SET #Item = #ItemList
SET #rowseq = #rowseq + 1
INSERT INTO #Items VALUES (#Item, #rowseq)
END
-- No delimiters were encountered in #InputString, so just return #InputString
ELSE INSERT INTO #Items VALUES (#InputString, 1)
RETURN
END
The above Function I found from this SO question, but i made some changes to it for your scenario. How to split a comma-separated value to rows
Then you SQL will be...
SELECT department, 'SalesManager' as TypeOfContract, s.Item as Contact , s.rowId
FROM <YOUR TABLE> t
CROSS APPLY
(SELECT * FROM dbo.Split(t.SalesMngrs, ',') where item is not null) S
UNION ALL
SELECT department, 'Operators' as TypeOfContract, s.Item as Contact , s.rowId
FROM <YOUR TABLE> t
CROSS APPLY
(SELECT * FROM dbo.Split(t.Operators, ',') where item is not null ) S
UNION ALL
SELECT department, 'Secretary' as TypeOfContract, s.Item as Contact , s.rowId
FROM <YOUR TABLE> t
CROSS APPLY
(SELECT * FROM dbo.Split(t.Secretary, ',') where item is not null) S
I hope this helps.

Stored procedure that returns a table from 2 combined

I am trying to write a stored procedure which returns a result combining 2 table variables which looks something like this.
Name | LastName | course | course | course | course <- Columns
Name | LastName | DVA123 | DVA222 | nothing | nothing <- Row1
Pete Steven 200 <- Row2
Steve Lastname 50 <- Row3
From these 3 tables
Table Staff:
Name | LastName | SSN |
Steve Lastname 234
Pete Steven 132
Table Course Instance:
Course | Year | Period |
DVA123 2013 1
DVA222 2014 2
Table Attended by:
Course | SSN | Year | Period | Hours |
DVA123 234 2013 1 200
DVA222 132 2014 2 50
I am taking #year as a parameter that will decide what year in the course will be displayed in the result.
ALTER proc [dbo].[test4]
#year int
as
begin
-- I then declare the 2 tables which I will then store the values from the tables
DECLARE #Table1 TABLE(
Firstname varchar(30) NOT NULL,
Lastname varchar(30) NOT NULL
);
DECLARE #Table2 TABLE(
Course varchar(30) NULL
);
Declare #variable varchar(max) -- variable for saving the cursor value and then set the course1 to 4
I want at highest 4 results/course instances which I later order by the period of the year
declare myCursor1 CURSOR
for SELECT top 4 period from Course instance
where year = #year
open myCursor1
fetch next from myCursor1 into #variable
--print #variable
while ##fetch_status = 0
Begin
UPDATE #Table2
SET InstanceCourse1 = #variable
where current of myCursor1
fetch next from myCursor1 into #variable
print #variable
End
Close myCursor1
deallocate myCursor1
insert into #table1
select 'Firstname', 'Lastname'
insert into #table1
select Firstname, Lastname from staff order by Lastname
END
select * from #Table1 -- for testing purposes
select * from #Table2 -- for testing purposes
--Then i want to combine these tables into the output at the top
This is how far I've gotten, I don't know how to get the courses into the columns and then get the amount of hours for each staff member.
If anyone can help guide me in the right direction I would be very grateful. My idea about the cursor was to get the top (0-4) values from the top4 course periods during that year and then add them to the #table2.
Ok. This is not pretty. It is a really ugly dynamic sql, but in my testing it seems to be working. I have created an extra subquery to get the courses values as the first row and then Union with the rest of the result. The top four courses are gathered by using ROW_Number() and order by Year and period. I had to make different versions of the courses string I am creating in order to use them for both column names, and in my pivot. Give it a try. Hopefully it will work on your data as well.
DECLARE #Year INT
SET #Year = 2014
DECLARE #Query NVARCHAR(2000)
DECLARE #CoursesColumns NVARCHAR(2000)
SET #CoursesColumns = (SELECT '''' + Course + ''' as c' + CAST(ROW_NUMBER() OVER(ORDER BY Year, Period) AS nvarchar(50)) + ',' AS 'data()'
FROM AttendedBy where [Year] = #Year
for xml path(''))
SET #CoursesColumns = LEFT(#CoursesColumns, LEN(#CoursesColumns) -1)
SET #CoursesColumns =
CASE
WHEN CHARINDEX('c1', #CoursesColumns) = 0 THEN #CoursesColumns + 'NULL as c1, NULL as c2, NULL as c3, NULL as c4'
WHEN CHARINDEX('c2', #CoursesColumns) = 0 THEN #CoursesColumns + ',NULL as c2, NULL as c3, NULL as c4'
WHEN CHARINDEX('c3', #CoursesColumns) = 0 THEN #CoursesColumns + ', NULL as c3, NULL as c4'
WHEN CHARINDEX('c4', #CoursesColumns) = 0 THEN #CoursesColumns + ', NULL as c4'
ELSE #CoursesColumns
END
DECLARE #Courses NVARCHAR(2000)
SET #Courses = (SELECT Course + ' as c' + CAST(ROW_NUMBER() OVER(ORDER BY Year, Period) AS nvarchar(50)) + ',' AS 'data()'
FROM AttendedBy where [Year] = #Year
for xml path(''))
SET #Courses = LEFT(#Courses, LEN(#Courses) -1)
SET #Courses =
CASE
WHEN CHARINDEX('c1', #Courses) = 0 THEN #Courses + 'NULL as c1, NULL as c2, NULL as c3, NULL as c4'
WHEN CHARINDEX('c2', #Courses) = 0 THEN #Courses + ',NULL as c2, NULL as c3, NULL as c4'
WHEN CHARINDEX('c3', #Courses) = 0 THEN #Courses + ', NULL as c3, NULL as c4'
WHEN CHARINDEX('c4', #Courses) = 0 THEN #Courses + ', NULL as c4'
ELSE #Courses
END
DECLARE #CoursePivot NVARCHAR(2000)
SET #CoursePivot = (SELECT Course + ',' AS 'data()'
FROM AttendedBy where [Year] = #Year
for xml path(''))
SET #CoursePivot = LEFT(#CoursePivot, LEN(#CoursePivot) -1)
SET #Query = 'SELECT Name, LastName, c1, c2, c3, c4
FROM (
SELECT ''Name'' as name, ''LastName'' as lastname, ' + #CoursesColumns +
' UNION
SELECT Name, LastName,' + #Courses +
' FROM(
SELECT
s.Name
,s.LastName
,ci.Course
,ci.Year
,ci.Period
,CAST(ab.Hours AS NVARCHAR(100)) AS Hours
FROM Staff s
LEFT JOIN AttendedBy ab
ON
s.SSN = ab.SSN
LEFT JOIN CourseInstance ci
ON
ab.Course = ci.Course
WHERE ci.Year=' + CAST(#Year AS nvarchar(4)) +
' ) q
PIVOT(
MAX(Hours)
FOR
Course
IN (' + #CoursePivot + ')
)q2
)q3'
SELECT #Query
execute(#Query)
Edit: Added some where clauses so only courses from given year is shown. Added Screenshot of my results.
try this
DECLARE #CourseNameString varchar(max),
#query AS NVARCHAR(MAX);
SET #CourseNameString=''
select #CourseNameString = STUFF((SELECT distinct ',' + QUOTENAME(Course)
FROM Attended where [Year]= 2013
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = '
select Name,LastName,'+#CourseNameString+' from Staff as e inner join (
SELECT * FROM
(SELECT [Hours],a.SSN,a.Course as c FROM Attended as a inner JOIN Staff as s
ON s.SSN = s.SSN) p
PIVOT(max([Hours])FOR c IN ('+#CourseNameString+')) pvt)p
ON e.SSN = p.SSN'
execute(#query)
Use subquery like this one :
SELECT Firstname, Lastname, (select instanceCourse1 from table2) as InstanceCourse1 from Table1

Trouble combining rows into one column using CAST(

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