One column into multiple columns by type, plus concatenating multiples - sql

I have a table like this:
Customer
Number
Type
1
234.567.8910
1
1
234.234.2345
2
2
234.567.5555
1
2
151.513.5464
1
3
845.846.8486
3
I am trying to include this information with information from another table (say... address), but in separate columns by type, and I want to concatenate values that are of the same type so that the return looks like this:
Customer
Cell
Home
Work
1
234.567.8910
234.234.2345
NULL
2
234.567.5555 & 151.513.5464
NULL
NULL
3
NULL
NULL
845.846.8486
When I use the STRING_AGG function, it appends the value for each line of that customer - even if it would be null when the function isn't applied, so the customer 1 has both the cell and home numbers repeated twice.
The only workaround I can find is to select each column in a subquery and join them together. Is that the only option?

Try with FOR XML and PATH.
Query
select [Customer],
stuff((
select distinct ',' + [Number]
from [your_table_name]
where [Customer] = a.[Customer]
and [Type] = 1
for xml path (''))
, 1, 1, '') as [Cell],
stuff((
select distinct ',' + [Number]
from [your_table_name]
where [Customer] = a.[Customer]
and [Type] = 2
for xml path (''))
, 1, 1, '') as [Home],
stuff((
select distinct ',' + [Number]
from [your_table_name]
where [Customer] = a.[Customer]
and [Type] = 3
for xml path (''))
, 1, 1, '') as [Work]
from [your_table_name] as a
group by [Customer];

You need conditional aggregation:
SELECT
Customer,
STRING_AGG(CASE WHEN Type = 1 THEN Number END, ' & ') Cell,
STRING_AGG(CASE WHEN Type = 2 THEN Number END, ' & ') Home,
STRING_AGG(CASE WHEN Type = 3 THEN Number END, ' & ') Work
From table

Related

Select only the top 1 value from a column in a table that I'm joining?

So below is the query, how can I store only the top 1 "agencyID" into DCA.AgencyID? I know that the way the query below is structured now it would select all the "agencyID" values and not just the top 1.
SELECT AP.ID
,DCA.AgencyID as agencyID --How to store select top 1?
,replace(LTRIM(RTRIM(AP.UserNameWebsite)), '\', '') AS username
,replace(LTRIM(RTRIM(AP.FirstName)), '\', '') + N' ' + replace(LTRIM(RTRIM(AP.LastName)), '\', '') AS fullName
,LTRIM(RTRIM(AP.EmailAddress)) AS email
,LTRIM(RTRIM(AP.Phone1)) AS phone1
,LTRIM(RTRIM(AP.Phone2)) AS phone2
,LTRIM(RTRIM(AP.GreenSolution)) AS greenSolution
,CASE
WHEN UserType = 'AM'
THEN 1
ELSE 0
END AS producer
FROM DEV01_DataExchange.[DuckCreek].[fpmAgentsProfile] AP
Inner Join [DUCKCREEK_DEV].[DEV01_DuckCreek_Consolidated].[dbo].Agency DCA
on AP.AgencyID = DCA.Reference
WHERE TransType = 'A' AND DC_LastModifiedDate IS NULL AND DCA.Reference is NOT NULL
Your question is a bit vague, but the answer is cross apply:
FROM DEV01_DataExchange.[DuckCreek].[fpmAgentsProfile] AP CROSS APPLY
(SELECT TOP (1) DCA.*
FROM [DUCKCREEK_DEV].[DEV01_DuckCreek_Consolidated].[dbo].Agency DCA
WHERE AP.AgencyID = DCA.Reference
) DCA
WHERE TransType = 'A' AND DC_LastModifiedDate IS NULL AND DCA.Reference is NOT NULL;
Normally an ORDER BY clause would be used. Perhaps ORDER BY DCA.AgencyID DESC? However, if there is a column called AgencyID, I'm surprised it is not being used for the alignment to AP.
Also, some of the WHERE conditions might belong in the subquery.
You can use a Cross Apply instead of the Join:
SELECT AP.ID
,DCA.AgencyID as agencyID
,replace(LTRIM(RTRIM(AP.UserNameWebsite)), '\', '') AS username
,replace(LTRIM(RTRIM(AP.FirstName)), '\', '') + N' ' + replace(LTRIM(RTRIM(AP.LastName)), '\', '') AS fullName
,LTRIM(RTRIM(AP.EmailAddress)) AS email
,LTRIM(RTRIM(AP.Phone1)) AS phone1
,LTRIM(RTRIM(AP.Phone2)) AS phone2
,LTRIM(RTRIM(AP.GreenSolution)) AS greenSolution
,CASE
WHEN UserType = 'AM'
THEN 1
ELSE 0
END AS producer
FROM DEV01_DataExchange.[DuckCreek].[fpmAgentsProfile] AP
Cross Apply
(
Select top 1 AG.*
From [DUCKCREEK_DEV].[DEV01_DuckCreek_Consolidated].[dbo].Agency AG
where AG.Reference = AP.AgencyID
) DCA
WHERE TransType = 'A' AND DC_LastModifiedDate IS NULL AND DCA.Reference is NOT NULL

SQL - combine multiple rows in to a single as a list

How to combine/merge multiple rows into a single rows as a list in SQL.
[original Scenario:]
[Required Scenario:]
SQL Server provides ROW_NUMBER() function to achieve the above
SELECT CASE WHEN ROW_NUMBER() OVER(PARTITION BY [column1] ORDER BY [column1]) > 1
THEN ''
ELSE CAST([column1] AS VARCHAR)
END [column1],
[column2]
FROM <table>;
EDIT : use of STUFF() function
select
[column1],
[column2] = stuff(
(select DISTINCT ' '+[column2] from <table> where [column1] = t.[column1] for xml path('')),
1,1, ''
)
from <table> t group by [column1]
Result :
column1 column2
1 Value 1 Value 2 Value 3
2 Value 4
3 Value 5 Value 6
thanks #Yogesh For your help !!
I have used the below query and it is working fine for me and displays the data as requiredrequired Scenario:
Select distinct ST2.SubjectID,
substring(
(
Select CHAR(10) +ST1.StudentName AS [text()]
From dbo.Students ST1
Where ST1.SubjectID = ST2.SubjectID
ORDER BY ST1.SubjectID
For XML PATH ('')
), 2, 1000) [Students]
From dbo.Students ST2

Concatenate several columns as comma-separated string

The following is a starting point to concatenate several columns to one string where the values are comma separated. If the column entry is empty or NULL no comma should be used:
IF OBJECT_ID(N'tempdb..#Temp') IS NOT NULL
DROP TABLE #Temp;
CREATE TABLE #Temp
(
Id INT,
Name1 NVARCHAR(10) ,
Name2 NVARCHAR(10) ,
Name3 NVARCHAR(10)
);
INSERT INTO #Temp
SELECT 1,
N'Name1' ,
NULL ,
N'Name3'
UNION
SELECT 2,
N'Name1' ,
N'Name2' ,
N'Name3'
UNION
SELECT 3,
NULL ,
NULL ,
N'Name3'
UNION
SELECT
4,
N'' ,
N'' ,
N'Name3';
SELECT Id, STUFF(COALESCE(N',' + Name1, N'') + COALESCE(N',' + Name2, N'')
+ COALESCE(N',' + Name3, N''), 1, 1, '') AS ConcateStuff
FROM #Temp;
The current results are as follows:
Id ConcateStuff
1 Name1,Name3
2 Name1,Name2,Name3
3 Name3
4 ,,Name3
Everything work fine for NULL entries but not for empty entries. The last row's result should just be:
Name3
Is there a simple way to get this to work without using complex nested case statements (ultimately I have to concatenate more than 3 columns).
By using NULLIF you can achieve it.
SELECT Id, STUFF(COALESCE(N',' + NULLIF(Name1, ''), N'') + COALESCE(N',' + NULLIF(Name2, ''), N'')
+ COALESCE(N',' + NULLIF(Name3, ''), N''), 1, 1, '') AS ConcateStuff
FROM #Temp;
Result
Id ConcateStuff
-----------------
1 Name1,Name3
2 Name1,Name2,Name3
3 Name3
4 Name3

Stored procedure redundant data and sorting challenge

I've got a stored procedure with a pivot where data fields become columns. Data that is presently returned has some redundancies and would like to see if there is a way to correct this. The stored procedure is actually feeding a .rdlc report and current output looks like this:
Period | No Of Interim | Excellent | Very Good | Good | Satisfactory | Unsatisfactory
-------------------------------------------------------------------------------------
1 1
12 1
18 1
18 1
18 1
19 1
19 1
2 1
2 1
This is what it needs to look like:
Period | No Of Interim | Excellent | Very Good | Good | Satisfactory | Unsatisfactory
-------------------------------------------------------------------------------------
1 1
2 1 1
12 1
18 1 1 1
19 1 1
Period column needs to sorted in ascending fashion and repeating instances need to be added to the same line.
The stored procedure and corresponding view responsible for output is as follows:
BEGIN
DECLARE #cols NVARCHAR(MAX)
DECLARE #query NVARCHAR(MAX)
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
SELECT #cols = STUFF((SELECT '],[' + [Description]
FROM vQualScoringGrade
GROUP BY [Description]
ORDER BY MAX([orderby]), [Description]
FOR XML PATH('')), 1,2,'') + ']'
SET #query = N'SELECT Period, ' + #cols + ' FROM
(SELECT Period, Description, Value, OrderBy FROM
vQualScoringGrade) p
PIVOT (SUM([Value]) for [Description] IN ( ' + #cols + ' )) AS pvt ORDER BY Period'
execute(#query)
end
SELECT TOP (100) PERCENT Period, Description, GradeCount AS Value, OrderBy
FROM (SELECT CASE CHARINDEX('.', Number) WHEN 0 THEN Number ELSE
REPLACE(LEFT(Number, CHARINDEX('.', Number)), '.', '')
END AS Period, 'NoOfInterim' AS Description, COUNT(Number) AS
GradeCount, 1 AS OrderBy
FROM vQualScoringExcellent AS vQualScoringExcellent
GROUP BY Number, Description
UNION
SELECT CASE CHARINDEX('.', Number) WHEN 0 THEN Number ELSE
REPLACE(LEFT(Number, CHARINDEX('.', Number)), '.', '')
END AS Period, 'Excellent' AS Description, COUNT(Description) AS
GradeCount, 2 AS OrderBy
FROM vQualScoringExcellent AS vQualScoringExcellent_1
GROUP BY Number, Description
UNION
SELECT CASE CHARINDEX('.', Number) WHEN 0 THEN Number ELSE
REPLACE(LEFT(Number, CHARINDEX('.', Number)), '.', '')
END AS Period, 'VeryGood' AS Description, COUNT(Description) AS
GradeCount, 3 AS OrderBy
FROM vQualScoringVeryGood AS vQualScoringVeryGood
GROUP BY Number, Description
UNION
SELECT CASE CHARINDEX('.', Number) WHEN 0 THEN Number ELSE
REPLACE(LEFT(Number, CHARINDEX('.', Number)), '.', '')
END AS Period, 'Good' AS Description, COUNT(Description) AS
GradeCount, 4 AS OrderBy
FROM vQualScoringGood AS vQualScoringGood
GROUP BY Number, Description
UNION
SELECT CASE CHARINDEX('.', Number) WHEN 0 THEN Number ELSE
REPLACE(LEFT(Number, CHARINDEX('.', Number)), '.', '')
END AS Period, 'Satisfactory' AS Description, COUNT(Description) AS
GradeCount, 5 AS OrderBy
FROM vQualScoringSatisfactory AS vQualScoringSatisfactory
GROUP BY Number, Description
UNION
SELECT CASE CHARINDEX('.', Number) WHEN 0 THEN Number ELSE
REPLACE(LEFT(Number, CHARINDEX('.', Number)), '.', '')
END AS Period, 'Unsatisfactory' AS Description, COUNT(Description) AS
GradeCount, 6 AS OrderBy
FROM vQualScoringUnsatisfactory AS vQualScoringUnsatisfactory
GROUP BY Number, Description) AS QualScoringGrade
ORDER BY OrderBy
I am guessing that the problem is due to the OrderBy column in your PIVOT query. This line is the cause of your problem:
SELECT Period, Description, Value, OrderBy
I would alter the query to be:
SELECT #cols = STUFF((SELECT '],[' + [Description]
FROM vQualScoringGrade
GROUP BY [Description]
ORDER BY MAX([orderby]), [Description]
FOR XML PATH('')), 1,2,'') + ']'
SET #query = N'SELECT Period, ' + #cols + '
FROM
(
SELECT Period, Description, Value
FROM vQualScoringGrade
) p
PIVOT
(
SUM([Value])
for [Description] IN ( ' + #cols + ' )
) AS pvt
ORDER BY Period'
execute(#query);
The OrderBy column is not being used in the outer select or the PIVOT but you include it in the subquery. This column is being used in the grouping that takes place during a PIVOT. If the values are distinct, then you will get multiple rows.
You can easily test this by including the OrderBy on the final select list.

sql server(find the count of table base on condition)

i have a table like following
RequestNo Facility status
1 BDC1 Active
1 BDC2 Active
1 BDC3 Active
2 BDC1 Active
2 BDC2 Active
i want like this
RequestNo Facilty Count
1 BDC (1,2,3) 1
2 BDC(1,2) 1
the count should display based on Status with facilty.Fcilityv should take as BDC only
Try this, (assuming that your facility is fixed 4 character code)
SELECT RequestNo, Fname + '(' + FnoList + ')' Facilty, count(*) cnt
FROM
(
SELECT distinct RequestNo,
SUBSTRING(Facility,1,3) Fname,
stuff((
select ',' + SUBSTRING(Facility,4,4)
from Dummy
where RequestNo = A.RequestNo AND
SUBSTRING(Facility,1,3) = SUBSTRING(A.Facility,1,3)
for xml path('')
) ,
1, 1, '') as FnoList
FROM Dummy A
) x
group by RequestNo, Fname, FnoList;
SQL DEMO
This doesn't put any constraints on the length of the Facility field. It strips out the chars from the beginning and the numeric numbers from the ending:
SELECT RequestNo, FacNameNumbers, COUNT(Status) as StatusCount
FROM
(
SELECT DISTINCT
t1.RequestNo,
t1.Status,
substring(facility, 1, patindex('%[^a-zA-Z ]%',facility) - 1) +
'(' +
STUFF((
SELECT DISTINCT ', ' + t2.fac_number
FROM (
select distinct
requestno,
substring(facility, 2 + len(facility) - patindex('%[^0-9 ]%',reverse(facility)), 9999) as fac_number
from facility
) t2
WHERE t2.RequestNo = t1.RequestNo
FOR XML PATH (''))
,1,2,'') + ')' AS FacNameNumbers
FROM Facility t1
) final
GROUP BY RequestNo, FacNameNumbers
And the SQL Fiddle