Get unique values from multiple columns in single request - sql

I have an employee data with several columns. I have a WebAPI that is reading from this database for a search application that surfaces users. I need to get a list of unique values for 4 of the fields. I am hoping to do this in a single query if possible.
I have 25+ columns but I need the unique values for 4 columns (JobTitle,Department,Position,Group). I just need them return as a comma-separated list.
Sample SQL Server 2016 Table:
Name | JobTitle | Department | Position | Group
------|----------|--------------|------------|---------
John | Partner | Department1 | Position1 | Group1
Jane | Manager | Department2 | Position2 | Group2
Joe | Analyst | Department2 | Position2 | Group2
I want my results to be a single row with a list of unique value that are comma-separated:
Single row returned
Departments: Department1,Department2
JobTitles: Partner,Manager,Analyst
Positions: Position1,Position2
Groups: Group1, Group2

Upto SQL Server 2016, you can use STUFF to concatenate STRING from different row into one Row/Column with a comma separator. This following script will return list of DISTINCT values as per your requirement-
SELECT DISTINCT
STUFF((SELECT DISTINCT ',' + A.JobTitle FROM Employee A FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') JobTitle,
STUFF((SELECT DISTINCT ',' + A.Department FROM Employee A FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') Department,
STUFF((SELECT DISTINCT ',' + A.Position FROM Employee A FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') Position,
STUFF((SELECT DISTINCT ',' + A.[Group] FROM Employee A FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') [Group]
FROM Employee A
If you want to add label before the list of distinct values, use this following script-
SELECT
'JobTitles: ' + B.JobTitle AS JobTitle,
'Departments: ' + B.Department AS Department,
'Positions: ' + B.Position AS Position,
'Groups: ' + B.[Group] AS [Group]
FROM
(
SELECT DISTINCT
STUFF((SELECT DISTINCT ',' + A.JobTitle FROM Employee A FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') JobTitle,
STUFF((SELECT DISTINCT ',' + A.Department FROM Employee A FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') Department,
STUFF((SELECT DISTINCT ',' + A.Position FROM Employee A FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') Position,
STUFF((SELECT DISTINCT ',' + A.[Group] FROM Employee A FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') [Group]
FROM Employee A
)B

Related

Not able to create UNIQUE CLUSTERED INDEX on View WITH SCHEMABINDING having sub queries

Below is the code:
CREATE VIEW [dbo].[View_GetAllContents] WITH SCHEMABINDING AS
SELECT C.ContentID,
C.Title,
C.[Description],
C.Tags,
C.Markets,
C.Formats,
C.Categories,
C.IsGlobalSalesResource,
C.IsActive,
C.CreatedBy,
C.CreatedDate,
C.ModifiedBy,
STUFF(
(SELECT ',' + T.TagName
FROM [dbo].[Tags] T
WHERE T.IsActive=1
AND T.TagID IN
(SELECT LTRIM(RTRIM(items))
FROM [dbo].[SplitString](C.Tags, ','))
FOR XML PATH(''), TYPE).value('.[1]', 'nvarchar(max)'), 1, 2, '') AS TagNames,
STUFF(
(SELECT ',' + M.MarketName
FROM [dbo].[Markets] M
WHERE M.IsActive=1
AND M.MarketID IN
(SELECT LTRIM(RTRIM(items))
FROM [dbo].[SplitString](C.Markets, ','))
FOR XML PATH(''), TYPE).value('.[1]', 'nvarchar(max)'), 1, 2, '') AS MarketNames,
STUFF(
(SELECT ',' + F.FormatName
FROM [dbo].[Formats] F
WHERE F.IsActive=1
AND F.FormatID IN
(SELECT LTRIM(RTRIM(items))
FROM [dbo].[SplitString](C.Formats, ','))
FOR XML PATH(''), TYPE).value('.[1]', 'nvarchar(max)'), 1, 2, '') AS FormatNames,
STUFF(
(SELECT ',' + Ca.CategoryName
FROM [dbo].[Categories] Ca
WHERE Ca.IsActive=1
AND Ca.CategoryID IN
(SELECT LTRIM(RTRIM(items))
FROM [dbo].[SplitString](C.Categories, ','))
FOR XML PATH(''), TYPE).value('.[1]', 'nvarchar(max)'), 1, 2, '') AS CategorieNames,
STUFF(
(SELECT '$A$' + cast(A.AttachmentID AS varchar) + '$,$' + cast(A.ActualFileName AS nvarchar(1000))
FROM [dbo].[Attachments] A
WHERE A.ContentID=C.ContentID
FOR XML PATH(''), TYPE).value('.[1]', 'nvarchar(max)'), 1, 3, '') AS Attachments
FROM [dbo].[Contents] C GO
Error on below code:
CREATE UNIQUE CLUSTERED INDEX [IX_View_GetAllContents_ContentID] ON [dbo].[View_GetAllContents](ContentID);
GO
Exception:
Cannot create index on view "SM_SalesWarehouse.dbo.View_GetAllContents" because it contains one or more subqueries. Consider changing the view to use only joins instead of subqueries. Alternatively, consider not indexing this view.

How to make a SQL query adding in a field with multiple entries

HELP
My Query:
SELECT
[_ResourceGuid]
,[TICKET]
= STUFF((
SELECT ',' + [Ticket Number]
FROM [Inv_Service_Desk_Ticket]
Where _ResourceGuid = _ResourceGuid
FOR XML PATH(''), TYPE).value('.', 'varchar(max)'), 1, 1, '')
FROM Inv_Service_Desk_Ticket]
Results Should look like:
[_Resource Guid] TICKET
klsdro72934579072 234234,76456,56756
lkjd1234907812302 456456,34345,45455
Table Fields:
ID ResourceTicket Number
23 lkjd1234907812302 456456
123 lkjd1234907812302 34345
345 lkjd1234907812302 45455
233 klsdro72934579072 234234
567 klsdro72934579072 76456
978 klsdro72934579072 56756
Notice, the "ID" is unique and the Ticket Number is unique, the Resource can be duplicated
Try this:
SELECT DISTINCT
a.[_ResourceGuid]
, STUFF((
SELECT ',' + [Ticket Number]
FROM [Inv_Service_Desk_Ticket]
Where _ResourceGuid = a._ResourceGuid
FOR XML PATH('')
, TYPE).value('.', 'varchar(max)'), 1, 2, '')
FROM [Inv_Service_Desk_Ticket] a
Edit: Stuff formatted correctly, I think... didn't test it. Stuff is just a subquery where you put it in an XML. You have to add it to your main query like it's a column.

Select comma sperated column value

I'm trying to get comma , separated column value in SQL Server.
Select
CT.ClaimTypeId, CT.ClaimTypeName,CT.MHWSchemeNo,CT.MHWClaimTypeID,CFCTR.FormId,CF.Name,
FF.FormId,FF.FieldId,FF.Label,FF.Name,FF.IsDefaultField,FF.IsFieldLabelVisible,FF.IsRequired,FF.Type,
Label = STUFF((
SELECT ',' +JFFO.Label
FROM dbo.FormFieldOption JFFO
where JFFO.FieldId = FF.FieldId
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, ''),
Value= STUFF((
SELECT ',' +JFFO.Value
FROM dbo.FormFieldOption JFFO
where JFFO.FieldId = FF.FieldId
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, ''),
AdditionalTextfieldName= STUFF((
SELECT ',' +JFFO.AdditionalTextfieldName
FROM dbo.FormFieldOption JFFO
where JFFO.FieldId = FF.FieldId
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
from EmployeeClaimTypeRelationship ECR
LEFT JOIN ClaimType CT on CT.MHWSchemeNo=ECR.MHWSchemeNo
LEFT JOIN ClaimFormClaimTypeRelationship CFCTR on CFCTR.ClaimTypeId=CT.ClaimTypeId
LEFT JOIN ClaimForm CF on CF.FormId=CFCTR.FormId
LEFT JOIN FormField FF on FF.FormId=CFCTR.FormId
LEFT JOIN FormFieldOption FFO on FFO.FieldId=FF.FieldId
where ECR.MHWUserId='CITITEST1'
group by FF.FieldId,CT.ClaimTypeId, CT.ClaimTypeName,CT.MHWSchemeNo,CT.MHWClaimTypeID,CFCTR.FormId,CF.Name,
FF.FormId,FF.FieldId,FF.Label,FF.Name,FF.IsDefaultField,FF.IsFieldLabelVisible,FF.IsRequired,FF.Type
This is my query. And it is giving me the correct result. Here I'm using group by clause.
Now I want to optimize this query.
As you can see above, I have 3 columns which have comma , separated values which are coming from same table.
Is there any way so that I don't have to write multiple time same query for different fields?
Like this
Select
CT.ClaimTypeId, CT.ClaimTypeName,CT.MHWSchemeNo,CT.MHWClaimTypeID,CFCTR.FormId,CF.Name,
FF.FormId,FF.FieldId,FF.Label,FF.Name,FF.IsDefaultField,FF.IsFieldLabelVisible,FF.IsRequired,FF.Type,
Label = STUFF((
SELECT ',' +JFFO.Label
FROM dbo.FormFieldOption JFFO
where JFFO.FieldId = FF.FieldId
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, ''),
Value,AdditionalTextfieldName = STUFF((
SELECT ',' +JFFO.Value as Value,','+JFFO.AdditionalTextfieldName as AdditionalTextfieldName
FROM dbo.FormFieldOption JFFO
where JFFO.FieldId = FF.FieldId
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
from EmployeeClaimTypeRelationship ECR
LEFT JOIN ClaimType CT on CT.MHWSchemeNo=ECR.MHWSchemeNo
LEFT JOIN ClaimFormClaimTypeRelationship CFCTR on CFCTR.ClaimTypeId=CT.ClaimTypeId
LEFT JOIN ClaimForm CF on CF.FormId=CFCTR.FormId
LEFT JOIN FormField FF on FF.FormId=CFCTR.FormId
LEFT JOIN FormFieldOption FFO on FFO.FieldId=FF.FieldId
where ECR.MHWUserId='CITITEST1'
group by FF.FieldId,CT.ClaimTypeId, CT.ClaimTypeName,CT.MHWSchemeNo,CT.MHWClaimTypeID,CFCTR.FormId,CF.Name,
FF.FormId,FF.FieldId,FF.Label,FF.Name,FF.IsDefaultField,FF.IsFieldLabelVisible,FF.IsRequired,FF.Type
You could have a view.
CREATE VIEW view_name AS
SELECT * FROM ... WHERE ...
(Instead of *, you might prefer to select the union of all columns you ever might need and possibly give them aliases.) Then you would only need to
SELECT column1, column2, column3
FROM view_name
WHERE [additional conditions]
I would create a UDF Table-Valued Function and call that, it will also give you some performance benefit
Function Definition
CREATE FUNCTION dbo.fn_FormFieldOptionList ( #FieldId INT)
RETURNS TABLE
AS
RETURN (
SELECT DISTINCT
Label = STUFF((
SELECT ',' +JFFO.Label
FROM dbo.FormFieldOption JFFO
where JFFO.FieldId = FF.FieldId
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
,Value= STUFF((
SELECT ',' +JFFO.Value
FROM dbo.FormFieldOption JFFO
where JFFO.FieldId = FF.FieldId
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
,AdditionalTextfieldName= STUFF((
SELECT ',' +JFFO.AdditionalTextfieldName
FROM dbo.FormFieldOption JFFO
where JFFO.FieldId = FF.FieldId
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM dbo.FormFieldOption FF
WHERE FF.FieldId = #FieldId )
Your Query
Select
CT.ClaimTypeId, CT.ClaimTypeName,CT.MHWSchemeNo
,CT.MHWClaimTypeID,CFCTR.FormId,CF.Name
,FF.FormId,FF.FieldId,FF.Label,FF.Name
,FF.IsDefaultField,FF.IsFieldLabelVisible
,FF.IsRequired,FF.Type,f.Label. f.Value ,f.AdditionalTextfieldName
from EmployeeClaimTypeRelationship ECR
LEFT JOIN ClaimType CT on CT.MHWSchemeNo=ECR.MHWSchemeNo
LEFT JOIN ClaimFormClaimTypeRelationship CFCTR on CFCTR.ClaimTypeId=CT.ClaimTypeId
LEFT JOIN ClaimForm CF on CF.FormId=CFCTR.FormId
LEFT JOIN FormField FF on FF.FormId=CFCTR.FormId
OUTER APPLY dbo.fn_FormFieldOptionList(FF.FieldId) f
where ECR.MHWUserId='CITITEST1'
Here in this query, we are joining dbo.FormFieldOption twice. Good optimization & speed can be achieved if we can restructure the query. Try to restructure the join
towards table dbo.FormFieldOption to single.

SQL - Combine Multiple Columns with Multiple Rows into one row

What I'm trying to do:
I have records in a SQL table where there are 5 columns and thousands of rows.
The rows share duplicate data (i.e. account number) but what makes each unique is that data in one of the columns is different.
As an example:
col1|col2|col3|col4|col5
------------------------
123|abc|456|def|789
123|abc|456|def|date
But the columns can have different values, not necessarily always in column 5.
Here's what I started with:
SELECT TOP (15) stuff((
SELECT ', ' + te.[accountid]
,te.[char1]
,te.[date]
,te.[date2]
,te.[char2]
FROM D AS te
INNER JOIN D AS tue ON tue.[accountid] = te.[accountid]
WHERE tue.[accountid] = ue.[accountid]
FOR XML path('')
,type
).value('.', 'varchar(max)'), 1, 2, '') AS ifile
FROM D AS ue
GROUP BY ue.[accountid]
But I get a monster long string that includes the duplicate rows in one column. I'm not sure what else to try so any insight would be appreciated.
If I had to guess, you have an unnecessary self join in the subquery:
SELECT TOP (15) stuff((
SELECT ', ' + te.[accountid], te.[char1], te.[date], te.[date2], te.[char2]
FROM D te
WHERE te.[accountid] = ue.[accountid]
FOR XML path(''), type
).value('.', 'varchar(max)'), 1, 2, '') AS ifile
FROM D ue
GROUP BY ue.[accountid];
You might also want SELECT DISTINCT in the subquery.
Use UNION to get rid of all the duplicate values and use your FOR XML PATH on the output to append it to a single string:
SELECT TOP (15) stuff((
SELECT ', ' + CAST(te.[accountid] AS varchar(255)) FROM D
UNION
SELECT ', ' + CAST(te.[char1] AS varchar(255)) FROM D
UNION
SELECT ', ' + CAST(te.[date] AS varchar(255)) FROM D
UNION
SELECT ', ' + CAST(te.[date2] AS varchar(255)) FROM D
UNION
SELECT ', ' + CAST(te.[char2] AS varchar(255)) FROM D
FOR XML path('')
,type
).value('.', 'varchar(max)'), 1, 2, '') AS ifile
Untested, treat as pseudo-code to give the general idea.

SQL query to split a column based on hardcoded values

I have a table eg assume this setup
table MyTable has various columns Id, UserId, col1, col2 col3 including column called Stuff.
I want to output certain Columns from MyTable with a query
but i want to split the 'Stuff' column such that 2 new columns are shown in the query
I can define the categories hardcoded, im not sure how this can be represented in sql
Categoy1 = "alpha, bravo, delta, gamma';
Categoy2 = "charlie, echo, hotel';
MyTable
ID | UserID | Stuff | Other Cols....
----------------------------------------------------------
1 1 alpha
2 2 hotel
3 1 charlie
4 1 echo
5 1 gamma
6 2 bravo
7 2 delta
i want the select query to show
UserId | Category1 | Catergory2
----------------------------------------------------------
1 alpha, gamma charlie, echo
---------------------------------------------------------
2 bravo, delta hotel
----------------------------------------------------------
i.e produce 2 columns split based on whether the stuff column contains an item from category1 or category2
based on a distinct userId the categories content can be comma separated as hown above
Please can you show how this can be done
Hope this makes sense.
Thanks
You can use the xml extensions to concatenate your strings, then just hard code the categories into each subquery:
CREATE TABLE #T (ID INT, UserID INT, [Stuff] VARCHAR(300))
INSERT #T VALUES
(1, 1, 'alpha'),
(2, 2, 'hotel'),
(3, 1, 'charlie'),
(4, 1, 'echo'),
(5, 1, 'gamma'),
(6, 2, 'bravo'),
(7, 2, 'delta');
SELECT UserID,
[Category1] = STUFF(( SELECT ', ' + [Stuff]
FROM #T t2
WHERE [Stuff] IN ('alpha', 'bravo', 'delta', 'gamma')
AND t.UserID = t2.UserID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 2, ''),
[Category2] = STUFF(( SELECT ', ' + [Stuff]
FROM #T t2
WHERE [Stuff] IN ('charlie', 'echo', 'hotel')
AND t.UserID = t2.UserID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 2, '')
FROM ( SELECT DISTINCT UserID
FROM #T
) t
Example on SQL Fiddle
You could define your categories at the start in a CTE (Categories) for improved readibility:
WITH Categories AS
( SELECT Category, Name
FROM (VALUES
(1, 'alpha'),
(1, 'bravo'),
(1, 'delta'),
(1, 'gamma'),
(2, 'charlie'),
(2, 'echo'),
(2, 'hotel')
) t (Category, Name)
), Data AS
( SELECT UserID, [Stuff], Category
FROM T
INNER JOIN Categories c
ON c.Name = T.[Stuff]
)
SELECT UserID,
[Category1] = STUFF(( SELECT ', ' + [Stuff]
FROM Data t2
WHERE Category = 1
AND t.UserID = t2.UserID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 2, ''),
[Category2] = STUFF(( SELECT ', ' + [Stuff]
FROM Data t2
WHERE Category = 2
AND t.UserID = t2.UserID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 2, '')
FROM ( SELECT DISTINCT UserID
FROM T
) t
Example on SQL Fiddle
My try, the technique I learned from Stack Overflow!... Please check:
DECLARE #Categoy1 NVARCHAR(MAX) = 'alpha, bravo, delta, gamma',
#Categoy2 NVARCHAR(MAX) = 'charlie, echo, hotel'
SELECT
UserID,
STUFF((SELECT ', ' + display_term
FROM sys.dm_fts_parser('"'+ ',' + #Categoy1 + '"', 1033, NULL, 0) INNER JOIN
YourTable T on display_term=[Stuff]
WHERE T.UserID= x.UserID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 2, '') Category1,
STUFF((SELECT ', ' + display_term
FROM sys.dm_fts_parser('"'+ ',' + #Categoy2 + '"', 1033, NULL, 0) INNER JOIN
YourTable T on display_term=[Stuff]
WHERE T.UserID= x.UserID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 2, '') Category2
FROM YourTable x
GROUP BY UserID